Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript ES6 Destructuring - Identifier 'location' has already been declared

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?

like image 675
StandardNerd Avatar asked Jan 12 '17 13:01

StandardNerd


2 Answers

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);
like image 116
Juan Mendes Avatar answered Nov 15 '22 00:11

Juan Mendes


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.

like image 32
Zorgatone Avatar answered Nov 14 '22 22:11

Zorgatone