更新:最近Mozilla发布了一篇精彩的文章。如果你好奇,就读读吧。
正如你可能知道的,他们计划在ECMAScript 6中包括新的Symbol原始类型(更不用说其他一些疯狂的东西)。我一直认为:符号概念在Ruby中是不必要的;我们可以很容易地使用纯字符串,就像在JavaScript中做的那样。现在他们决定让JS中的事情变得复杂。
我不明白他们的动机。有人能解释一下JavaScript中是否真的需要符号吗?
更新:最近Mozilla发布了一篇精彩的文章。如果你好奇,就读读吧。
正如你可能知道的,他们计划在ECMAScript 6中包括新的Symbol原始类型(更不用说其他一些疯狂的东西)。我一直认为:符号概念在Ruby中是不必要的;我们可以很容易地使用纯字符串,就像在JavaScript中做的那样。现在他们决定让JS中的事情变得复杂。
我不明白他们的动机。有人能解释一下JavaScript中是否真的需要符号吗?
将符号引入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
以下是我的看法。通过防止对象的键/属性通过一些流行的方法(如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的角度来看,这些方法有时已经足够了。
符号是一种新的特殊类型的对象,可以用作对象中的唯一属性名。使用符号而不是字符串允许不同的模块创建互不冲突的属性。符号也可以有效地设置为私有,这样它们的属性就不能被尚未直接访问符号的任何人访问。
符号是一种新的原语,就像数字、字符串和布尔原语一样。与其他原语不同的是,符号没有文字语法(例如,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');
阅读这篇很棒的文章。
这篇文章是关于Symbol()的,提供了我能找到/制作的实际示例以及我能找到的事实和定义。
TLDR;
Symbol()是随着ECMAScript 6 (ES6)发布而引入的数据类型。
关于这个符号有两个奇怪的事实。
JavaScript中第一个也是唯一一个没有文字的数据类型 用Symbol()定义的任何变量都获得唯一的内容,但它不是真正的私有。 任何数据都有自己的符号,对于相同的数据,符号是相同的。更多信息在下面的段落,否则它不是一个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!
让我们称这些符号为“第二类”符号。它们不以任何方式与“第一类”符号(即用Symbol(data)定义的符号)相交。
下面两段只涉及第一类符号。
使用Symbol而不是旧的数据类型有什么好处?
让我们首先考虑一个对象,一个标准数据类型。我们可以在那里定义一些键值对,并通过指定键来访问这些值。
var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan
如果有两个叫彼得的人呢?
这样做:
var persons = {"peter":"first", "peter":"pan"};
没有什么意义。
所以,这似乎是两个完全不同的人有相同的名字的问题。然后让我们引用new 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作为键初始化的对。 执行对象。键也不会返回这样的Symbol()->值对。
使用这种初始化,绝对不可能将条目误认为第一人称和第二人称。为它们调用console.log将正确地输出它们的第二个名称。
console.log(persons[a]);
// first
console.log(persons[b]);
// pan
在对象中使用时,与定义不可枚举属性相比有何不同?
事实上,已经存在一种方法来定义一个属性,以隐藏在Object中。键和枚举。下面就是:
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
如果如前一段所述使用Symbol定义:
fruit = Symbol("apple");
只有知道它的变量,你才有能力接收它的值。
console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined
此外,在键“apple”下定义另一个属性将使对象丢弃旧的属性(如果硬编码,则可能抛出错误)。所以,再也没有苹果了!真遗憾。参考上一段,符号是唯一的,将键定义为Symbol()将使其唯一。
类型转换和检查
与其他数据类型不同,不可能将Symbol()转换为任何其他数据类型。 可以通过调用symbol (data)来基于基本数据类型“制造”一个符号。 在检查类型方面,没有任何变化。 函数isSymbol(变量){ return typeof someSymbol === "symbol"; } var a_Symbol = Symbol("嘿!"); var totaly_not_a_symbol = "hey"; console.log (isSymbol (a_Symbol));/ /正确的 console.log (isSymbol (totally_Not_A_Symbol));/ /错误
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
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.
源