「前端开发」 -

JS-ES6-06

ECMAScript6 —— Symbol类型

Posted by eliochiu on November 10, 2022

概述

ES5中,所有对象的属性都是字符串类型,容易造成属性名的冲突。为了解决此问题,ES6添加了一种基本类型——Symbol

Symbol是基本类型,表示独一无二的值。他是JavaScipt语言中的第7种数据类型、第6种简单类型(其余的六种简单类型分别为Number、String、Boolean、Undefined、Null、BigInt)。至此,所有简单类型均已介绍完毕。

Symbol值通过Symbol函数生成。也就是说,对象的属性名可以有两种形式:字符串和Symbol。只要属性名属于Symbol类型,就可以保证属性名唯一,从而不会产生冲突。

1
2
let s = Symbol();
typeof s // "symbol"

注意:Symbol函数不能使用new关键字,否则会报错。这是因为生成的Symbol是一个原始类型的值,而不是一个对象(构造函数不完整),本质上,Symbol类型是一个类似字符串的数据类型。

Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了控制台显示。

1
2
3
4
5
6
7
8
var s1 = Symbol('foo');
var s2 = Symbol('bar');

s1 // Symbol(foo);
s2 // Symbol(bar);

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

如果Symbol的参数是一个对象,就会调用对象的toString()方法,将其转为字符串,然后再生成一个Symbol值:

1
2
3
4
5
6
7
8
const obj = {
    toString() {
        return "abc"
    }
};

const sym = Symbol(obj);
sym // Symbol(abc);

注意:Symbol函数的字符串仅仅是对当前的值的描述,所有Symbol值都是不相等的,因此相同描述的Symbol不等。

1
2
3
const s1 = Symbol('a');
const s2 = Symbol('a');
s1 === s2; // false

作为对象属性

由于任何Symbol值都不想等,也就意味着它可以用作对象的属性,并不会出现同名的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var mySymbol = Symbol();

// 写法1
var a = {};
a[mySymbol] = 'Hello';

// 写法2
var a = {
    [mySymbol]: 'Hello'
};

// 写法3
var a = {};
Object.defineProperty(a, mySymbol, {
    value: 'Hello'
});

// 以上写法均得到同一个结果
a[mySymbol] // 'Hello'

注意:Symbol类型的对象属性不能通过点运算符访问,只能通过中括号来访问。

1
2
3
4
5
6
var mySymbol = Symbol();
var a = {};

a.mySymbol = 'Hello';
a[mySymbol] // undefined
a['mySymbol'] // 'Hello'

上述代码使用了点运算符为对象amySymbol属性赋值,并没有成功。实际上,上述代码并未给Symbol类型的属性赋值,而是创造了一个普通的字符串属性'Symbol'

同理,在对象中,如果要使用Symbol值定义属性时,必须当在中括号里:

1
2
3
4
5
6
7
var s = Symbol();

let obj = {
    [s]: function() {
        //... 
    }
}

遍历属性名

Symbol类型的属性,不会出现在for...in, for...of循环中,也无法通过Object.keys(), Object.getOwnPropertyNames()获得。可以使用Object.getOwnPropertySymbols()方法获得。

getOwnPropertySymbols()方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值:

1
2
3
4
5
6
7
8
9
10
var obj = {};
var a = Symbol('a');
var b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

var objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols // [Symbol(a), Symbol(b)]

再看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var obj = {};

var foo = Symbol("foo");

Object.defineProperty(obj, foo, {
    value: "foobar",
    enumerable: true
});

Object.defineProperty(obj, 'bar', {
    value: "barbaz",
    enumerable: true
});

for (let i in obj) {
    console.log(i);
} 
// 'bar'

console.log(Object.getOwnPropertyNames(obj));
// [ 'bar' ]

console.log(Object.getOwnPropertySymbols(obj));
// [Symbol(foo)]