为什么要将符号带入JavaScript?

正如你可能知道他们正计划在ECMAScript 6中包含新的Symbol原始types(更不用说其他一些疯狂的东西了)。 我一直认为Ruby中的:symbol概念是不必要的, 我们可以轻松地使用纯string,就像我们在JavaScript中所做的一样。 现在他们决定用JS来使JS变得复杂。

我不明白这个动机。 有人可以向我解释我们是否真的需要JavaScript中的符号?

更新 :最近来自Mozilla的一篇精彩文章出现了。 如果你好奇,请阅读。

启用私有属性,如kangax在他的回答中所描述的,确实是将符号引入JavaScript的原始动机。

然而不幸的是,他们最终被严重降级,毕竟不是私有的,因为你可以通过反思find它们。 具体来说,通过Object.getOwnPropertySymbols方法和代理。

他们现在被称为独特的符号,他们唯一的用途是避免名称之间的冲突属性。 例如,EcmaScript本身现在可以通过一些可以放在对象上的方法来引入扩展钩子(例如定义它们的迭代协议),而不会冒用户名冲突的风险。

这是否足够强大的动机,以添加符号的语言是值得商榷的。

符号不保证真实的隐私,但可以用来分隔对象的公共和内部属性。 举个例子,我们可以使用Symbol来获得私有属性。

我们举一个例子,一个对象的属性不是私有的。

 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 

以上, Pet类属性type不是私有的。 为了保密,我们必须创build一个闭包。 下面的例子说明了我们如何使用闭包来实现私有type

 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 

上述方法的缺点:我们为每个创build的Pet实例引入一个额外的闭包,这会损害性能。

现在我们介绍一下Symbol 。 这可以帮助我们在不使用额外的不必要的closures的情况下将财产私有化 下面的代码示例:

 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 

这是我如何看待它。 通过Object.keys()和JSON.stringify()等一些stream行的方法,通过阻止对象的键/属性被暴露,符号提供了“额外的隐私级别”。

 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…) 

虽然给定一个对象本身,这些属性仍然可以通过reflection,代理,Object.getOwnPropertySymbols()等暴露出来,但没有自然的方法通过几个直接的方法来访问它们,有时从OOP的angular度来看是足够的。

Symbols是一种new specialtypes的对象,可用作对象中的unique属性名称。 使用symbols而不是strings允许不同的modules创build不相互冲突的属性。 Symbols也可以是private ,以便任何没有直接访问symbol人都无法访问它们的属性。

Symbols是一个new 原始 。 就像NumberStringBoolean原语一样, Symbols也有一个Symbol函数可以用来创build它们。 与其他基元不同, Symbols不具有字面语法(例如,string是如何具有的) – 创build它们的唯一方法是使用Symbol构造函数,方法如下:

 let symbol=Symbol(); 

实际上, Symbols只是将属性附加到对象上的一种slightly不同的方式 – 您可以很容易地提供well-known 符号作为标准方法,就像Object.prototype.hasOwnProperty ,它出现在inheritance自Object所有东西中。

这是符号原始types的一些好处。

符号具有内置的可debugging性

Symbols可以给出一个description ,这实际上只是用于debugging ,使他们logging到控制台时, life变得更加容易。

符号可以用作对象键

这是Symbols变得真正有趣的地方。 它们与Objects heavily交织在一起。 Symbols可以指定为Objects keys (类似于string键),这意味着您可以将无限数量的唯一符号分配给对象,并保证这些符号永远不会与string键或其他唯一Symbols冲突。

符号可以作为一个unique价值。

假设您有一个logging库,其中includes多个日志levels例如logger.levels.DEBUGlogger.levels.INFOlogger.levels.WARN等等。 在ES5代码中,你想使这些Strings (如logger.levels.DEBUG === 'debug' )或numberslogger.levels.DEBUG === 10 )。 这两个都不理想,因为这些值不是唯一的值,但Symbols是! 所以logger.levels simply变成:

 log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn'), }; log(log.levels.DEBUG, 'debug message'); log(log.levels.INFO, 'info message'); 

阅读更多在这个伟大的文章。