ES6 之 Symbol
Posted isnan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 之 Symbol相关的知识,希望对你有一定的参考价值。
1. 基本用法
Symbol 是ES6引入的一种新的原始数据类型,表示独一无二的值。
前六种基础数据类型是 undefined null Boolean String Number Object
Symbol 值通过Symbol函数生成 可以接受一个字符串作为参数,主要为了区分不同Symbol值
参数若是一个对象,则会调用该对象的 toString 方法转成字符串 传递给Symbol
*** 参数只做为一个描述存在,同样的参数生成的Symbol也不相等
***** 不能用 new 创建
围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。然而,现有的原始包装器对象,如 new Boolean、new String以及new Number,因为遗留原因仍可被创建。
1 let s1 = Symbol(‘foo‘); 2 let s2 = Symbol(‘bar‘); 3 let s3 = Symbol(‘bar‘); 4 console.log(s1); // Symbol(foo) 5 console.log(typeof s1); // symbol 6 console.log(String(s2)); // "Symbol(bar)" 不能用new String 7 console.log(Boolean(s2)); // true 8 console.log(Number(s2)); // 报错 TypeError: Cannot convert a Symbol value to a number 9 console.log(s2 === s3); //false 10 console.log(s3.description); //"bar" (ES2019)
1 let obj1 = { 2 a: 12 3 }; 4 let obj2 = { 5 a: 12, 6 toString() {} 7 }; 8 let obj3 = { 9 a: 12, 10 toString() { 11 return this.a; 12 } 13 }; 14 let s4 = Symbol(obj1); 15 let s5 = Symbol(obj2); 16 let s6 = Symbol(obj3); 17 console.log(s4); //Symbol([object Object]) 18 console.log(s5); //Symbol(undefined) 19 console.log(s6); //Symbol(12)
2. 使用场景
基本应用场景有两种,一是作为key ,一是作为value
***作为key时 只能用 [] 定义 , 不能用 .
1 let s7 = Symbol(); 2 let s8 = Symbol(); 3 let s9 = Symbol(); 4 let jo = { 5 name: ‘nan‘, 6 age: 18, 7 [s7]: ‘Hi‘ 8 }; 9 jo[s8] = ‘hook‘; 10 Object.defineProperty(jo, s9, { 11 value: ‘jay‘, 12 enumerable: true 13 }) 14 console.log(jo); //{ name: ‘nan‘, age: 18, [Symbol()]: ‘Hi‘, [Symbol()]: ‘hook‘, [Symbol()]: ‘jay‘ } 15 console.log(jo[s8]); //hook 16 // 是无法通过正常的遍历方法取到Symbol 属性名的 17 // for...in / for...of / Object.keys() / Object.getOwnPropertyNames() / JSON.stringify() 18 // 可以通过 Object.getOwnPropertySymbols() / Reflect.ownKeys() 19 // 可以利用这个特点来定义一些不暴露出去的属性或方法 20 console.log(Object.keys(jo)); //[ ‘name‘, ‘age‘ ] 21 console.log(Object.getOwnPropertyNames(jo)); //[ ‘name‘, ‘age‘ ] 22 console.log(Object.getOwnPropertySymbols(jo)); //[ Symbol(), Symbol(), Symbol() ] 23 console.log(Reflect.ownKeys(jo)); //[ ‘name‘, ‘age‘, Symbol(), Symbol(), Symbol() ]
***作为value , 可以定义一组常量 , 并且保证这些常量的值不想等
当只想定义一组常量 , 并不关心值的时候 ,就可以使用Symbol来定义
1 const PENDING = Symbol(); 2 const SUCCESS = Symbol(); 3 const FAIL = Symbol(); 4 class Promise { 5 constructor (executor) { 6 this.state = PENDING; 7 let resolve = value => { 8 if (this.state === PENDING) { 9 this.state = SUCCESS; 10 // ... 11 } 12 } 13 let reject = reason => { 14 if (this.state === PENDING) { 15 this.state = FAIL; 16 // ... 17 } 18 } 19 executor(resolve, reject); 20 } 21 }
3. Symbol.for() & Symbol.keyFor()
Symbol.for() 接收一个字符串,然后搜索有没有以该参数作为名称的 Symbol 值, 有则直接返回,没有创建一个新的Symbol 且注册到全局 (可以应用到iframe中)
Symbol.keyFor 返回一个Symbol.for登记过值的 key
1 let s10 = Symbol.for(‘foo‘); 2 let s11 = Symbol(‘foo‘); 3 console.log(Symbol.for(‘foo‘) === s10); //true 4 console.log(s11 === s10); //false 5 console.log(Symbol.keyFor(s10)); //"foo" 6 console.log(Symbol.keyFor(s11)); //undefined
1). Symbol.hasInstance 用于判断某对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。
1 class Counts { 2 static [Symbol.hasInstance](instance) { 3 return !(instance % 2); //是否被 2 整除 4 } 5 } 6 console.log(1 instanceof Counts); //false 7 console.log(4 instanceof Counts); // true
2). Symbol.isConcatSpreadable 符号用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。
1 let arr1 = [1, 2, 3]; 2 let arr2 = [‘a‘, ‘b‘]; 3 console.log(arr1.concat(arr2)); //[ 1, 2, 3, ‘a‘, ‘b‘ ] 4 arr2[Symbol.isConcatSpreadable] = false; 5 console.log(arr1.concat(arr2)); //[ 1, 2, 3, [‘a‘, ‘b‘] ]
3). Symbol.species 是个函数值属性,其被构造函数用以创建派生对象。
***你可能想在扩展数组类 MyArray 上返回 Array 对象。 例如,当使用例如 map() 这样的方法返回默认的构造函数时,你希望这些方法能够返回父级的 Array 对象,以取代 MyArray 对象。Symbol.species 允许你这么做:
1 class MyArray extends Array { 2 // 覆盖 species 到父级的 Array 构造函数上 3 static get [Symbol.species]() { return Array; } 4 } 5 let a = new MyArray(1,2,3); 6 let mapped = a.map(x => x * x); 7 console.log(mapped instanceof MyArray); // false 8 console.log(mapped instanceof Array); // true
4). Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。
1 let myIterable = {} 2 myIterable[Symbol.iterator] = function* () { 3 let i = 0; 4 while (i < 5) { 5 yield i; 6 i++; 7 } 8 }; 9 console.log([...myIterable]); //[ 0, 1, 2, 3, 4 ] 10 function getOdd () { 11 let arr = []; 12 for (let i of myIterable) { 13 !(i%2) && arr.push(i); 14 } 15 return arr; 16 } 17 console.log(getOdd()); //[ 0, 2, 4 ]
5). Symbol.asyncIterator 符号指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性,它就是异步可迭代对象,可用于for await...of循环。
1 let asyncIterable = { 2 [Symbol.asyncIterator]() { 3 return { 4 i: 0, 5 next() { 6 if (this.i < 3) { 7 return Promise.resolve({ value: this.i++, done: false }); 8 } 9 10 return Promise.resolve({ done: true }); 11 } 12 }; 13 } 14 }; 15 16 (async function() { 17 for await (num of asyncIterable) { 18 console.log(num); // 0 1 2 19 } 20 })();
***** 其他属性和方法可以参考 MDN 文档
***** 参考文献
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
以上是关于ES6 之 Symbol的主要内容,如果未能解决你的问题,请参考以下文章