将符号引入ES6的动机是什么?

时间:2014-02-12 09:53:30

标签: javascript symbols ecmascript-harmony ecmascript-6

  

更新:最近出现了brilliant article from Mozilla。如果你好奇,请阅读它。

您可能知道它们是ECMAScript 6中的planning to include新符号原语类型(更不用说其他一些疯狂的东西)。我一直认为Ruby中的:symbol概念是不必要的;我们可以轻松地使用纯字符串,就像我们在JavaScript中一样。现在他们决定用JS复杂化JS。

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

7 个答案:

答案 0 :(得分:192)

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

不幸的是,他们最终被严重降级。它们不再是私有的,因为您可以通过反射找到它们,例如,使用Object.getOwnPropertySymbols或代理。

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

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

答案 1 :(得分:81)

符号不保证真正的隐私,但可用于分隔对象的公共和内部属性。我们举个例子,我们可以使用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不是私有的。为了使它成为私有,我们必须创建一个闭包。下面的示例说明了如何使用闭包使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

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

现在我们介绍Symbol。这可以帮助我们将属性设为私有,而无需使用额外的不必要的闭包。代码示例如下:

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

答案 2 :(得分:33)

StringFormat  是一种新的,特殊的对象,可以在对象中用作唯一的属性名称。使用Symbols而不是Symbol允许不同的模块创建不会相互冲突的属性。 string也可以设为私有,因此任何尚未直接访问Symbols的人都无法访问其属性。

Symbol是一个新的原语。与Symbolsnumberstring原语一样,boolean具有可用于创建它们的函数。与其他原语不同,Symbol没有文字语法(例如Symbols如何string) - 创建它们的唯一方法是使用下面的''构造函数方式:

Symbol

实际上,let symbol = Symbol(); 只是将属性附加到对象的方式略有不同 - 您可以轻松地将众所周知的Symbol作为标准方法提供,就像Symbols一样它出现在从Object.prototype.hasOwnProperty继承的所有内容中。

以下是Object基元类型的一些好处。

Symbol内置了可调试性

Symbols可以给出一个描述,它实际上只是用于调试,以便在将它们登录到控制台时使生活更轻松。

Symbols可用作Symbols

这是Object非常有趣的地方。它们与物体交织在一起。 Symbol可以被指定为对象的键,这意味着您可以为对象分配无限数量的唯一Symbol,并保证它们永远不会与Symbol键或其他键冲突唯一的string

Symbols可用作唯一值。

假设您有一个日志记录库,其中包含多个日志级别,例如Symbolslogger.levels.DEBUGlogger.levels.INFO等。在ES5代码中,您需要制作这些logger.levels.WARN s(所以string)或logger.levels.DEBUG === 'debug' s(number)。这两个都不理想,因为这些值不是唯一值,但logger.levels.DEBUG === 10是!所以Symbol只会变成:

logger.levels

详细了解great article.

答案 3 :(得分:28)

这篇文章是关于Symbol(),提供了我可以找到/制作的实际例子和事实&我能找到的定义。

TLDR;

Symbol()是数据类型,随ECMAScript 6(ES6)的发布而引入。

关于这个符号有两个奇怪的事实。

  • 第一个数据类型,只有JavaScript中没有文字的数据类型

  • 使用Symbol()定义的任何变量都会获得独特的内容,但它并不是private

  • 任何数据都有拥有的符号,对于相同的数据,符号将相同。以下段落中的更多信息,否则它不是TLRD; :)

如何初始化符号?

1。获取具有可调试值的唯一标识符

你可以这样做:

var mySymbol1 = Symbol();

或者这样:

var mySymbol2 = Symbol("some text here");

"some text here"字符串无法从符号中提取,它只是用于调试目的的描述。它不会以任何方式改变符号的行为。虽然,你可以console.log它(这是公平的,因为这个值是用于调试的,所以不要将该日志与其他日志条目混淆):

console.log(mySymbol2);
// Symbol(some text here)

2。获取某些字符串数据的符号

在这种情况下,符号的值实际被考虑在内,这样两个符号可能是非唯一的。

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

让我们称这些符号"第二类"符号。它们与" first-type"不相交。符号(即用Symbol(data)定义的符号)。

接下来的两段仅涉及第一类符号。

如何使用Symbol代替旧数据类型?

让我们首先考虑一个对象,即标准数据类型。我们可以在那里定义一些键值对,并通过指定键来访问值。

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

如果我们有两个名叫Peter的人?

这样做:

var persons = {"peter":"first", "peter":"pan"};

没有多大意义。

所以,似乎是两个完全不同的人有同名的问题。然后让我们引用新的Symbol()。它就像现实生活中的一个人 - 任何人都是独一无二的,但他们的名字可以是平等的。让我们定义两个"人员"。

 var a = Symbol("peter");
 var b = Symbol("peter");

现在我们有两个同名的人。我们的人确实不同吗?他们是;你可以查看:

 console.log(a == b);
 // false

我们如何在那里受益?

我们可以在您的对象中为不同的人创建两个条目,但不能以任何方式弄错。

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
  

注意:
  但值得注意的是,用JSON.stringify对对象进行字符串化将删除所有以Symbol作为键初始化的对。
  执行Object.keys无法返回此类Symbol()->value对。

使用这种初始化,绝对不可能将第一和第二人的条目误认。为他们调用console.log将正确输出他们的第二个名字。

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

在对象中使用时,与定义非可枚举属性相比有何不同?

实际上,已经存在一种方法来定义要从Object.keys和枚举隐藏的属性。这是:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

Symbol()带来了什么区别?不同之处在于您仍然可以通常的方式获取使用Object.defineProperty定义的属性:

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

如果使用符号定义,如前一段所述:

fruit = Symbol("apple");

只有知道其变量,即

,您才有能力接收其价值
console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

此外,在键"apple"下定义另一个属性将使对象删除较旧的属性(如果是硬编码,则可能会抛出错误)。所以,不再是苹果!真可惜。参考前一段,符号是唯一的,并定义一个键,因为Symbol()将使其独特。

类型转换和检查

  • 与其他数据类型不同,无法将Symbol()转换为任何其他数据类型。

  • 可以"制作"通过调用Symbol(data)基于原始数据类型的符号。

  • 在检查类型方面,没有任何变化。

    function 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
    

答案 4 :(得分:16)

以下是我的看法。符号通过阻止对象的键/属性通过一些流行的方法(如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观点。

答案 5 :(得分:2)

JS符号是一种新的原始数据类型。 它们是充当唯一ID的令牌。可以使用Symbol构造函数来创建符号。以MDN为例:

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

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

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123

答案 6 :(得分:0)

符号有两个主要用例:

  1. “隐藏”对象属性。如果我们要将属性添加到“属于”另一个脚本或库的对象中,则可以创建一个 符号并将其用作属性键。象征性财产没有 出现在for..in中,因此不会一起被意外处理 与其他属性。另外,也不会直接访问它,因为 另一个脚本没有我们的符号。所以财产将是 防止意外使用或覆盖。

    因此,我们可以“隐蔽地”将某些东西隐藏到所需的对象中,但是 其他人使用符号属性看不到。

  2. JavaScript使用了许多系统符号,它们可以作为Symbol.*访问。我们可以用它们来改变一些内置的 行为。例如, ...... Symbol.iterator用于迭代,Symbol.toPrimitive用于设置 对象到原始的转换等。

Source