What Is the Motivation for Bringing Symbols to Es6

What is the motivation for bringing Symbols to ES6?

The original motivation for introducing symbols to Javascript was to enable private properties.

Unfortunately, they ended up being severely downgraded. They are no longer private, since you can find them via reflection, for example, using Object.getOwnPropertySymbols or proxies.

They are now known as unique symbols and their only intended use is to avoid name clashes between properties. For example, ECMAScript itself can now introduce extension hooks via certain methods that you can put on objects (e.g. to define their iteration protocol) without risking them to clash with user names.

Whether that is strong enough a motivation to add symbols to the language is debatable.

What is the use of Symbol in javascript ECMAScript 6?

Symbol is used to create a totally unique, one-of-a-kind identifier. It's use is precisely for the example you list.

Even if you call Symbol with the same string, the instances will be different. This allows different libraries (which may be used at the same time) to define keys which may be used at the same time.

For example, imagine two libraries using a common name to define something on window or global (or for illustration, the fake global door):

const door = {};// from library 1door.cake = () => console.log('chocolate');// from library 2door.cake = () => console.log('vanilla');
// your codedoor.cake();

Why does JavaScript use Symbols instead of just strings?

Symbols are explained in some detail at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

In particular:

Note that Symbol("foo") does not coerce the string "foo" into a symbol. It creates a new symbol each time:

Symbol('foo') === Symbol('foo'); // false

Let's say I have some data that I want to store in some widely-visible object. I can come up with a string, say "foo", and use that as the key to store my data in the object. Later on, I can use that same string (either hard-coded or stored somewhere) to get my data back out of the object.

There are two things to keep in mind if we do this: any other code can look up our data, and any other code can overwrite our data either intentionally or by accident (e.g. they just so happened to pick the same string as us).

If we use a symbol as a key instead, then there is no way for other code to look up or modify the data, unless we explicitly give them the symbol (e.g. as an argument). This way, clashes can't occur, since lots of different code can all use the same string (like "foo" in the MDN example) but they'll always end up with different symbols, so there's no conflict.

One reason we might want to do this is if we have a single "state" object which various pieces of code will modify. If we want to keep each piece of code modular then it's important that they don't end up depending on each other indirectly via the state: by using symbols we make sure that not only can a piece of code not alter someone else's part of the state, but they also can't depend on someone else's state (i.e. choosing what to do based on reading someone's state).

What is the point of the 'Symbol' type in ECMA-262-v6?

Symbols are private keys that replace magic names. They prevent using a simple string to reference the field, so only consumers with the symbol can gain access.

Some symbols are used to indicate particular behaviors to the runtime (like Symbol.iterator, which acts much like a pre-shared secret), while others can be allocated by the library and used to effectively hide fields.

In general, symbols are intended as a replacement for magical names. Rather than having a properties simply called 'foo', you can allocate a symbol const foo = Symbol() and pass that selectively. This allows the runtime to allocate Symbol.iterator when it starts up and guarantees that anyone trying to implement an iterable does so in a consistent fashion.

The runtime could use symbols to optimize access to certain fields, if it felt the need to, but doesn't have to.

You can use symbols to direct consumers to a particular method, depending on their usage. For example, if you had a library that could return a synchronous iterable or a generator, depending on the client's async support, you could:

const syncIterable = Symbol();
const asyncIterable = Symbol();

class Foo {
static getIterable(async = false) {
return async ? asyncIterable : syncIterable;
}

[syncIterable]() {
return new SyncFoo();
}

[asyncIterable]() {
return new AsyncFoo();
}
}

let foo = new Foo();
for (let x of foo[Foo.getIterable(true)]()) {
// could be a iterator, could be a generator
}

That's a rather contrived example, but shows how a library can use symbols to selectively provide access to users.

ES6 Symbols in Chrome

Since symbols are values, not objects, they are created by a plain function call to Symbol, not by invoking new on it. In the version of Chrome you are using, new Symbol gives you a wrapper object for such a value (i.e., you can get the actual symbol by invoking .valueOf on the result).

Since this is a common pitfall, the ES6 draft spec recently changed to disallow constructor invocation of Symbol altogether. That is, you already get an exception on the first line of your code. This change has already been implemented in V8, but hasn't made it into Chrome yet.

What are the possible usage scenarios for the new JavaScript Symbol datatype?

After reading the documentation and playing a little bit with this Symbol type in chrome, it appears that a Symbol is a way to define a name -- not a value --, and the fact that properties defined using symbols are not visible using for..in, Object.getOwnPropertyNames() or JSON.stringify() makes symbols useful for metadata properties:

// define metadata symbols
var Metadata = {
Date: Symbol('Message date')
};

var email = function(recipient, message) {
this.Recipient = recipient;
this.Message = message;
this[Metadata.Date] = new Date();
};

var email1 = new email('@Me', 'test');
JSON.stringify(email1);
// {
// Recipient: '@Me',
// Message: 'test'
// }

// Date is still accessible using
email1[Metadata.Date];
// Thu Nov 27 2014 16:50:00 GMT+0000

// Debugging in Console:
// {
// Recipient: '@Me',
// Message: 'test'
// Symbol(Message date): Thu Nov 27 2014 16:50:00 GMT+0000
// }

Symbols can be made global using the Symbol.for function, so metadata names can be created once and used across all project files.

Accessing the value using a symbol requires having a reference to the symbol when created. Each call to Symbol() creates a new one even if the same description is used:

var a = Symbol('a');
var b = Symbol('a');
a != b
// and
a != Symbol('a')

but, creating a symbol using Symbol.for, it will be registered in a global registry and the description becomes a key, meanong only one symbol with the same key will exist in the global registry:

var a = Symbol.for('a');
var b = Symbol.for('a');
a == b
// and
a == Symbol.for('a')

Symbols: Well Known Symbols to Change Built-In Behavior

You didn't quite elaborate enough in your question, but from a bit of inference and the comments, it seems like you are be misunderstanding how well-known symbols are used to interact with existing types. That is causing you to misunderstand how they improve over possible ES5 global overwriting solutions.

It's important to understand that the value of String.match is a symbol, not a function for matching. It is almost as if someone had done

Symbol.match = Symbol("match");

at the top of your program to create a new Symbol, and set it as a global property so anyone can access it from anywhere.

This is in contrast with the value of String.prototype.match which is the actual function that is used when developers call "foo".match(...).

You seem to be envisioning String.prototype.match like

String.prototype.match = function(obj) {
return Symbol.match(obj);
};

which is not the case. Looking at a simplified example of the actual implementation may help you understand:

String.prototype.match = function(obj) {
// Any object that has a `Symbol.match` property
// will just call that property. This includes every
// existing RegExp object.
if (obj != null && obj[Symbol.match] !== undefined) {
return obj[Symbol.match](this);
}

// Otherwise create a new regex to match against
// match match using that. This is to handle strings, e.g
// "foo".match("f")
const reg = new RegExp(obj);
return reg[Symbol.match](this);
};

and remember, obj[Symbol.match](this); is not calling Symbol.match(), it is reading a property from obj with the name Symbol.match, and then calling the resulting function.

Why use a Symbol to override these functions?

Hopefully that example makes the reasoning behind this clearer.

var regexp = new RegExp("little");
var result = "a little pattern".match(regexp);

is essentially identical to doing

var regexp = new RegExp("little");
var result = regexp[Symbol.match]("a little pattern");

So why does this matter? Because now when you're designing an API to process text, you aren't limited to doing so with regular expressions. I could make my own whole library as

class MyOwnMatcher {
constructor(word) {
this.word = word;
}

[Symbol.match](haystack) {
return haystack.indexOf(this.word);
}
}

var index = "a little pattern".match(new MyOwnMatcher("little"));
// index === 2

and most importantly, I'm able to do this without having to change any globals. In JS code generally it's considered bad practice to modify globals unless you're polyfilling an officially specced and adopted API. You could implement the above like

var original = String.prototype.match;
String.prototype.match = function(arg) {
if (arg instanceof MyOwnMatcher) return this.indexOf(arg);

return original.apply(this, arguments);
};

but it's extremely ugly, easy to get wrong, and modifies a global object that isn't one that you've defined in your own code.

You can essentially think of well-known symbols as a way to implement an interface defined by a separate piece of code.



Related Topics



Leave a reply



Submit