1

I am trying to understand Symbols in ES6 better, and I have read this answer:

https://stackoverflow.com/a/22280202/5591717

They are now known as unique symbols and their only intended use is to avoid name clashes between properties.

Does a name clash only ever mean, one name shadowing another name? Or does it also mean an error, where something can't be be redeclared?

An example:

let color = Symbol('this is a color')

let apple = {
  [color]: 'red'
}

console.log(apple[color]) //red
console.log(apple.color) //undefined 

apple[color] = 'black'

console.log(apple[color]) //black
console.log(apple.color) //undefined

apple.color = 'white'

console.log(apple[color]) //black
console.log(apple.color) //white

It looks like properties can be shadowed, even if they are accessed through Symbols.

And they also allow dot notation properties with the same name as the Symbol name to coexist with a different value. Is this what is meant by avoiding name clashes?

  • 2
    The fact that you get `black` and `white` in the last lines indicates the values are *not* being shadowed. – Mark Jun 19 '19 at 00:49
  • @MarkMeyer Yes, they are, in the example black shadows red. –  Jun 19 '19 at 00:53
  • 1
    No, you replaced red with black when you did `apple[color] = 'black'` – Herohtar Jun 19 '19 at 00:54
  • @tadadadadi just think of Symbols as a unique key. It doesn't matter that you save it in a variable named `color` it's a different key than the string `color`. When you add both, you end up with an object that looks like: `{ color: 'black', [Symbol(this is a color)]: 'red' }` – Mark Jun 19 '19 at 00:56
  • @MarkMeyer This is not correct, I end up with `Object { color: "white", Symbol(this is a color): "black" }`. –  Jun 19 '19 at 01:07
  • Sorry I wasn't clear @tadadadadi. It was just an example — the point is the string "color" and the Symbol are distinct, independent keys. – Mark Jun 19 '19 at 01:12

1 Answers1

3

This is not shadowing:

In computer programming, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope. This outer variable is said to be shadowed...

eg

const foo = 'foo';
(() => {
  // entirely separate variable which shadows outer variable with same name
  const foo = 'foo2';
})();

The variable name you store the Symbol in has no effect on anything (which is good - code execution shouldn't depend on what variable names are used). You could have named it mySymbol instead of color, and the engine would have the exact same output:

let mySymbol = Symbol('this is a mySymbol')

let apple = {
  [mySymbol]: 'red'
}

console.log(apple[mySymbol]) //red
console.log(apple.color) //undefined 

apple[mySymbol] = 'black'

console.log(apple[mySymbol]) //black
console.log(apple.color) //undefined

apple.color = 'white'

console.log(apple[mySymbol]) //black
console.log(apple.color) //white

Name clashes are avoided when entirely separate sections of the code need to store data on an object. The different sections of the code may not be aware of each other, but they want to make sure that the property that one uses is not the same as another. This can be accomplished by each section using its own Symbol, making it impossible for the two sections of code to accidentally use the same property name and thus cause bugs. For example:

// module1.js

const module1 = (() => {
  const module1Sym = Symbol();
  return (obj) => {
    // put some data on obj which can be retrieved by this module later
    obj[module1Sym] = 'module 1 data';
  };
})();

// module2.js

const module2 = (() => {
  const module2Sym = Symbol();
  return (obj) => {
    // put some data on obj which can be retrieved by this module later
    obj[module2Sym] = 'module 2 data';
  };
})();

const obj = {};
module1(obj);
module2(obj);

If one module used the property name module1data instead, there could be problems - what if some other module which called itself module1 was trying to store data on the object? Then, things would break down:

// module1Foo.js

const module1Foo = (() => {
  return (obj) => {
    // put some data on obj which can be retrieved by this module later
    obj.module1Data = 'foo data';
  };
})();

// module1Bar.js

const module1Bar = (() => {
  return (obj) => {
    // put some data on obj which can be retrieved by this module later
    obj.module1Data = 'bar data';
  };
})();

const obj = {};
module1Foo(obj);
module1Bar(obj);

// Uh oh, foo data got overwritten
console.log(obj);

The above is an example of a name clash. Two modules accidentally used the same property name.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320