初识ES6中的Symbol

Symbol

文章内容参考《ECMAScript 6 入门》。

概述

Symbol 是 ES6 引入的一种新的原始数据类型,是独一无二的值。

注意: Symbol 函数不能使用 new 命令,因为生成的 Symbol 是一个原始类型的值,不是对象。


JavaScript 的七种数据类型:

  1. Undefined
  2. Null
  3. Boolean
  4. String
  5. Number
  6. Object
  7. Symbol


判断数据类型的方法

1
2
3
let data = Symbol()

Object.prototype.toString.call(data).slice(8,-1) // "Symbol"


使用事项

  1. Symbol 是独一无二的值。
1
2
3
4
5
6
7
let idx = Symbol('pro')
let key = Symbol('pro')

idx // Symbol(pro)
key // Symbol(pro)

a === b // false
  1. Symbol 函数可以接受一个字符串作为参数(如:例 1),表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
1
2
3
4
5
6
// 无法区分两者
let s1 = Symbol()
let s2 = Symbol()

s1 // Symbol()
s2 // Symbol()
  1. 当 Symbol 的参数是一个对象,就会调用该对象的 toString 方法(执行拆箱操作),将其转为字符串,然后才生成一个 Symbol 值。
1
2
3
4
5
6
7
const obj = {
toString() {
return 'abc'
}
};
const sym = Symbol(obj)
sym // Symbol(abc)
  1. Symbol 值可以显式转为字符串和布尔值。但是不能转换成数值,也不能与其他类型的值进行运算,会报错。
  • 转字符串
1
2
3
4
5
6
7
let s = Symbol('Str Symbol')

String(s) // 'Symbol(Str Symbol)'
s.toString() // 'Symbol(Str Symbol)'

// 不能将Symbol强制转换为字符串或是数字类型
s + '' // TypeError: Cannot convert a Symbol value to a string
  • 转布尔
1
2
3
let s = Symbol('Str Symbol')

Boolean(s) // true
  • 转数值,运算
1
2
3
4
5
let s = Symbol('Str Symbol')

Number(s) // Cannot convert a Symbol value to a number

'look at this' + s // TypeError: Cannot convert a Symbol value to a string
  1. Symbol 函数不能使用 new 命令,因为生成的 Symbol 是一个原始类型的值,不是对象。
1
new Symbol() // TypeError: Symbol is not a constructor


Symbol.prototype.description

创建 Symbol 的时候,可以添加一个描述。

1
const s = Symbol('sym')

上面代码中,s 的描述就是字符串 sym。

但是,读取这个描述需要将 Symbol 显式转为字符串,即下面的写法。

1
2
3
const s = Symbol('sym')

String(s).slice(7,-1) // "sym"

上面的用法不是很方便。ES2019 提供了一个实例属性 description,直接返回 Symbol 的描述。

1
2
3
const s = Symbol('sym')

s.description // "sym"


Symbol 作为对象属性名

根据每一个 Symbol 值都是不等的特性,可以用 Symbol 作为唯一标识符,使用 Symbol 作为对象属性名可以保证不会出现同名的属性。

注意: symbol 不是字符串类型!!!

使用 symbol 作为对象属性名时,必须放在方括号 [ ]之 中,因为点运算符后面总是字符串。

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

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"


使用 Symbol 来代替常量

我们经常需要用一组常量区分不同的类型,处理不同的逻辑。其实我们并不在意这几个常量值是什么,只要是唯一的。这时可以用 Symbol 来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const apple = 'myApple'
const orange = 'myOrange'
const pear = 'myPear'

function getFruit(fruit) {
switch (fruit) {
case apple:
console.log('我是a先生')
break
case orange:
console.log('我是b女士')
break
case pear:
console.log('我是c女士')
break
default:
console.log('这是什么')
}
}

getFruit(apple) // 我是a先生

观察以上代码,我们会发现,上述定义的常量值是什么并不重要。只要保证常量值间互不相等即可。

直接使用 Symbol 赋值即可:

1
2
3
const apple = Symbol()
const orange = Symbol()
const pear = Symbol()


Symbol 属性检索

以 Symbol 作为属性名,不会出现在 for…in、for…of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。

  1. Object.getOwnPropertySymbols 方法:获取指定对象的所有 Symbol 属性名的集合数组
1
2
3
4
5
6
7
8
9
const a = Symbol('a')
const b = Symbol('b')

let obj = {
[a]: 'aaa',
[b]: 'bbb'
}

console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(a), Symbol(b) ]
  1. Reflect.ownKeys 方法:返回所有类型的键名,包括常规键名和 Symbol 键名。
1
2
3
4
5
6
7
let obj = {
[Symbol('a')]: 'aaa',
b: 'bbb',
c: 'ccc'
}

console.log(Reflect.ownKeys(obj)) // [ 'b', 'c', Symbol(a) ]st


Symbol 定义类的私有属性/方法 (重点)

以 Symbol 值作为名称的属性,不会被常规方法遍历得到。利用这个特性,可以为对象定义一些非私有的、但又希望只用于内部的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function myObject(name, age) {
let Age = Symbol()
this.name = name
this[Age] = age
this.checkAge = (num) => {
return this[Age] === num
}
}

let func = new myObject('z先生', 18)

console.log(func.checkAge(18)) // true
console.log(func.name) // z先生
console.log(func.age) // undefined
console.log(func[age]) // age is not defined


Symbol 共享体系

如果想创建一个可共享的 Symbol,可以使用 Symbol.for()方法。

Symbol.for 方法,接收一个字符串参数,然后全局环境中搜索有没有以该字符串参数作为名称的 Symbol 值:

  • 如果有,就返回这个 Symbol 值
  • 否则就全局环境中登记新建并返回一个以该字符串为名称的 Symbol 值。
1
2
3
4
5
6
7
8
9
let s1 = Symbol.for('s')
let s2 = Symbol('s')
let s3 = Symbol.for('s')

s1 === s2 // false
s1 === s3 // true

Symbol('sym') === Symbol('sym') // false
Symbol.for('sym') === Symbol.for('sym') // true

注意: Symbol.for 为 Symbol 值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。

1
2
3
4
5
iframe = document.createElement('iframe');
iframe.src = String(window.location);
document.body.appendChild(iframe);

iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo') // true

上面代码中,iframe 窗口生成的 Symbol 值,可以在主页面得到。


END

无论怎样,都别丢了快乐和努力。

转载分享,请标明出处。



喜欢可以支持一下