更新:最近Mozilla发布了一篇精彩的文章。如果你好奇,就读读吧。

正如你可能知道的,他们计划在ECMAScript 6中包括新的Symbol原始类型(更不用说其他一些疯狂的东西)。我一直认为:符号概念在Ruby中是不必要的;我们可以很容易地使用纯字符串,就像在JavaScript中做的那样。现在他们决定让JS中的事情变得复杂。

我不明白他们的动机。有人能解释一下JavaScript中是否真的需要符号吗?


当前回答

符号是一种新的特殊类型的对象,可以用作对象中的唯一属性名。使用符号而不是字符串允许不同的模块创建互不冲突的属性。符号也可以有效地设置为私有,这样它们的属性就不能被尚未直接访问符号的任何人访问。

符号是一种新的原语,就像数字、字符串和布尔原语一样。与其他原语不同的是,符号没有文字语法(例如,string没有“)-创建它们的唯一方法是使用Symbol构造函数,以以下方式:

let symbol = Symbol();

实际上,符号只是将属性附加到对象上的一种略有不同的方式——您可以轻松地将众所周知的符号作为标准方法提供,就像object .prototype一样。hasOwnProperty,它出现在从Object继承的所有东西中。

下面是Symbol原语类型的一些好处。

符号具有内建的可调试性

符号可以给出一个描述,这实际上只是用于调试,以便在将它们记录到控制台时更容易一些。

符号可以用作对象键

这就是符号真正有趣的地方。它们与物体紧密地交织在一起。符号可以被分配为对象的键,这意味着您可以为一个对象分配无限数量的唯一符号,并保证这些符号永远不会与字符串键或其他唯一符号冲突。

符号可以用作唯一值

让我们假设您有一个日志库,其中包括多个日志级别,如logger.levels。调试logger.levels。INFO, logger。levels。warn等等。在ES5代码中,你需要创建这些字符串(logger.levels. debug === 'debug')或数字(logger.levels. debug == 'debug')。Debug === 10)。这两种方法都不理想,因为这些值不是唯一的值,但符号是唯一的值!所以日志记录器。Levels简单地变成:

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

阅读这篇很棒的文章。

其他回答

Symbols have two main use cases: “Hidden” object properties. If we want to add a property into an object that “belongs” to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in for..in, so it won’t be accidentally processed together with other properties. Also it won’t be accessed directly, because another script does not have our symbol. So the property will be protected from accidental use or overwrite. So we can “covertly” hide something into objects that we need, but others should not see, using symbolic properties. There are many system symbols used by JavaScript which are accessible as Symbol.*. We can use them to alter some built-in behaviors. For instance, ...... Symbol.iterator for iterables, Symbol.toPrimitive to setup object-to-primitive conversion and so on.

将符号引入Javascript的最初动机是启用私有属性。

不幸的是,他们最终被严重降级。它们不再是私有的,因为你可以通过反射找到它们,例如,使用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类属性类型不是私有的。为了使它私有,我们必须创建一个闭包。下面的示例说明了如何使用闭包将类型设置为私有。

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

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

现在我们介绍符号。这可以帮助我们在不使用额外不必要的闭包的情况下使属性为私有。代码示例如下:

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

JS符号是一种新的原始数据类型。它们是作为唯一id的令牌。可以使用symbol构造函数创建符号。以MDN上的这个片段为例:

//符号构造函数有一个可选参数, //只用于调试的描述。 //这里有两个具有相同描述的符号 let Sym1 = Symbol("Sym"); let Sym2 = Symbol("Sym"); console.log(Sym1 == Sym2);//返回"false" //保证符号是唯一的。 //即使我们创建许多具有相同描述的符号, //它们是不同的值。

使用符号作为唯一的对象属性键通常很方便,例如:

让obj = {}; let prop = Symbol(); Obj [prop] = 123;//符号prop被赋值为123 obj。Prop = 456;// string prop被赋值为456 console.log (obj。道具,obj[支持]);//日志456,123

以下是我的看法。通过防止对象的键/属性通过一些流行的方法(如object .keys()和JSON.stringify())暴露,符号提供了“额外的隐私级别”。

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的角度来看,这些方法有时已经足够了。