What is the motivation for bringing Symbols to ES6?
UPDATE: Recently a brilliant article from Mozilla came up. Read it if you're curious.
您可能知道他们计划在ECMAScript 6中包含新的Symbol原语类型(更不用说其他一些疯狂的东西)。 我一直认为Ruby中的
我不明白动机。 有人可以向我解释我们是否真的需要JavaScript中的符号吗?
将符号引入Javascript的最初动机是启用私有属性。
不幸的是,他们最终被严重降级。它们不再是私有的,因为您可以通过反射找到它们,例如,使用
它们现在被称为唯一符号,它们唯一的用途是避免属性之间的名称冲突。例如,ECMAScript本身现在可以通过某些方法引入扩展钩子,这些方法可以放在对象上(例如定义它们的迭代协议),而不会有使它们与用户名冲突的风险。
这是否足够强大,为语言添加符号的动机是值得商榷的。
符号不保证真正的隐私,但可用于分隔对象的公共和内部属性。让我们举一个例子,我们可以使用
让我们举一个例子,其中对象的属性不是私有的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var Pet = (function() { function Pet(type) { this.type = type; } Pet.prototype.getType = function() { return this.type; } return Pet; }()); var a = new Pet('dog'); console.log(a.getType());//Output: dog a.type = null; //Modified outside console.log(a.getType());//Output: null |
上面,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var Pet = (function() { function Pet(type) { this.getType = function(){ return type; }; } return Pet; }()); var b = new Pet('dog'); console.log(b.getType());//dog b.type = null; //Stays private console.log(b.getType());//dog |
上述方法的缺点:我们为每个创建的
现在我们介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var Pet = (function() { var typeSymbol = Symbol('type'); function Pet(type) { this[typeSymbol] = type; } Pet.prototype.getType = function(){ return this[typeSymbol]; } return Pet; }()); var a = new Pet('dog'); console.log(a.getType());//Output: dog a.type = null; //Stays private console.log(a.getType());//Output: dog |
是一种新的,特殊的对象,可以在对象中用作唯一的属性名称。使用
1 | let symbol = Symbol(); |
实际上,
以下是
这是
假设您有一个日志库,其中包含多个日志级别,例如
1 2 3 4 5 6 7 | log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn'), }; log(log.levels.DEBUG, 'debug message'); log(log.levels.INFO, 'info message'); |
阅读这篇精彩的文章。
这篇文章是关于
TLDR;
关于符号有两个奇怪的事实。
-
第一个数据类型,只有JavaScript中没有文字的数据类型
-
任何用
Symbol() 定义的变量都会获得唯一的内容,但它并不是真正的私有。 -
任何数据都有自己的符号,对于相同的数据,符号将是相同的。以下段落中的更多信息,否则它不是TLRD; :)
如何初始化符号?
1.获取具有可调试值的唯一标识符
你可以这样做:
1 | var mySymbol1 = Symbol(); |
或者这样:
1 | var mySymbol2 = Symbol("some text here"); |
无法从符号中提取
1 2 | console.log(mySymbol2); // Symbol(some text here) |
2.获取某些字符串数据的符号
在这种情况下,实际上考虑了符号的值,这样两个符号可以是非唯一的。
1 2 3 | var a1 = Symbol.for("test"); var a2 = Symbol.for("test"); console.log(a1 == a2); //true! |
我们将这些符号称为"第二类"符号。它们不以任何方式与"第一类型"符号(即用
接下来的两段仅涉及第一类符号。
如何使用Symbol代替旧数据类型?
我们首先考虑一个对象,一个标准数据类型。我们可以在那里定义一些键值对,并通过指定键来访问值。
1 2 3 | var persons = {"peter":"pan","jon":"doe"}; console.log(persons.peter); // pan |
如果我们有两个名叫彼得的人??怎么办?
这样做:
1 | var persons = {"peter":"first","peter":"pan"}; |
没有多大意义。
因此,似乎是两个完全不同的人具有相同名称的问题。让我们再引用新的
1 2 | var a = Symbol("peter"); var b = Symbol("peter"); |
现在我们有两个同名的人。我们的人确实不同吗?他们是;你可以检查一下:
1 2 | console.log(a == b); // false |
我们如何在那里受益?
我们可以在您的对象中为不同的人创建两个条目,并且它们不会以任何方式被误解。
1 2 3 | var firstPerson = Symbol("peter"); var secondPerson = Symbol("peter"); var persons = {[firstPerson]:"first", [secondPerson]:"pan"}; |
Note:
It's worth to notice, though, that stringifying the object withJSON.stringify will drop all the pairs initialised with a Symbol as a key.
ExecutingObject.keys won't either return suchSymbol()->value pairs.
使用此初始化,绝对不可能将条目误认为是第一人和第二人。为他们调用
1 2 3 4 | console.log(persons[a]); // first console.log(persons[b]); // pan |
在对象中使用时,与定义非可枚举属性相比,它有何不同?
实际上,已经存在一种方法来定义要从
1 2 3 4 5 6 7 | var anObject = {}; var fruit ="apple"; Object.defineProperty( anObject, fruit, { enumerable: false, value:"green" }); |
1 2 3 | console.log(anObject[fruit]); //green console.log(anObject["apple"]); //green console.log(anObject.apple); //green |
如果使用符号定义,如上一段所述:
1 | fruit = Symbol("apple"); |
只有知道其变量,您才有能力获得其价值,即
1 2 3 | console.log(anObject[fruit]); //green console.log(anObject["apple"]); //undefined console.log(anObject.apple); //undefined |
此外,在键
键入转换和检查
-
与其他数据类型不同,不可能将
Symbol() 转换为任何其他数据类型。 -
通过调用
Symbol(data) ,可以基于原始数据类型"制作"符号。 -
在检查类型方面,没有任何变化。
1
2
3
4
5
6
7
8
9function isSymbol ( variable ) {
return typeof someSymbol ==="symbol";
}
var a_Symbol = Symbol("hey!");
var totally_Not_A_Symbol ="hey";
console.log(isSymbol(a_Symbol)); //true
console.log(isSymbol(totally_Not_A_Symbol)); //false
我就是这样看的。符号通过阻止对象的键/属性通过一些流行的方法(如Object.keys()和JSON.stringify())来提供"额外的隐私级别"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var age = Symbol(); // declared in another module perhaps? class Person { constructor(n,a){ this.name = n; this[age] = a; } introduce(){ console.log(`My name is ${this.name}. I am ${this[age]-10}.`); } } var j = new Person('Jane',45); j.introduce(); // My name is Jane. I am 35. console.log(JSON.stringify(j)); // {"name":"Jane"} console.log(Object.keys(j)); // ["name"] console.log(j[age]); // 45 (well…only if you know the age in the first place…) |
尽管给定了一个对象本身,但仍然可以通过反射,代理,Object.getOwnPropertySymbols()等公开这些属性,没有通过一些直接方法访问它们的自然方法,从OOP的角度来看,这有时可能就足够了。