上节我们介绍了symbol 基本用法,这节我们来实现它的每一个特性。
特性
- Symbol 值通过 Symbol 函数生成,使用 typeof,结果为 "symbol"
- Symbol 函数前不能使用 new 命令,否则会报错
- instanceof 的结果为 false
- Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述
- 如果 Symbol 的参数是一个对象,就会调用该对象的 toString 方法,将其转为字符串
- Symbol 函数的参数只是表示对当前 Symbol 值的描述,相同参数的 Symbol 函数的返回值是不相等的
- Symbol 值不能与其他类型的值进行运算,会报错。
- Symbol 值可以显式转为字符串。
- Symbol 值可以作为标识符,用于对象的属性名,可以保证不会出现同名的属性。
- Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名。
- 如果我们希望使用同一个 Symbol 值,可以使用 Symbol.for。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
- Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key。
这么多特性大家有没有想过如何用原生js 怎么去实现这个symbol 函数。
下面我们去一个一个特性去实现。
特性一实现:
typeof 常常判断基本数据类型(包括symbol),首先我们必须知道js typeof 的原理:
js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。
- 000:对象
- 010:浮点数
- 100:字符串
- 110:布尔
- 1:整数
对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的。
null:所有机器码均为0
undefined:用 −2^30 整数来表示。
typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待。
typeof null
"object"
symbol : 低三位是怎么样的呢?有兴趣的可以告诉我。
这个功能我不知道怎么去实现,大家有兴趣可以探讨一下,我估计底层机器码是判断低三位是不是那个特定值来实现。
特性二实现:
实现很简单,一句话,估计大家都想到了。
// Symbol 函数前不能使用 new 命令
if (this instanceof Symbol) {
throw new TypeError('Symbol is not a constructor');
}
特性三实现:
在特性二的时候用到了 instanceof,我们来探究这个。
用途: instanceof来判断一个实例是否属于某种类型。
原理分析:instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可,返回状态分为三种:
- true
- false
- TypeError
知道instanceof 原理,那我们用一个函数来表示,这里涉及到js 原型继承知识点,需要小伙伴们自己去查看相应的知识点:
function myInstanceOf (left,right){
let rightProto = right.prototype;
let leftVaule = left.__proto__;
if (left === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
}
特性四实现:
理解 😄
特性五实现:
function Symbol(description) {
// 对象模拟返回symbol
var descString = description === undefined ? undefined : String(description)
var symbol = Object.create(null)
Object.defineProperties(symbol, {
'__Description__': {
value: descString,
writable: false,
enumerable: false,
configurable: false
}
});
return symbol;
}
特性六实现:
参考特训五的实现,因为每次创建都是通过 Object.create(null) 创建的,所以每个symbol() 不相等,小伙伴不要理解错了,底层我估计不是用Object.create(null) 创建的,是其他方法。
特性七实现:
var symbol = Object.create({
toString: function() {
return this.__Name__;
},
valueOf: function() {
return this;
}
})
特性十实现:
Object.defineProperties(symbol, {
'__Description__': {
value: descString,
writable: false,
enumerable: false, // 设置为false 就可以不可枚举
configurable: false
}
});
特性十一实现:
Object.defineProperties(SymbolPolyfill, {
'for': {
value: function(description) {
var descString = description === undefined ? undefined : String(description)
return forMap[descString] ? forMap[descString] : forMap[descString] = SymbolPolyfill(descString);
},
writable: true,
enumerable: false,
configurable: true
},
});
特性十二实现:
Object.defineProperties(SymbolPolyfill, {
'keyFor': {
value: function(symbol) {
for (var key in forMap) {
if (forMap[key] === symbol) return key;
}
},
writable: true,
enumerable: false,
configurable: true
}
});
感谢你读到这里:
给大家一个完整的symbol函数的实现:
(function() {
var root = this;
var generateName = (function(){
var postfix = 0;
return function(descString){
postfix++;
return '@@' + descString + '_' + postfix
}
})()
var SymbolPolyfill = function Symbol(description) {
if (this instanceof SymbolPolyfill) throw new TypeError('Symbol is not a constructor');
var descString = description === undefined ? undefined : String(description)
var symbol = Object.create({
toString: function() {
return this.__Name__;
},
valueOf: function() {
return this;
}
})
Object.defineProperties(symbol, {
'__Description__': {
value: descString,
writable: false,
enumerable: false,
configurable: false
},
'__Name__': {
value: generateName(descString),
writable: false,
enumerable: false,
configurable: false
}
});
return symbol;
}
var forMap = {};
Object.defineProperties(SymbolPolyfill, {
'for': {
value: function(description) {
var descString = description === undefined ? undefined : String(description)
return forMap[descString] ? forMap[descString] : forMap[descString] = SymbolPolyfill(descString);
},
writable: true,
enumerable: false,
configurable: true
},
'keyFor': {
value: function(symbol) {
for (var key in forMap) {
if (forMap[key] === symbol) return key;
}
},
writable: true,
enumerable: false,
configurable: true
}
});
root.SymbolPolyfill = SymbolPolyfill;
})()
网友评论