Symbol 类型 - JavaScript 数据类型
Symbol 类型
在使用别人提供的对象时,在拿到这个对象,然后想往里面添加新的属性名,为了防止和别人的属性名重复,这样是会发生冲突的,对象的属性名必须要求是独一无二的,因此,ES6 中加入了 Symbol 数据类型,确保对象的属性名独一无二。Symbol 实际上是引入的一种原始数据类型。
// ES6的symbol let sym = Symbol("addr"); console.log(sym); console.log(typeof sym);
Symbol
函数不是一个构造函数,前面不能用new
操作符,所以Symbol
类型的值也不是对象,不能添加任何属性,只是类似于字符型的数据类型,强加new
操作符会报错!
Symbol 值是唯一的,这是 Symbol 变量最大的特点。通过同样的方式创建的两个变量,即使参数也一样,这两个 Symbol 变量也是不相等的。
let x = Symbol(); let y = Symbol(); console.info(x === x); // true console.info(x === y); // false
symbols 重要的用途,就是用作对象的 key。
const obj = {}; const sym = Symbol(); obj[sym] = 'foo'; obj.bar = 'bar'; console.log(obj); // { bar: 'bar' } console.log(sym in obj); // true console.log(obj[sym]); // foo console.log(Object.keys(obj)); // ['bar']
我们注意到使用Object.keys()
并没有返回 symbols,这是为了向后兼容性的考虑。老代码不兼容 symbols,因此古老的Object.keys()
不应该返回 symbols。
Symbol函数的参数
Symbol 函数还可以接受一个字符参数,来对产生的 Symbol 值进行描述,方便我们区分不同的 Symbol值
let symName = Symbol('test'); let symAddr = Symbol('test'); console.log('symName',symName); console.log('symAddr',symAddr); console.log('symName == symAddr?',symName == symAddr); let sAddr = Symbol('symAddr'); console.log('sAddr == symAddr?',sAddr == symAddr);
如果创建 Symbol 值时传入的是对象,那么会先调用对象的toString
方法得到一个字符串,再将结果字符串作为参数生成一个 Symbol 值。所以,Symbol 函数的参数只能是字符串。
const obj = { a: "a", b: "b", toString() { return "xx"; }, }; console.info(Symbol(obj)); // Symbol(xx)
如果创建 Symbol 值时传入的是对象,没有定义 toString 方法:
const obj = { a: "a", b: "b", }; console.info(Symbol(obj)); // Symbol([object Object])
Symbol 值不可以进行运算
Symbol 是一种数据类型,但它与 Number 以及 String 类型不同,不能参与运算,Symbol 值可以转化为字符串或布尔值,但是不能转为数值。
let sym1 = Symbol('sym1'); let sym2 = Symbol('sym2'); //转为字符串 console.log(String(sym1)); //转为布尔值 console.log(Boolean(sym2)); // 直接进行运算 console.log('sym1+sym2=?',sym1+sym2);
Symbol 作为属性
Symbol 就是用于属性去创造的,下面是几种 Symbol 作为属性的写法
//写法一: let obj = {}; //创建一个对象 let sym = Symbol(); obj[sym] = '湖南省长沙市'; //取属性值 console.log(obj[sym]);
// 写法二 let sym = Symbol(); let obj = { [sym]:'安徽省芜湖市' }; console.log(obj[sym]);
//写法三 let obj = {} let sym = Symbol() Object.defineProperty(obj,sym,{value:'小陈同学'}); console.log(obj[sym]);
Symbol 值作为属性名时,获取相应的属性值不能用作点运算符,如果点运算符来给对象的属性赋 Symbol 类型的值,实际上属性名会变成字符串,而不是 Symbol 值,在对象内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号内,否则就是一个字符串。
getOwnPropertySymbols()
通常遍历对象,会有for...in
, for...of
,Object.keys()
,Object.getOwnPropertyNames()
这些方法,但是,这些方法都不能获取到对象的 Symbol 值的属性名。那是不是在外部就没有办法获取 Symbol 值的属性名了呢?办法当然还是有的。
Object.getOwnPropertySymbols()
就可以返回对象中的所有类型为 Symbol 值的属性名,得到一个元素类型为 symbol 的数组。除此之外,还有一个方法Reflect.ownKeys()
可以返回包含 Symbol 属性名的所有属性名。
const obj = { a: 1, [Symbol()]: 2, }; console.info("obj", Object.getOwnPropertyNames(obj)); // [ 'a' ] console.info("obj", Object.getOwnPropertySymbols(obj)); // [ Symbol() ] console.info("obj", Reflect.ownKeys(obj)); // [ 'a', Symbol() ]
var obj={ [Symbol('name')]:'like', [Symbol('age')]:'18', [Symbol()]:'play dou dou', sex:'male' } obj.sex // male //获取不到 obj[Symbol('name')] //undefined obj[Symbol('age')] //undefined obj[Symbol()] //undefined //for 检测不到 for(var i in obj){ console.log(obj[i]) } // 输出结果只有:male
let name = Symbol('name'); let age = Symbol('age'); let hobby = Symbol('hobby'); var obj={ [name]:'like', [age]:'18', [hobby]:'play dou dou', sex:'male' } obj[name] //like obj[age] //18 //仍然遍历不到 for(var i in obj){ console.log(obj[i]) } // 输出结果只有:male //Object.getOwnPropertySymbols(obj) 可以遍历到 let symbols = Object.getOwnPropertySymbols(obj) for(var i in symbols){ console.log(obj[symbols[i]]) } /*遍历结果: like 18 play dou dou */
Symbol.for()、Symbol.keyFor()
Symbol 类型的变量除了自身,不会等于其他任何变量,但是依然存在特例。区别于通过Symbol()
函数创建 Symbol 变量,我们还可以通过Symbol.for()
方法来创建 Symbol 变量,该方法可以传入参数,而且在传入参数相同的时候,创建的 Symbol 也是相等的。
const a = Symbol.for("aa"); const b = Symbol.for("aa"); console.info("a===b", a === b); // a===b true console.info("type", typeof a); // type symbol
Symbol 类型的 a 和 b ,通过===
运算可以得到true
。这是为什么呢?
因为Symbol.for()
其实是带有类似重用机制的,具体的说,就是通过Symbol.for()
创建变量时,传入的参数(假设为 x)会作为 Symbol 变量的 key ,然后到全局中搜索,是否已经有相同 key 的 Symbol 变量,如果存在,则直接返回这个 Symbol 变量。如果没有,才会创建一个 key 为传入参数 x 的 Symbol 变量,并将这个变量写到全局,供下次创建时被搜索。
通过Symbol.for()
创建的 Symbol 变量,传入的参数是否相等决定得到的 Symbol 变量是否相等。
分析上面的代码,通过Symbol.for("aa")
创建变量并赋值给 a 的时候,会在全局检索是否有 key 为'aa'的 Symbol 变量,这里显然是没有的,所有得到一个全新的 Symbol 变量,其带有的 key 是'aa',并且变量被写到全局。然后执行const b = Symbol.for("aa");
时,Symbol.for('aa')会找到之前的 key 为'aa'的 Symbol 变量,并赋值给 b ,所以,a 和 b 实际上是同一个 Symbol 变量。此时 Symbol.for("aa")不管执行多少次,得到的都是同一个 Symbol 变量。
既然通过Symbol.for()
创建的 Symbol 变量的 key 这么重要,那我们怎么获取到这个 key 呢,那就要Symbol.keyFor()
方法了,该函数会返回一个已经写到全局的 Symbol 变量的 key 值。这样获取到 Symbol 变量的 key,就可以创建一个和原 Symbol 变量相等的变量了。
内置的 Symbol 值
Symbol.hasInstance
:对象的 Symbol.hasInstance 属性指向一个内部方法,当其他对象使用 instance 运算符,判断是否为该对象的实例时,会调用这个方法。Symbol.isConcatSpreadable
:布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。Symbol.species
:对象的 Symbol.species 属性,指向一个构造函数。创建衍生对象时,会使用该属性定义 Symbol.species 属性要采用 get 取值器。Symbol.match
:指向一个函数。当执行 str.match(myObject)时,如果该属性存在,会调用他,返回该方法的返回值。Symbol.replace
:指向一个方法,当该对象被 String.prototype.replace 方法调用时,会返回该方法的返回值。Symbol.search
:指向一个方法,当该对象被 String.prototype.search 方法调用时,会返回该方法的返回值。Symbol.split
:指向一个方法,当该对象被 String.prototype.split 方法调用时,会返回该方法的返回值。Symbol.iterator
:对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法。Symbol.toPrimitive
:指向一个方法,当该对象被转化为原始类型的值时,会返回该方法的返回值。被调用时,会接受一个字符串参数,表示当前运算的模式:Number,String,Default。Symbol.toStringTag
:指向一个方法,当该对象被 String.prototype.toString 方法调用时,可以用来定制[object xxx]中 object 后面的那个字符串。Symbol.unscopables
:指向一个对象,该对象指定了使用 with 关键字时,那些属性会被 with 环境排除(with 语句的作用是将代码的作用域设置到一个特定的作用域中)。
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!