Symbol([description])举个例子:
// 堆代码 duidaima.com const symbol1 = Symbol(); const symbol2 = Symbol(123); const symbol3 = Symbol('test'); console.log(symbol2); // Symbol(123) console.log(typeof symbol1); // symbol console.log(symbol2 === 123); // 这个永远输出是false console.log(Symbol('test')===Symbol('test'));// 永远输出是false可以看出通过 Symbol 创建返回的symbol值都是唯一的。
var symbol = new Symbol()上面这种是有问题的创建 symbol 值方式,会抛出 TypeError 错误。
2.5Object.getOwnPropertyNames():返回一个数组,其中包含对象的所有属性(包括不可枚举属性)的名称,但是不包括使用symbol值作为名称的属性
let symbol = Symbol('test'); let obj = {[symbol]:123}; for(const key in obj){ console.log(key); // 无打印信息 } console.log(obj[symbol]); // 123 console.log(Object.keys(obj)); // [] console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(test) ] console.log(Reflect.ownKeys(obj)); // [ Symbol(test) ]3.Object.assign 将属性从源对象复制到目标对象,会包含 Symbol 类型作为 key 的属性
const symbolKey = Symbol('key'); const source = { [symbolKey]: 'Symbol Property', regularProperty: 'Regular Property' }; Object.defineProperty(source,"w",{ value:456, enumerable:true, configurable:true, writable:true }) Object.defineProperty(source,"r",{ value:123, enumerable:false, configurable:false, writable:false }) const target = {}; Object.assign(target, source); console.log(target); // 输出 // { // regularProperty: 'Regular Property', // w: 456, // [Symbol(key)]: 'Symbol Property' // } // [Symbol(key)] 类型也会被打印,但是不可枚举属性不会打印3. Symbol 在 JSON.stringify 中的问题
let symbol = Symbol('test'); let obj = {[symbol]:123}; console.log(obj[symbol]); // 1235. Symbol.For 介绍
console.log(Symbol.for('test') === Symbol.for('test')); // true console.log(Symbol("test")===Symbol("test")); // false6. Symbol 直接传入一个函数
let a = function(){ console.log('哈哈哈'); } console.log(Symbol(a)); // Symbol(function(){ console.log('哈哈哈');}) console.log(typeof Symbol(a)); // symbolSymbol 应用场景
let symbolObjTest1 = { 0:"a", 1:"b", 2:"c", length:3, [Symbol.iterator]:function(){ let index = 0; return { next(){ // 迭代器返回的对象需要有next函数 return { value:symbolObjTest1[index++], // value为迭代器生成的值 done:index>symbolObjTest1.length // 迭代器的终止条件,done为true时终止遍历 } } } } } for(const iterator1 of symbolObjTest1){ console.log(iterator1); // 打印 a b c }demo2(Symbol.iterator属性中使用Generator)
let symbolObjTest2 = { 0:"d", 1:"e", 2:"f", length:3, [Symbol.iterator]:function*(){ // 注意Generator函数格式 let index = 0; while(index<symbolObjTest2.length){ yield symbolObjTets2[index++] } } } for(const iterator2 of symbolObjTest2){ console.log(iterator2);//打印 d e f }demo3(不影响原始对象遍历,遍历正常返回key value)
const obj = {a:1,b:2,c:3}; obj[Symbol.iterator] = function*(){ for(const key of Object.keys(this)){ yield [key,this[key]] } } for(const [key,value] of obj){ console.log(`${key}:${value}`); // 打印 }总结一下,一个对象只要有[Symbol.iterator]属性,它就拥有了可迭代的能力。
class Animal{ constructor(name,sex,isMammal){ this.name = name; this.sex = sex; this.isMammal = isMammal; } } // 堆代码 duidaima.com class Zoo{ constructor(){ this.animals = []; } addAnimals(animal){ this.animals.push(animal); } [Symbol.iterator](){ let index = 0; const animals = this.animals; return { next(){ return { value:animals[index++], done:index>animals.length } } } } } const zoo = new Zoo(); zoo.addAnimals(new Animal('dog','victory',true)); zoo.addAnimals(new Animal('pig','defeat',false)); zoo.addAnimals(new Animal('cat','defeat',false)); for (const animal of zoo) { console.log(`${animal.name};${animal.sex};${animal.isMammal}`) } // 打印 dog;victory;true pig;defeat;false cat;defeat;false这个例子写完文章后感觉不太贴切,用动物园和动物进行举例,大家练习的时候可以把它改成实体和列表,更好理解
const toStringCallFun = Object.prototype.toString.call; toStringCallFun(new Date); // [object Date] toStringCallFun(new String); // [object String] toStringCallFun(Math); // [object Math] toStringCallFun(undefined); // [object Undefined] toStringCallFun(null); // [object Null]我们也可以自定义函数,来覆盖默认的 toString() 方法。自定义的 toString() 方法可以是任何你想要的值。 举个例子:
function Student(score,age){ this.score = score; this.age = age; } let student = new Student('100',13); // 直接调用toString函数 console.log(student.toString()); // '[object Object]' // 覆盖默认的toString函数 Student.prototype.toString = function(){ return `年龄${this.age};成绩${this.score}` } console.log(student.toString()); // 年龄13;成绩100默认情况下,toString() 方法被每个 Object 对象继承,如果此方法在自定义对象中未被覆盖,toString() 返回“[object type]”,其中 type 是对象的类型。
Object.prototype.toString.call(new Map()); // "[object Map]" Object.prototype.toString.call(function* () {}); // "[object GeneratorFunction]" Object.prototype.toString.call(Promise.resolve()); // "[object Promise]" // ... and more但是在早期不是所有对象都有 toStringTag 属性,没有 toStringTag 属性的对象也会被toString() 方法识别并返回特定的类型标签。如下:
let toStringFunc = Object.prototype.toString toStringFunc.call('foo') // '[object String]' toStringFunc.call([1, 2]) // '[object Array]' toStringFunc.call(3) // '[object Number]' toStringFunc.call(true) // '[object Boolean]' toStringFunc.call(undefined) // '[object Undefined]' toStringFunc.call(null) // '[object Null]'介绍了这么多再来说我们可以 Symbol.toStringTag 做点什么? 我们自己创建的类,toString() 可就找不到 toStringTag 属性喽!只会默认返回 Object 标签
class TestClass{} Object.prototype.toString.call(new TestClass());// '[object Object]'我们给类增加一个 toStringTag 属性,自定义的类也就拥有了自定义的类型标签
class TestClass{ get [Symbol.toStringTag](){ return "TestToStringTag" } } Object.prototype.toString.call(new TestClass());// '[object TestToStringTag]'Symbol.asyncIterator 实现对象异步迭代器
// 异步迭代器demo class AsyncRequest{ constructor(request){ this._request = request; } async *[Symbol.asyncIterator](){ for (const item of this._request) { const res = await this._dealAsyncRequest(item); yield res; } } async _dealAsyncRequest(item){ // 模拟异步处理数据请求的过程 return new Promise((resolve)=>{ setTimeout(()=>{ resolve(item*100); },1000) }) } } (async function dealData(){ const dataSource = new AsyncRequest([1,2,3,4]); for await(const data of dataSource){ console.log(data) } })()使用 for await of 进行异步迭代时,每次迭代都会等待前一个异步操作完成,然后再进行下一次迭代,这样可确保按顺序处理每个异步操作的结果
export const pathMetadataKey = Symbol('path'); export function GET(path: string) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { Reflect.defineMetadata(pathMetadataKey, path, target, propertyKey); implementProcess(Method.GET, path, target, propertyKey, descriptor); } }自己实现一个 Symbol
(function(){ let root = this; // 因为symbol一个特殊的能力就是可以保证对象key的唯一性 const generateName = (function(){ let postfix = 0; return function(descStr){ postfix++; return `&&_${descStr}_&&_${postfix}`; } })() const CustomSymbol = function(desc){ // 不可以 new if(this instanceof CustomSymbol) throw new TypeError('Symbol is not a constructor') // desc 如果不是undefined会被toString let descStr =desc === undefined ? undefined : String(desc); // 需保证 symbol 值唯一性 let symbol = Object.create({ toString:function(){ return this.__Name__; // 没有直接返回Symbol字符串是和保证作为对象key的唯一性有冲突,选择了后者 obj[symbol1] obj[symbol2] // return 'Symbol('+this.__Desc__+')'; }, // 显示调用返回该值 隐式调用(会先调用对象的valueOf函数,如果没有返回基本值,就会再调用toString方法) valueOf:function(){ return this; } }); // 保证 symbol 值唯一性 Object.defineProperties(obj,{ '__Desc__':{ value:descStr, writable:false, enumerable:false, configurable:false, }, // __Name__的generateName保证作为对象key时唯一性 '__Name__':{ value:generateName(descStr), writable:false, enumerable:false, configurable:false, } }); return symbol; } let forMap = {} Object.defineProperties(customSymbol,{ // 实现 Symbol.for 'for':{ value:function(desc){ let descStr = des if(!Reflect.has(forMap,key)){ Reflect.set(forMap,key,customSymbol(descStr)) } return Reflect.get(forMap,key) }, writable:false, enumerable:false, configurable:false, }, // 实现 Symbol.keyFor 'keyFor':{ value:function(symbolValue){ for (const [key,value] of forMap.entries()) { if(value === symbolValue) return key } }, writable:false, enumerable:false, configurable:false } }) root.symbol = CustomSymbol; })()---------------------------------文章到此结束,欢迎点赞收藏-------------------------