Javascript(笔记35) - ES6特性 - Symbol数据类型


Symbol

ES6引入了一种新的原始数据类型 Symbol ,表示独一无二的值。它是 Javascript 语言的第七种数据类型: 

特点:

Symbol 的值是唯一的,用来解决命名冲突的问题;

Symbol 值不能与其他数据进行运算;

Symbol 定义的对象属性不能使用 for ... in 循环遍历;

可以使用 Reflect.ownKeys 来获取对象的所有键名;


创建Symbol

let s1 = Symbol();
let s2 = Symbol();
console.log(s1,s2,typeof(s1)); // Symbol() Symbol() 'symbol'
console.log(s1 === s2); // false
console.log(s1 == s2); // false

Symbol 的唯一性是内部实现的,表面上看都是  Symbol() ,长的一样,但不相等; 

也可以带参数:仅做为说明;

let s1 = Symbol("Jack");        // 简单说明这个 Symbol 是干嘛的;
let s2 = Symbol("Jack");
console.log(s1 === s2); // false

即使有参数说明,也是不相等的,就像都是叫“小明”的人,是两个人;

通过 Symbol.for() 对象方式创建的,就可以相等了;

let s1 = Symbol.for("Jack");
let s2 = Symbol.for("Jack");
console.log(s1,s2,typeof(s1)); // Symbol(Jack) Symbol(Jack) 'symbol'
console.log(s1 === s2); // true

这么些数据类型怎么记呢: USONB: you are so niubility.

U:undefined

S:string、symbol

O:object

N:number、null

B:boolean

特性1:不能运算

let s = Symbol();
let res = s + 100; // 报错
let res = s > 100; // 报错:Cannot convert a Symbol value to a number
let res = s + "100"; // 报错:Cannot convert a Symbol value to a string

Javascript(笔记35) - ES6特性 - Symbol数据类型_javascript

特性2:不能 new

这是个新规定,以前的包装类不受影响;

let sym = new Symbol();   // 报错

Javascript(笔记35) - ES6特性 - Symbol数据类型_数据类型_02

以前的还是可以用:​​new Boolean​​new String​​ 以及 ​new Number​


特性3:不能被 for...in 枚举

let obj = {};
obj[Symbol('a')] = 'one';
obj[Symbol.for('b')] = 'two';
obj['c'] = 'three';
obj.d = 'four';

for (let k in obj) {
console.log(k); // c d
}
console.log(obj[Object.getOwnPropertySymbols(obj)[0]]); // one

可以通过 Object.getOwnPropertySymbols(obj) 来获得 Symbol 属性;


特性4:JSON.stringify() 会被忽略

还以上面 obj 为例:

let obj = {};
obj[Symbol('one')] = 'one';
obj[Symbol.for('two')] = 'two';
obj['c'] = 'three';
obj.d = 'four';

console.log(JSON.stringify(obj)); // {"c":"three","d":"four"}


给对象添加方法和属性


1 在外部声明实例

同名的方法 ,两种方法,调用的时候用中括号 [] 。

// 原对象,有两个方法
let game = {
name:"wow",
up(){
console.log("穿上装备");
},
down(){
console.log("换下装备");
}
};

// 第一种办法:声明一个 Symbol()
let up = Symbol("up:grade");
// 为 game 对象创建方法
game[up] = function(){
console.log("打怪升级");
}
//调用方法
game[up](); // 打怪升级

// 第二种办法:声明一个对象,里面放俩属性
let methods = {
up:Symbol("up"),
down:Symbol("down")
}
// 再把对象的属性作为 game 的方法
game[methods.up] = function(){
console.log("穿上神秘装备");
}
game[methods.down] = function(){
console.log("换下神秘装备");
}
// 调用这些方法
game[methods.up](); // 穿上神秘装备
game[methods.down](); // 换下神秘装备

Javascript(笔记35) - ES6特性 - Symbol数据类型_数据类型_03

原对象 game 里已有了一个 up 方法;新建了一个 Symbol 实例,把这个实例作为 game 的方法名,再建一个 up 方法;声明一个对象,把 up (Symbol) 再作为 game 的方法名,再建一个 up 方法;

调用这些方法:

// obj[方法名]()

game[up](); // 打怪升级
game[methods.up](); // 穿上神秘装备

2 在内部隐去实例

也可以隐去实例化对象,直接使用 [Symbol()] 作为属性和方法名,效果一样;

let youxi = {
name : "狼人杀",
[Symbol('say')]:function(){
console.log('我可以发言');
},
[Symbol('zibao')](){
console.log('我可以自爆');
}
}

Javascript(笔记35) - ES6特性 - Symbol数据类型_Symbol_04

调用这些方法:

// Reflect.ownKeys(obj)
youxi[Reflect.ownKeys(youxi)[1]](); // 我可以发言
youxi[Reflect.ownKeys(youxi)[2]](); // 我可以自爆

// Object.getOwnPropertySymbols(obj);
youxi[Object.getOwnPropertySymbols(youxi)[0]](); // 我可以发言
youxi[Object.getOwnPropertySymbols(youxi)[1]](); // 我可以自爆

注意: 这两调用方法都是返回数组,但调用的下标却不一样;

​Reflect.ownKeys(obj)​​  反射方法,返回所有自身属性数组,包括 Symbol 属性;

​Object.getOwnPropertyNames(youxi)​​   返回所有属性,但不包括 Symbol 属性的数组;

​Object.getOwnPropertySymbols(obj)​​    返回一个给定对象自身的所有 Symbol 属性的数组;

所以:Reflect.ownKeys(youxi) 相当于:

Object.getOwnPropertyNames(youxi).concat(Object.getOwnPropertySymbols(youxi))

时髦写法:

[...Object.getOwnPropertyNames(youxi),...Object.getOwnPropertySymbols(youxi)]

看下控制台:

Javascript(笔记35) - ES6特性 - Symbol数据类型_javascript_05


Symbol内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法;

Symbol.hasInstance

用来判断某个对象是否为某构造器的实例。因此你可以用它自定义 instanceof 操作符在某个类上的行为。

class Person {
static [Symbol.hasInstance](param){
console.log(param);
console.log('我被用来检测类型了');
return true;
}
}
let o ={};
console.log(o instanceof Person); // true

Javascript(笔记35) - ES6特性 - Symbol数据类型_javascript_06

自定义返回值了,想要返回 fasle ,也可以自己修改;


Symbol.isConcatSpreadable

配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。

const arr1 = [1,2,3];
const arr2 = [4,5,6];
const arr = arr1.concat(arr2);
console.log(arr); // [1, 2, 3, 4, 5, 6]

这是常规操作数组合并的方式;

改一下:

const arr1 = [1,2,3];
const arr2 = [4,5,6];
arr2[Symbol.isConcatSpreadable] = false;
const arr = arr1.concat(arr2);
console.log(arr); // [1, 2, 3, Array(3)]


其他属性也是控制当前对象在特定场景下的表现,如:

Symbol.split

Symbol.Primitive

Symbol.toStringTag

Symbol.species

这些Symbol的属性都和 Symbol 连在一起用,用来作为某对象的属性,在某些时候扩展功能;


Symbol.iterator

对象进行 for ... of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器;

迭代器的剖分,放在下篇文章;