ECMAScript6 -Symbols

 

Symbols-标志


ECMAScript 6 symbols began as a way to create private object members, a feature JavaScript developers have long wanted. The focus was around creating properties that were not identified by string names. Any property with a string name was easy picking to access regardless of the obscurity of the name. The initial “private names” feature aimed to create non-string property names. That way, normal techniques for detecting these private names wouldn’t work.

  ES6 标志开始用来创建私有对象成员,是一个js开发者期待很久的功能。重点是围绕创建非字符串名字的属性。字符串名字的属性,无论名字是否明确,都可以很容易的获取到。最初“私有名字”功能的目标是创建非字符串属性的名字。那样,普通的用来检测私有名字的技术就无法工作。

The private names proposal eventually evolved into ECMAScript 6 symbols. While the implementation details remained the same (non-string values for property identifiers), TC-39 dropped the requirement that these properties be private. Instead, the properties would be categorized separately, being non-enumerable by default but still discoverable.

  私有名字提议最终演化成了ES6 标志,然而实施的细节是相同的(属性的标识符是非字符串),TC-39放弃了这些属性是私有的需求。相反,这些属性会单独分类,默认情况下是不可数的但仍可被发现。

Symbols are actually a new kind of primitive value, joining strings, numbers, booleans, null, and undefined. They are unique among JavaScript primitives in that they do not have a literal form. The ECMAScript 6 standard uses a special notation to indicate symbols, prefixing the identifier with @@, such as @@create. This book uses this same convention for ease of understanding.

  标志实际上是一种新的原始值,加入到字符串,数值,布尔值,null,undefined中。他们在JavaScript原语中是独一无二的,他们没有文字形式。ES6标准使用了特殊的符号来表示标志,在标识符之前增加前缀@@,例如@@create。为了方便理解,本书采用了同样的约定。


Despite the notation, symbols do not exactly map to strings beginning with “@@”. Don’t try to use string values where symbols are required.


 

Creating Symbols-创建标志

You can create a symbol by using the Symbol function, such as:

你可以使用Symbol函数来创建symbol(标志),例如:

var firstName = Symbol();
var person = {};

person[firstName] = "Nicholas";
console.log(person[firstName]);     // "Nicholas"

 In this example, the symbol firstName is created and used to assign a new property on person. That symbol must be used each time you want to access that same property. It’s a good idea to name the symbol variable appropriately so you can easily tell what the symbol represents.

  在这个例子中,标志firstName被创建,接着赋给了person对象的新属性。如果你想获取到相同的属性,必须用到这个标志。适当的命名标志是一个很好的想法,这样你就可以很容易的辨别这个标志所代表的内容。


Because symbols are primitive values, new Symbol() throws an error when called. It is possible to create an instance of Symbol via new Object(yourSymbol), but it’s unclear when this capability would be useful.

  注意:因为标志是原始值,所以new Symbol()时会抛出错误。可以通过new Object(yourSymbol)来创建一个标志的实例,但是这个功能是否可用还不明确。


The Symbol function accepts an optional argument that is the description of the symbol. The description itself cannot be used to access the property but is used for debugging purposes. For example:

  symbol函数接受一个可选的参数用来描述标志。描述本身不能用来获取属性,但是可以用来调试。例如:

var firstName = Symbol("first name");
var person = {};

person[firstName] = "Nicholas";

console.log("first name" in person);        // false
console.log(person[firstName]);             // "Nicholas"
console.log(firstName);                     // "Symbol(first name)"

 A symbol’s description is stored internally in a property called [[Description]]. This property is read whenever the symbol’s toString() method is called either explicitly or implicitly (as in this example). It is not otherwise possible to access [[Description]] directly from code. It’s recommended to always provide a description to make both reading and debugging code using symbols easier.

  一个标志的描述存储在一个属性的内部叫做[[Description]].当调用标志的toString方法时-无论时显示的还是隐示(例子中),都会读取这个属性。不可以直接从代码访问[Description]]

为了很好的阅读和调试代码,建议总是为标志提供一个描述。

Identifying Symbols-识别标志

Since symbols are primitive values, you can use the typeof operator to identify them. ECMAScript 6 extends typeof to return "symbol" when used on a symbol. For example:

  因为标志是原始值,所以你可以使用typeof来识别它们。ES6 扩展了typeof,当用于检测一个标志时,会返回“symbol”。例如:

var symbol = Symbol("test symbol");
console.log(typeof symbol);         // "symbol"

 While there are other indirect ways of determining whether a variable is a symbol, typeof is the most accurate and preferred way of doing so.

还有一些间接的方法来检测一个变量是否是标志,typeof是最准确和首选的方法。

Using Symbols-使用标志

You can use symbols anywhere you would use a computed property name. You’ve already seen the use of bracket notation in the previous sections, but you can use symbols in computed object literal property names as well as with Object.defineProperty(), and Object.defineProperties(), such as:

  你可以将标志用于任何你将会使用“计算的”属性名字的地方。在之前的章节中,你已经了解到使用方括号,和Object.defineProperty()、Object.defineProperties()一样,你可以使用标志来计算对象文字属性名称。例如:

var firstName = Symbol("first name");
var person = {
    [firstName]: "Nicholas"
};

// make the property read only
Object.defineProperty(person, firstName, { writable: false });

var lastName = Symbol("last name");

Object.defineProperties(person, {
    [lastName]: {
        value: "Zakas",
        writable: false
    }
});

console.log(person[firstName]);     // "Nicholas"
console.log(person[lastName]);      // "Zakas"

 With computed property names in object literals, symbols are very easy to work with.

  对于对象字面量中的计算的属性名字,使用标志很容易。

Sharing Symbols-分享标志

You may find that you want different parts of your code to use the same symbols. For example, suppose you have two different object types in your application that should use the same symbol property to represent a unique identifier. Keeping track of symbols across files or large codebases can be difficult and error-prone. That’s why ECMAScript 6 provides a global symbol registry that you can access at any point in time.

  你会发现,在你代码的不同地方你希望使用同样的标志,假设在你的应用中有两个不同的对象会使用同样的标志属性来代表一个独特的标识符。跨越文件和大的代码库来追踪一个标志会很困难和容易出错的。这就是为什么ES6 提供了一个全局的标志注册表,你就可以在任何地点来访问。

When you want to create a symbol to be shared, use the Symbol.for() method instead of calling Symbol(). The Symbol.for() method accepts a single parameter, which is a string identifier for the symbol you want to create (this value is also used as the description). For example:

  当你想创建一个可分享的标志时,使用Symbol.for()方法来替代Symbol()方法。Symbol.for()方法接受一个唯一的参数-你创建的标志的字符串标识符(这个值也可以用作描述)。例如:

var uid = Symbol.for("uid");
var object = {};

object[uid] = "12345";

console.log(object[uid]);       // "12345"
console.log(uid);               // "Symbol(uid)"

 The Symbol.for() method first searches the global symbol registry to see if a symbol with the key "uid" exists. If so, then it returns the already existing symbol. If no such symbol exists, then a new symbol is created and registered into the global symbol registry using the specified key. The new symbol is then returned. That means subsequent calls to Symbol.for() using the same key will return the same symbol:

Symbol.for()方法首先检索全局的标志注册表来查看“uid”是否已经存在,如果存在,返回已经存在的标志,如果这个标志不存在,创建新的标志注册并将指定的键值到全局标志注册表中。这意味这接下来使用相同的键值调用symbol.for会返回同样的标志。

var uid = Symbol.for("uid");
var object = {
    [uid]: "12345"
};

console.log(object[uid]);       // "12345"
console.log(uid);               // "Symbol(uid)"

var uid2 = Symbol.for("uid");

console.log(uid === uid2);      // true
console.log(object[uid2]);      // "12345"
console.log(uid2);              // "Symbol(uid)"

 In this example, uid and uid2 contain the same symbol and so they can be used interchangeably. The first call to Symbol.for() creates the symbol and second call retrieves the symbol from the global symbol registry.

在这个例子中,uid和uid2包含相同的标志,所以它们可以交换使用。第一次调用Symbol.for创建标志,第二次调用在全局标志注册表中检索标志。

Another unique aspect of shared symbols is that you can retrieve the key associated with a symbol in the global symbol registry by usingSymbol.keyFor(), for example:

  分享标志另一个独特的方面是,你可以使用Symbol.keyFor()在全局注册表中检索与标志相关联的键值。例如:

var uid = Symbol.for("uid");
console.log(Symbol.keyFor(uid));    // "uid"

var uid2 = Symbol.for("uid");
console.log(Symbol.keyFor(uid2));   // "uid"

var uid3 = Symbol("uid");
console.log(Symbol.keyFor(uid3));   // undefined

Notice that both uid and uid2 return the key "uid". The symbol uid3 doesn’t exist in the global symbol registry, so it has no key associated with it and Symbol.keyFor() returns undefined.

注意,uid和uid3都返回键值“uid”。标志uid3zai全局标志注册表中不存在,所以没有与之关联的键值,Symbol.keyFor()返回undefined。


The global symbol registry is a shared environment, just like the global scope. That means you can’t make assumptions about what is or is not already present in that environment. You should use namespacing of symbol keys to reduce the likelihood of naming collisions when using third-party components. For example, jQuery might prefix all keys with "jquery.", such as "jquery.element".

全局标志注册表是一个共享环境,就像全局作用域一样。这意味着你不能假设在当前的环境中什么存在什么不存在。当使用第三方插件的时候,你需要为标志的键名使用命名空间来减少命名冲突的可能性。例如,jQuery可能会为所有的键名增加前缀“jquery”,例如“jquery.element”。

Finding Object Symbols-查找对象的标志

Similar to other properties on objects, you can access symbol properties using the Object.getOwnPropertySymbols() method. This method works exactly the same as Object.getOwnPropertyNames() except that the returned values are symbols rather than strings. Since symbols technically aren’t property names, they are omitted from the result of Object.getOwnPropertyNames().

类似于对象上的其他属性,你可以通过Object.getOwnPropertySymbols()方法来获取标志属性。这个方法和Object.getOwnPropertyNames()完全相同除了返回的值是标志而不是其他的字符串。因为严谨的来说标志并不是属性的名字,所以它们从Object.getOwnPropertyNames()中的返回值里面省略了。

 The return value of Object.getOwnPropertySymbols() is an array of symbols that represent own properties. For example:

Object.getOwnPropertySymbols()的返回值是一个代表拥有的标志属性的数组。例如:

var uid = Symbol.for("uid");
var object = {
    [uid]: "12345"
};

var symbols = Object.getOwnPropertySymbols(object);

console.log(symbols.length);        // 1
console.log(symbols[0]);            // "Symbol(uid)"
console.log(object[symbols[0]]);    // "12345"

 In this code, object has a single symbol property. The array returned from Object.getOwnPropertySymbols() is an array containing just that symbol

在这个代码中,object只有一个标志属性,从Object.getOwnPropertySymbols()返回的数组包含了这个标志。


All objects start off with zero own symbol properties (although they do have some inherited symbol properties).


Coercing Symbols to Strings-标志qiangzhi转换为字符串

TODO-未完待续

String(symbol) works but symbol + “” throws

Well-Known Symbols-公共的标志

In addition to the symbols you defined, there are some predefined symbols as well (called well-known symbols in the specification). These symbols represent common behaviors in JavaScript that were previously considered internal-only operations. Each well-known symbol is represented by a property on Symbol, such as Symbol.create for the @@create symbol.

  除了你定义的标志,已经预定义了一些标志(在规范说明书中叫做well-known symbols).这些标志代表了之前js中被认为是内部演示操作的内部行为。每一个公共标志代表了标志的一个属性,例如Symbol.create的@@create标志。

A central theme for both ECMAScript 5 and ECMAScript 6 was exposing and defining some of the “magic” parts of JavaScript - the parts that couldn’t be emulated by a developer. ECMAScript 6 follows this tradition by exposing even more of the previously internal-only logic of the language. It does so primarily through the use of symbol prototype properties that define the basic behavior of certain objects.

ES5和ES6的中心主题是暴露和定义一些js的“有魔力”的-不能被开发者模仿的部分,ES6延续了这个惯例暴露了更多这个语言之前内部的逻辑。首先通过使用标志的prototype属性来定义特定对象的基本行为。

 


Overwriting a method defined with a well-known symbol changes an ordinary object to an exotic object because this changes some internal default behavior. 

重写公共标志定义的方法会将一个普通对象转变成独特对象,因为这回改变一些内部的默认行为。


The well-known symbols are:

公共的标志有:

  • @@hasInstance - a method used by instanceof to determine an object’s inheritance.
  • @@isConcatSpreadable - a Boolean value indicating if use with Array.prototype.concat() should flatten the collection’s elements.
  • @@iterator - a method that returns an iterator (see Chapter 7).
  • @@match - a method used by String.prototype.match() to compare strings.
  • @@replace - a method used by String.prototype.replace() to replace substrings.
  • @@search - a method used by String.prototype.search() to locate substrings.
  • @@species - the constructor from which derived objects are made.
  • @@split - a method used by String.prototype.split() to split up strings.
  • @@toPrimitive - a method that returns a primitive value representation of the object.
  • @@toStringTag - a string used by Object.prototype.toString() to create an object description.
  • @@unscopeables - an object whose properties are the names of object properties that should not be included in a with statement.

Some of the well-known symbols are discussed below while others are discussed throughout the book to keep them in the correct context.

  一些公共标志会在下面讨论,其他的标志会在本书的其他章节讨论来保持它们正确的上下文。

@@toStringTag

One of the most interesting problems in JavaScript has been the availability of multiple global execution environments. This occurs in web browsers when a page includes an iframe, as the page and the iframe each have their own execution environments. In most cases, this isn’t a problem, as data can be passed back and forth between the environments with little cause for concern. The problem arises when trying to identify what type of an object you’re dealing with.

  js中最有意思的一个问题是多重全局环境的可用性。web浏览器中,当一个web页面拥有一个iframe时会出现这个问题,页面和iframe都有它们自己的执行环境。在大多数例子中,这不是一个问题,环境之间来回传递参数很少引起关注。当试图确定你处理对象的类型时问题出现了。

The canonical example of this is passing an array from the iframe into the containing page or vice-versa. Now in a different execution environment, instanceof Array returns false because the array was created with a constructor from a different environment.

  一个典型的例子是从iframe中向包含它的页面传递一个数组或者相反的操作。在不同的执行环境中,instanceof Array返回false因为数组是在另外的一个环境中创建的。

Developers soon found a good way to identify arrays. It was discovered that by calling the standard toString() method on the object, a predictable string was always returned. Thus, many JavaScript libraries began including a function that works similar to this:

  开发者迅速发现了一个很好的方法来识别数组。通过调用对象上的toString方法,总是返回一个可以预测的字符串。这样,很多js库都包含类似下面例子的函数:

function isArray(value) {
    return Object.prototype.toString.call(value) === "[object Array]";
}

console.log(isArray([]));   // true

 This may look a bit roundabout, but in reality it was found to work quite well in all browsers. The toString() method on arrays isn’t very useful for this purpose because it returns a string representation of the items it contains. The toString() method on Object.prototype, however, had this quirk where it included some internally-defined name in the result. By using this method on an object, you could retrieve what the JavaScript environment thought the data type was.

  这个看起来可能有一点迂回,但是事实上在浏览器中它能够很好的工作。对于这个目的,数组的toString方法不是很有用,因为它返回一个代表数组包含元素的字符串。Object.prototye的toString方法,返回结果中包含一些内部定义的名字。通过使用对象上的这个方法,你可以获得js环境认为的数据的类型。

Developers quickly realized that since there was no way to change this behavior, it was possible to use the same approach to distinguish between native objects and those created by developers. The most important case of this was the ECMAScript 5 JSON object.

  开发者们很快意识到既然没有办法改变这种行为,可以用同样的方法来区分原始对象和开发者创建的对象,最重要的例子就是ES5 中的JSON对象。

Prior to ECMAScript 5, many used Douglas Crockford’s json2.js, which created a global JSON object. As browsers started to implement the JSONglobal object, it became necessary to tell whether the global JSON was provided by the JavaScript environment itself or through some other library. Using the same technique, many created functions like this:

  ES5之前,很多人使用Douglas Crockford的json2.js,它创建了一个全局的JSON对象。当浏览器开始实现JSON全局对象的时候,区分js环境本身提供的全局的JSON还是通过其他的库提供的JSON对象就变得很必要。使用同样的技术,很多函数创建成这样:

function supportsNativeJSON() {
    return typeof JSON !== "undefined" &&
        Object.prototype.toString.call(JSON) === "[object JSON]";
}

Here, the same characteristic that allowed developers to identify arrays across iframe boundaries also provided a way to tell if JSON was the native one or not. A non-native JSON object would return [object Object] while the native version returned [object JSON]. From that point on, this approach became the de facto standard for identifying native objects.

   这里,开发者可以识别穿越iframe界限的数组,也提供了一种方式来区分JSON是否是原始的。非原始的JSON返回[object object]而原始的返回[object JSON]。通过这一点,这个方法变成了识别原始对象的事实上的方法。

ECMAScript 6 explains this behavior through the @@toStringTag symbol. This symbol represents a property on each object that defines what value should be produced when Object.prototype.toString.call() is called on it. So the value returned for arrays is explained by having the @@toStringTagproperty equal "Array". Likewise, you can define that value for your own objects:

ES6通过@@stringTag标志来解释了这一行为。这个标志代表每个对象上的一个属性,当调用 Object.prototype.toString.call()的时候会产生什么值。所以数组返回的值通过@@toStringTag属性解释之后返回“Array”。同样的,你可以为你自己的对象定义这个值:

function Person(name) {
    this.name = name;
}

Person.prototype[Symbol.toStringTag] = "Person";

var me = new Person("Nicholas");

console.log(me.toString());                         // "[object Person]"
console.log(Object.prototype.toString.call(me));    // "[object Person]"

 In this example, a @@toStringTag property is defined on Person.prototype to provide the default behavior for creating a string representation. SincePerson.prototype inherits Object.prototype.toString(), the value returned from @@toStringTag is also used when calling me.toString(). However, you can still define your own toString() that provides a different behavior without affecting the use of Object.prototype.toString.call():

  在这个例子中,Person.prototype上定义了一个@@toStringTag 属性用来为创建字符串表示提供一个默认的行为。Person.prototype继承了Object.prototype.toString()。@@toStringTag的返回值同样也用于调用me.toString()。然而,你依然可以定义你自己的toString()提供不同的行为不受使用Object.prototype.toString.call()的影响:

function Person(name) {
    this.name = name;
}

Person.prototype[Symbol.toStringTag] = "Person";

Person.prototype.toString = function() {
    return this.name;
};

var me = new Person("Nicholas");

console.log(me.toString());                         // "Nicholas"
console.log(Object.prototype.toString.call(me));    // "[object Person]"

 This code defines Person.prototype.toString() to return the value of the name property. Since Person instances no longer inherit Object.prototype.toString(), calling me.toString() exhibits a different behavior.

这个代码定义Person.prototype.toString()返回name属性的值。因为Person实例不在继承Object.prototype.toString(),调用me.toString展现了不同的行为。


All objects inherit @@toStringTag from Object.prototype unless otherwise specified. This default property value is "Object".

  除了特定的,所有的对象都从Object.prototype继承了@@toStringTag。这个默认属性值是“Object”


There is no restriction on which values can be used for @@toStringTag on developer-defined objects. For example, there’s nothing preventing you from using "Array" as the value of @@toStringTag, such as:

  在开发者定义的对象上为@@toStringTag使用什么值没有限制。例如,不会阻止你使用“Array”作为@@toStringTag的值。例如:

function Person(name) {
    this.name = name;
}

Person.prototype[Symbol.toStringTag] = "Array";

Person.prototype.toString = function() {
    return this.name;
};

var me = new Person("Nicholas");

console.log(me.toString());                         // "Nicholas"
console.log(Object.prototype.toString.call(me));    // "[object Array]"

 Here, the result of calling Object.prototype.toString() is "[object Array]", which is the same as you would get from an actual array. This highlights the fact that Object.prototype.toString() is no longer a completely reliable way of identifying an object’s type.

 这里,调用Object.prototype.toString()的返回值是[object Array],和你从真实数组返回的值一样。这表示Object.prototype.toString()不在是一个完全可靠的方法来识别对象类型。

It’s possible to change the string tag for native objects by assigning to @@toStringTag on their prototype. For example: 

  通过在对象的原型上指定@@toStringTag来改变原始对象的字符串标签。例如:

Array.prototype[Symbol.toStringTag] = "Magic";

var values = [];

console.log(Object.prototype.toString.call(values));    // "[object Magic]"

 Even though @@toStringTag is overwritten for arrays in this example, the call to Object.prototype.toString() results in "[object Magic]". While it’s recommended not to change built-in objects in this way, there’s nothing in the language that forbids it.

  尽管本例中重写了数组的@@toStringTag,使得调用Object.prototype.toString()返回"[object Magic]"。建议不要通过这种方法改变内置对象,在这个语言中并没有禁止。

@@toPrimitive

JavaScript frequently attempts to convert objects into primitive values implicitly when certain operations are applied. For instance, when you compare a string to an object using double equals (==), the object is converted into a primitive value before comparing. Exactly what value should be used was previously an internal operation that is exposed in ECMAScript 6 through the @@toPrimitive method.

  当使用特定的操作时,js经常试图将对象转变为原始值。例如,当你通过双等号(==)来比较一个字符串和一个对象时,在比较之前,对象会转变为一个原始值。正好是ES6通过@@toPrimitive method方法暴露的之前是内部操作的值。

The @@toPrimitive method is defined on the prototype of each standard type and prescribes the exact behavior. When a primitive conversion is needed, @@toPrimitive is called with a single argument, referred to as hint in the specification. The hint argument is "default", specifying that the operation has no preference as to the type, "string", indicating a string should be returned, or "number", if a number is necessary to perform the operation. Most standard objects treat "default" as equivalent to "number" (except for Date, which treats "default" as "string").

  @@toPromitive方法定义在每一个标准类型的原型上,规定了具体的行为。当需要原始转换的时候,传递一个参数调用@@toPromitive,关联到规范中的hit。hint参数是“default”,说明操作没有类型的优先权,“string”,表示返回一个字符串,或者是“number”, 如果数值对执行操作很必要。很多标准的对象把“default”等同于“number”(除了Date,把“default”等同于“string”)。 

TODO

@@isConcatSpreadable

TODO

@@species

TODO

@@unscopeables

TODO

Only applied to with statement object records - does not refer to other scopes.

@@hasInstance

TODO

Summary

TODO

 

转载于:https://www.cnblogs.com/avivaWang/p/4383203.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值