除了自己创建的 symbol,JavaScript 还内建了一些在 ECMAScript 5 之前没有暴露给开发者的 symbol,它们代表了内部语言行为。ES5的一个中心主旨时将JavaScript中的一些神奇的部分暴露出来,并详细定义了这些开发者们当初模拟不了的功能。ES6延续了这个传统,新标准主要通过在原型链上定义与Symbol相关的属性来暴露更多的语言内部逻辑。
ES6开放了之前JavaScript中常见的内部操作,并通过预定义一些well-known Symbol来表示。每一个这类Symbol都是Symbol对象的一个属性,例如Symbol.match.
All static properties of the Symbol constructor are Symbols themselves, whose values are constant across realms. They are known as
well-known Symbols
, and their purpose is to serve as “protocols” for certainbuilt-in JavaScript operations
, allowing users to customize the language’s behavior. For example, if a constructor function has a method withSymbol.hasInstance
as its name, this method will encode its behavior with theinstanceof
operator.
Prior to well-known Symbols, JavaScript used normal properties to implement certain built-in operations. For example, the
JSON.stringify
function will attempt to call each object’stoJSON()
method, and theString
function will call the object’stoString()
andvalueOf()
methods. However, as more operations are added to the language, designating each operation a “magic property” can break backward compatibility and make the language’s behavior harder to reason with. Well-known Symbols allow the customizations to be “invisible” from normal code, which typically only read string properties.
The static properties are all well-known Symbols. In these Symbols’ descriptions, we will use language like “Symbol.hasInstance is a method determining…”, but bear in mind that this is referring to the semantic of an object’s method having this Symbol as the method name (because well-known Symbols act as “protocols”), not describing the value of the Symbol itself.
The Symbol.hasInstance well-known symbol is used to determine if a constructor object recognizes an object as its instance. The instanceof operator’s behavior can be customized by this symbol.
一个在执行instanceof时调用的内部方法,用于检测对象的继承信息。每一个函数中都有一个Symbol.hasInstance方法,用于确定对象是否为函数的示例。该方法在Function.prototype中定义,所以所有函数都继承了instance of属性的默认行为。为了确保Symbol.hasInstance不会被意外重写,该方法被定义为不可写、不可配置并且不可枚举。Symbol.hasInstance方法只接受一个参数,即要检查的值。如果传入的值时函数的实例,则返回true。
Array[Symbol.hasInstance](obj)
类似于
obj instanceof Array
比如
class Array1 {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
// true
console.log([] instanceof Array1);
本质上,ES6只是将instanceof操作符重新定义为此方法的简写语法。现在引入方法调用后,就可以随意改变instanceof 的运行方式了。比如
function MyObject() {
}
let obj = new MyObject();
// true
console.log(obj instanceof MyObject);
// 只有通过Object.defineProperty方法才能修改一个不可写属性
// 修改Symbol.hasInstance,为其定义一个总是返回false的新函数
Object.defineProperty(MyObject, Symbol.hasInstance, {
value: function (v) {
return false;
}
})
obj = new MyObject();
// false
console.log(obj instanceof MyObject);
只有通过Object.defineProperty方法才能修改一个不可写属性。 上面的示例调用这个方法来改写了Symbol.hasInstance,为其定义了一个总是返回false的函数,即使obj实际上确实是MyObject类的实例,在调用defineProperty之后,instanceof运算符也是返回false.
当然也可以基于任意条件,通过值检测来确定被检测的是否为实例。举个例子,可以将1~100的数字定义为一个特殊数字类型的实例。
function SpecialNumber(){
}
Object.defineProperty(SpecialNumber,Symbol.hasInstance,{
value: function(v){
return (v instanceof Number) && (v >= 1 && v <= 100);
}
});
var two = new Number(2),
zero = new Number(0);
// true
console.log(two instanceof SpecialNumber);
// false
console.log(zero instanceof SpecialNumber);