7
votes

I have a simple array of objects and want to get the location attribute of the first element:

const companies = [
  { name: 'Google', location: 'Mountain View' },
  { name: 'Facebook', location: 'Menlo Park' },
  { name: 'Uber', location: 'San Francisco' }
];

const [{ location }] = companies; 
// expected: Mountain View but got Uncaught SyntaxError: Identifier 
//'location' has already been declared at <anonymous>:1:1

What am I missing?

4

4 Answers

12
votes

You cannot redefine the global variable location as a constant. Had you used let [{location}] = companies, you wouldn't have got an error, but you would have had the wrong behavior as it would try to assign a value to window.location and redirect the page.

Possible solutions:

  • Change the variable name (along with the property name)
  • Wrap your code in a block (IIFE is not necessary in this case since we are using ES6)
  • You can keep the property name and change the variable name, see the last example

const companies = [
  {name: 'Google',loc: 'Mountain View'},
  {name: 'Facebook', loc: 'Menlo Park'},
  {name: 'Uber', loc: 'San Francisco'}
];

const [{loc}] = companies;
console.log(loc);

// Block scope
{
  const companies = [
    {name: 'Google', location: 'Mountain View'},
    {name: 'Facebook', location: 'Menlo Park'},
    {name: 'Uber', location: 'San Francisco'}
  ];
  const [{location}] = companies;
  console.log(location);
}

// Variable name doesn't have to be the same as property
const [{ location: loca }] = [{location: 'Mountain View'}]
console.log(loca);
5
votes

The problem here is that you're not wrapping your code inside a function.

If you use an IIFE (Immediately Invocated Function Expression) the same code will work.

The reason your code fails is because you can't declare a constant in the same scope, if the constant/variable name is already taken.

You could have used var instead of const but that would have assigned to window.location, effectively reloading the page and taking you to a 404 Page because that url doesn't exist.

That's why you should always write your code in an IIFE, to prevent global namespace pollution and nasty bugs that are hard to fix.

Here's the same code with an IIFE (added strict mode):

(() => { // ES2015 IIFE
    "use strict";
    
    const companies = [
      { name: 'Google', location: 'Mountain View' },
      { name: 'Facebook', location: 'Menlo Park' },
      { name: 'Uber', location: 'San Francisco' }
    ];
    
    const [{ location }] = companies;
    console.log(location);
})();

Or better yet:

{ // ES2015 block
    "use strict";
    
    const companies = [
      { name: 'Google', location: 'Mountain View' },
      { name: 'Facebook', location: 'Menlo Park' },
      { name: 'Uber', location: 'San Francisco' }
    ];
    
    const [{ location }] = companies; 
    console.log(location);
};

The use of the ES2015 block won't pollute the global scope, given that you'll be using only let and const (and not using var)

Also, always remember that not all the browsers are supporting ES2015 syntax yet, so for now the code must be transpiled using Babel JS or similar tools.

0
votes

This happens because location has already been declared on global scope, as window.location.

To avoid this, this piece of code should not be executed in local scope, i.e. a block. This is ES6, while IIFEs are left-overs of ES5:

{
  ...
  const [{ location }] = companies; 
}

The typical use for ES6 is modular environment. Considering that current scope is CommonJS or ES6 module, the problem will never appear.

-3
votes
const companies = [
  { name: 'Google', location: 'Mountain View' },
  { name: 'Facebook', location: 'Menlo Park' },
  { name: 'Uber', location: 'San Francisco' }
];

console.log(companies[0].location);