ES6新增了一个基本数据类型:Symbol,至此ECMAScript的基本数据类型就有了6种:
字符串,数字,布尔,null,undefined,Symbol。
关于Symbol,我打算写2篇文章来提取一下比较重要的知识点,这篇是第一篇,主要讲Symbol的基本特性和使用场景。下一篇主要讲几个比较重要的Symbol属性。废话不多说,先进入第一篇的主题:
一:Symbol的分类和创建
Symbol有两种类型:

1: 非全局(非共享)类型的Symbol
2: 全局(共享)类型的Symbol

接下来就看看怎样创建以上两种不同的Symbol:

1: 创建非全局(非共享)类型的Symbol

所有的原始值,都有其字面形式,比如一个数字:24,一个字符串:'javaScript'。但是,Symbol没有。创建一个Symbol,必须使用Symbol函数:

let color = Symbol();

Symbol()函数也接受一个可选的参数,其为字符串类型,可以作为这个Symbol的描述:

let color = Symbol('color');    

但是,这里的描述并没有实际的用途,你也不能通过任何途径访问到它,它仅仅是用作更好地理解代码或者调试的时候给你一些context。

2: 创建全局(共享)类型的Symbol

在基于可能的需求场景,比如你想要在一个大的代码库中或者跨文件追踪一个Symbol变量,ES6提供了一个可以创建全局的(共享的)Symbol变量的方法,这些全局的Symbol存在于全局的Symbol注册表里。先来看一下创建一个全局的Symbol的方法:

let uid = Symbol.for('uid');
console.log(uid); // Symbol(uid)

全局的Symbol,需要使用到Symbol.for()方法,接受一个字符串类型的参数,这个参数作为这个Symbol的key,唯一标示,同时也作为其描述。为了更了解全局Symbol的特性,我们可以看下面一段代码:

let uid = Symbol.for('uid');
let uid2 = Symbol.for('uid');
console.log(uid === uid2); // true

上面一段代码,我们先创建了一个key为'uid'的全局Symbol,后面又创建了一个相同key的全局Symbol,最后证明这两个Symbol全等,因为当调用Symbol.for('uid')方法的行为表现为:

1: 先在全局的Symbol注册表中查找key为'uid'的Symbol,如果已经存在,就直接返回
2: 如果不存在,则创建一个新的Symbol,并用这个key在全局注册,随即返回新创建的Symbol

这里还有一个方法Symbol.keyFor()用来检测一个特定的Symbol是否已经在注册表中存在:

let uid = Symbol.for('uid');
let uid2 = Symbol.for('uid');
let uid3 = Symbol('uid3');
console.log(Symbol.keyFor(uid));// uid
console.log(Symbol.keyFor(uid2));// uid
console.log(Symbol.keyFor(uid3));// undefined 

变量uid和uid2的key都是'uid',所以'uid'既是uid也是uid2的key。但是uid3不是一个全局Symbol,所以调用Symbol.keyFor()得到的结果是undefined。

二:Symbol的使用场景

Symbol的使用场景只要有:
    1: 所有使用可计算属性名的地方
    2: Object.defineProperty()
    3: Object.defineProperties()
下面来看一个综合了以上三种使用场景的代码示例:
    let firstName = Symbol('first name');
let person = {
    [firstName]: 'mike' //用在对象的可计算属性
};

// 用在Object.defineProperty()
Object.defineProperty(person, firstName, {writable: false});

//用在Object.defineProperties()
let lastName = Symbol('last name');

Object.defineProperties(person, {
    [lastName]: {
        value: 'deep',
        writable: true
    }
});

三:Symbol的强制类型转换
Symbol是比较特殊的原始类型,因为其他类型没有与其逻辑等价的值,所以在某些可能会发生强制类型转换的场景,会因为二出错,需要特别注意一下。这里先看一下2个会报错的场景:

1: 将Symbol与字符串相加
2: 将Symbol进行数学运算
1: 将Symbol与字符串相加
let firstName = Symbol('first name');
let desc = firstName + '!'; //Uncaught TypeError: Cannot convert a Symbol value to a string
2: 将Symbol进行数学运算
let firstName = Symbol('first name');
let desc = firstName + 1; //Uncaught TypeError: Cannot convert a Symbol value to a number

以上两例代码都是因为Symbol无法直接强制转换为字符串类型和数字类型,从而报错。但是,在一下三种场景中,却可以把Symbol转换为字符串类型:

1: 调用console.log(symbol)时,会自动转为字符串
2: Symbol主动调用toString()方法
3: 直接调用String(symbol)方法

看一下示例:

let firstName = Symbol('first name');
let middleName = Symbol('middle name');
let lastName = Symbol('last name');

let firstNameDesc = firstName.toString();
let middleNameDesc = String(middleName);
console.log(lastName); // Symbol(last name)

使用console.log()的时候,其实是因为会默认调用toString()方法,把Symbol转换为String类型。
四:Symbol属性检索
在ES6之前,我们已经有2个检索对象属性的方法:

1: Object.keys() 返回所有可枚举的属性名
2: Object.getOwnPropertyNames() 不考虑可枚举性一律返回

ES6新增了Symbol类型,且我们现在知道了Symbol也可以被用作对象的属性名。但是,前来提到的2个方法都不支持Symbol属性,为了保持原有的功能,ES6新增了一个方法来检索Symbol类型的属性:Object.getOwnPropertySymbols()
接下来看一下式例:

let firstName = Symbol('first name');
let person = {
    [firstName]: 'mike',
    'lastName': 'deep'
};
console.log(Object.keys(person));//["lastName"]
console.log(Object.getOwnPropertyNames(person));//["lastName"]
console.log(Object.getOwnPropertySymbols(person));//[Symbol(first name)]

以上,就是关于Symbol的基本使用方法。


nanaistaken
586 声望43 粉丝