2019-7-17
第六章 符号与符号属性
(一)符号值
-
创建: 符号没有字面量形式,使用全局Symbol()函数创建符号值。
let firstName = Symbol(); let person = {}; person[firstName] = "Nicholas"; console.log(person[firstName]); // "Nicholas"Symbol()函数可接受一个参数作为描述符号值,该参数不能用来访问对应属性,但能用于调试。符号的描述信息被存储在内部属性[[Description]]中,只有当符号的toSting()方法被显式或隐式调用时,该属性才会被读取。
let firstName = Symbol("first name"); let person = {}; person[firstName] = "Nicholas"; console.log("first name" in person); // false console.log(person[firstName]); // "Nicholas" console.log(firstName); // "Symbol(first name)" -
使用:
可以在任意能使用“可计算属性名”的场合使用符号。
let firstName = Symbol("first name"); // use a computed object literal property let person = { [firstName]: "Nicholas" }; // make the property read only Object.defineProperty(person, firstName, { writable: false }); let lastName = Symbol("last name"); Object.defineProperties(person, { [lastName]: { value: "Zakas", writable: false } }); console.log(person[firstName]); // "Nicholas" console.log(person[lastName]); // "Zakas" -
共享:
使用Symbol.for()方法,创建共享符号值,仅接受字符串类型的单个参数,此参数即作为标识符(键),也作为描述信息。
全局符号注册表:保存共享符号值和它的键。
let uid = Symbol.for("uid"); let object = { [uid]: "12345" }; console.log(object[uid]); // "12345" console.log(uid); // "Symbol(uid)" let uid2 = Symbol.for("uid"); console.log(uid === uid2); // true console.log(object[uid2]); // "12345" console.log(uid2); // "Symbol(uid)"使用Symbol.keyFor()方法在全局符号注册表中根据符号值,查找出它的键。
let uid = Symbol.for("uid"); console.log(Symbol.keyFor(uid)); // "uid" let uid2 = Symbol.for("uid"); console.log(Symbol.keyFor(uid2)); // "uid" let uid3 = Symbol("uid"); console.log(Symbol.keyFor(uid3)); // undefined -
转换:
符号值无法被转换为字符串值或数值
// 调用String()方法可以获取符号的字符串描述信息 let uid = Symbol.for("uid"), desc = String(uid); console.log(desc); // "Symbol(uid)"// 直接将符号值与字符串拼接会产生错误 let uid = Symbol.for("uid"), desc = uid + ""; // error!// 对符号使用所有数学运算符会产生错误 let uid = Symbol.for("uid"), sum = uid / 1; // error!符号值在逻辑运算中会被认为等价于true
// 使用逻辑运算,符号值会被认为等价于true let uid = Symbol.for("uid"), sum = uid || false; // "Symbol(uid)" -
检索符号属性
Object.keys(): 检索所有可枚举属性;不包括Symbol类型的属性Object.getOwnPropertyNames(): 检索所有属性,不论是否可枚举;不包括Symbol类型的属性Object.getOwnPropertySymbols(): 检索Symbol类型的属性let uid = Symbol.for("uid"); let object = { [uid]: "12345" }; let symbols = Object.getOwnPropertySymbols(object); console.log(symbols.length); // 1 console.log(symbols[0]); // "Symbol(uid)" console.log(object[symbols[0]]); // "12345"
可以通过typeof识别符号值:
let symbol = Symbol("test symbol");
console.log(typeof symbol); // "symbol"
(二) 知名符号
知名符号:所有对象起初都不包含任何自有符号类型属性,但对象可以从它们的原型上继承符号类型属性。ES6预定义了一些此类属性,它们被称为“知名符号”。
使用知名符号暴露内部方法:
ES6定义了“知名符号”来代表JS中一些公共行为,而这些行为此前被认为只能是内部操作。
每个知名符号都对应全局Symbol对象的一个属性,例如Symbol.create
| 知名符号 | 描述 |
|---|---|
| Symbol.hasInstance | A method used by instanceof to determine an object’s inheritance. |
| Symbol.isConcatSpreadable | A Boolean value indicating that Array.prototype.concat() should flatten the collection’s elements if the collection is passed as a parameter to Array.prototype.concat(). |
| Symbol.iterator | A method that returns an iterator. |
| Symbol.match | A method used by String.prototype.match() to compare strings. |
| Symbol.replace | A method used by String.prototype.replace() to replace substrings. |
| Symbol.search | A method used by String.prototype.search() to locate substrings. |
| Symbol.species | The constructor for making derived objects. |
| Symbol.split | A method used by String.prototype.split() to split up strings. |
| Symbol.toPrimitive | A method that returns a primitive value representation of an object. |
| Symbol.toStringTag | A string used by Object.prototype.toString() to create an object description. |
| Symbol.unscopables | An object whose properties are the names of object properties that should not be included in a with statement. |
1.Symbol.hasInstance
每个函数都具有一个Symbol.hasInstance方法,用于判断指定对象是否为本函数的一个实例。该方法定义在Funtion.prototype上,因此所有函数都继承了面对instanceof运算符时的默认行为。
obj instanceof Array;
// 这句代码等价于
Array[Symbol.hasInstance](obj);
Symbol.hasInstance属性本身是不可写入、不可配置、不可枚举的,要重写一个不可写入的属性,必须使用
Object.defineProperty()
function SpecialNumber() {
// empty
}
// 将介于1到100之间的数值认定为一个特殊的数值类型
Object.defineProperty(SpecialNumber, Symbol.hasInstance, {
value: function(v) {
return (v instanceof Number) && (v >=1 && v <= 100);
}
});
let two = new Number(2),
zero = new Number(0);
console.log(two instanceof SpecialNumber); // true
console.log(zero instanceof SpecialNumber); // false
2. Symbol.isConcatSpreadable
该知名符号与其他知名符号不同,默认情况下并不会作为任意常规对象的属性,它只出现在类数组对象上。
类数组对象:
1)拥有长度属性length;
2)属性名要为数值类型
Symbol.isConcatSpreadable 用于标识类数组对象在作为concat()方法的参数时,是否要分离出各个项的值。
let collection = {
0: "Hello",
1: "world",
length: 2,
[Symbol.isConcatSpreadable]: true // 需要分离
};
let messages = [ "Hi" ].concat(collection);
console.log(messages.length); // 3
console.log(messages); // ["Hi","Hello","world"]
// 如果[Symbol.isConcatSpreadable]为false, 则message为['Hi', ['Hello', 'world']]
3. Symbol.match 、Symbol.replace、 Symbol.search、 Symbol.split
这些符号属性定义在RegExp.prototype上,以供对应的字符串方法使用。
| 知名符号 | 描述 | 对应字符串的方法 | 描述 |
|---|---|---|---|
| Symbol.match | 接受一个字符串参数,返回一个包含匹配结果的数组,若匹配失败,返回null | match(regex) | 匹配 |
| Symbol.replace | 接受一个字符串参数与一个替换用的字符串, 返回替换后的结果字符串 | replace(regex, replacement) | 替换 |
| Symbol.search | 接受一个字符串参数,返回匹配结果的数值所有,匹配失败,返回-1 | search(regex) | 查找 |
| Symbol.split | 接受一个字符串参数,返回一个用匹配值分割而成的字符串数组 | split(regex) | 分割 |
// effectively equivalent to /^.{10}$/
let hasLengthOf10 = {
[Symbol.match]: function(value) {
return value.length === 10 ? [value] : null;
},
[Symbol.replace]: function(value, replacement) {
return value.length === 10 ? replacement : value;
},
[Symbol.search]: function(value) {
return value.length === 10 ? 0 : -1;
},
[Symbol.split]: function(value) {
return value.length === 10 ? ["", ""] : [value];
}
};
let message1 = "Hello world", // 11 characters
message2 = "Hello John"; // 10 characters
let match1 = message1.match(hasLengthOf10),
match2 = message2.match(hasLengthOf10);
console.log(match1); // null
console.log(match2); // ["Hello John"]
let replace1 = message1.replace(hasLengthOf10, "Howdy!"),
replace2 = message2.replace(hasLengthOf10, "Howdy!");
console.log(replace1); // "Hello world"
console.log(replace2); // "Howdy!"
let search1 = message1.search(hasLengthOf10),
search2 = message2.search(hasLengthOf10);
console.log(search1); // -1
console.log(search2); // 0
let split1 = message1.split(hasLengthOf10),
split2 = message2.split(hasLengthOf10);
console.log(split1); // ["Hello world"]
console.log(split2); // ["", ""]
这虽然是个简单的例子,却表明能够进行比现有正则表达式更复杂的匹配,让自定义模式匹配更加可行。
4. Symbol.toPrimitive
定义在常规类型的原型上,规定了对象在被转换为基本类型值得时候会发生什么。
Symbol.toPrimitive接受一个提示性的字符串参数,该提示性参数会在调用时由js引擎自动填写。参数为“number”, 返回一个数值; 参数为“string”, 返回一个字符串; 参数为“default”返回值类型没有特别要求。
“数值模式”: valueOf() —> toString() —> 抛出error
“字符串模式”: toString() —> valueOf() —> 抛出error
"默认模式": 多数情况都等价于数值模式,只有Data类型例外,默认使用字符串模式。默认模式只在使用 ==运算符、+运算符或传递单一参数给Data()构造器的时候被使用。
function Temperature(degrees) {
this.degrees = degrees;
}
Temperature.prototype[Symbol.toPrimitive] = function(hint) {
switch (hint) {
case "string":
return this.degrees + "\u00b0"; // degrees symbol
case "number":
return this.degrees;
case "default":
return this.degrees + " degrees";
}
};
let freezing = new Temperature(32);
console.log(freezing + "!"); // "32 degrees!" 默认模式
console.log(freezing / 2); // 16 数值模式
console.log(String(freezing)); // "32°" 字符串模式
5. Symbol.toStringTag
定义在Object.prototype上
Symbol.toStringTag 属性中存储了 调用Object.prototype.toString.call()返回的值。
function Person(name) {
this.name = name;
}
Person.prototype[Symbol.toStringTag] = "Person";
let me = new Person("Nicholas");
console.log(me.toString()); // "[object Person]"
console.log(Object.prototype.toString.call(me)); // "[object Person]"