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

Yan*_*nis 344 javascript symbols ecmascript-harmony ecmascript-6

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

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

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

And*_*erg 217

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

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

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

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

  • 大多数语言(所有主流的语言都是afaik)提供了一些机制,通常是反思,无论如何都可以访问私有语言. (86认同)
  • 该机制不一定是反射 - C++,Java,C#,Ruby,Python,PHP,Objective-C都允许以某种方式访问​​,如果真的想要的话.这不是关于能力,而是沟通. (36认同)
  • @Esailija,我认为这不是真的 - 特别是因为许多语言首先不提供反射.通过反射泄漏私有状态(例如在Java中)应被视为错误,而不是功能.在网页上尤其如此,其中具有可靠的私有状态可以与安全相关.目前,在JS中实现它的唯一方法是通过闭包,这可能既乏味又昂贵. (19认同)
  • @plalx,在网络上,封装有时也是关于安全性的. (3认同)
  • @RolandPihlakas,不幸的是,`Object.getOwnPropertySymbols`不是唯一的漏洞; 更难的是能够使用代理拦截对"私有"属性的访问. (3认同)
  • @AndreasRossberg 是否存在“Object.getOwnPropertySymbols()”的安全问题?我认为隐私的主要目的只是为了防止开发人员相互踩踏,而不是让任何东西(更不用说调试器)完全不可能调整私有值 (3认同)
  • 我曾多次遇到过这种观点,但是引用从未得到过支持。是在规范中真正提到了这个目标还是由其作者讨论了?*在网络上,封装有时也与安全有关*-JS从来都不与安全有关,因为语言本身不提供安全性。封装影响的唯一因素是,如果无法反映私有成员,则侵入者必须复制和粘贴的代码量。 (2认同)

Sam*_*nda 89

符号不保证真正的隐私,但可用于分隔对象的公共和内部属性.让我们举一个例子,我们可以用它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
Run Code Online (Sandbox Code Playgroud)

上面,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
Run Code Online (Sandbox Code Playgroud)

上述方法的缺点:我们为每个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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,符号属性**不是私有**!符号是*无碰撞*.您可能想要阅读已接受的答案. (15认同)
  • 我不会说毫无意义,因为符号默认不可枚举,也不能通过'错误'访问,而任何其他键都可以. (9认同)
  • 我发现您的答案是唯一一个真正有意义的示例,它说明了为什么要将对象的私有属性定义为Symbol而不是普通属性。 (4认同)
  • 是的,符号不保证真正的隐私,但可用于分隔对象的公共和内部属性.对不起,忘了把这一点添加到我的答案中.会相应更新我的答案. (3认同)

Mih*_*nut 37

Symbols 是一种新的,特殊的对象,可以在对象中用作唯一的属性名称.使用Symbol而不是string's允许不同的模块创建不相互冲突的属性.Symbols也可以是私有的,这样任何没有直接访问权限的人都无法访问他们的属性Symbol.

Symbols是一个新的原语.就像number,stringboolean原语,Symbol有可用于创建它们的功能.不像其他的原语,Symbols没有文字语法(例如,如何string'') -创建它们与唯一的办法Symbol以下列方式构造:

let symbol = Symbol();
Run Code Online (Sandbox Code Playgroud)

实际上,Symbol将属性附加到对象只是一种稍微不同的方式 - 您可以轻松地提供众所周知的Symbols标准方法,就像Object.prototype.hasOwnProperty在继承的所有内容中出现的那样Object.

以下是Symbol原始类型的一些好处.

Symbols 内置可调试性

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

Symbols可以用作Object

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

Symbols 可以用作唯一值.

让我们假设你有一个日志库,其中包括多个日志级别,例如logger.levels.DEBUG,logger.levels.INFO,logger.levels.WARN等等.在ES5代码中,你想要制作这些string(所以logger.levels.DEBUG === 'debug')或numbers(logger.levels.DEBUG === 10).这两个都不理想,因为这些值不是唯一值,但Symbols是!所以logger.levels简单地变成:

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

阅读这篇精彩的文章.

  • 我不确定我理解你的例子,为什么你需要`log.levels = {DEBUG:Symbol('debug')`而不仅仅是`log.levels = {DEBUG:'debug'}`.最后它是一样的.我认为值得一提的是,当迭代Object的键时,符号是不可见的.那是他们的"事" (7认同)

nic*_*ael 36

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

TLDR;

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

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

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

  • 任何用Symbol()它定义的变量都会获得独特的内容,但它并不是真正的私有.

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

如何初始化符号?

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

你可以这样做:

var mySymbol1 = Symbol();
Run Code Online (Sandbox Code Playgroud)

或者这样:

var mySymbol2 = Symbol("some text here");
Run Code Online (Sandbox Code Playgroud)

"some text here"字符串不能从符号中提取,它只是用于调试目的的描述.它不会以任何方式改变符号的行为.虽然,你可以console.log(这是公平的,因为该值是用于调试,以免错误地将该日志与其他日志条目一起):

console.log(mySymbol2);
// Symbol(some text here)
Run Code Online (Sandbox Code Playgroud)

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

在这种情况下,实际上考虑了符号的值,这样两个符号可以是非唯一的.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!
Run Code Online (Sandbox Code Playgroud)

我们将这些符号称为"第二类"符号.它们不Symbol(data)以任何方式与"第一类型"符号(即用其定义的符号)相交.

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

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

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

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan
Run Code Online (Sandbox Code Playgroud)

如果我们有两个名叫彼得的人​​怎么办?

这样做:

var persons = {"peter":"first", "peter":"pan"};
Run Code Online (Sandbox Code Playgroud)

没有多大意义.

因此,似乎是两个完全不同的人具有相同名称的问题.让我们再提出新的Symbol().它就像现实生活中的一个人 - 任何人都是独一无二的,但他们的名字可以是平等的.让我们定义两个"人".

 var a = Symbol("peter");
 var b = Symbol("peter");
Run Code Online (Sandbox Code Playgroud)

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

 console.log(a == b);
 // false
Run Code Online (Sandbox Code Playgroud)

我们如何在那里受益?

我们可以在您的对象中为不同的人创建两个条目,并且它们不会以任何方式被误解.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};
Run Code Online (Sandbox Code Playgroud)

注意:
值得注意的是,对象进行字符串化JSON.stringify将删除所有以Symbol作为键初始化的对.
执行Object.keys不会返回这样的Symbol()->value对.

使用此初始化,绝对不可能将条目误认为是第一人和第二人.调用console.log它们将正确输出其第二个名称.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan
Run Code Online (Sandbox Code Playgroud)

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

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

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

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});
Run Code Online (Sandbox Code Playgroud)

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

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green
Run Code Online (Sandbox Code Playgroud)

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

fruit = Symbol("apple");
Run Code Online (Sandbox Code Playgroud)

只有知道变量即可以获得其价值

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined
Run Code Online (Sandbox Code Playgroud)

此外,在键下定义另一个属性"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
    
    Run Code Online (Sandbox Code Playgroud)

  • 关于符号的很好的答案,但是我仍然不知道为什么我要使用带有符号键的对象而不是数组。如果我有多个像 {"peter":"pan"} {"john":"doe"} 这样的人,将它们放在一个对象中对我来说感觉很糟糕。出于同样的原因,我不创建具有重复属性(如 personFirstName1、personFirstName2)的类。再加上无法对其进行字符串化,我看不到好处,只有坏处。 (5认同)
  • @KNU 不是;我已经收集了信息并自己写了这个答案 (2认同)

Cho*_*ang 18

我就是这样看的.符号通过阻止对象的键/属性通过一些流行的方法(如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…)
Run Code Online (Sandbox Code Playgroud)

尽管给定了一个对象本身,但仍然可以通过反射,代理,Object.getOwnPropertySymbols()等公开这些属性,没有通过一些直接方法访问它们的自然方法,从OOP的角度来看,这有时可能就足够了.