05 ES6标准入门(Symbol)

概述

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值,每一个Symbol值都是不相等的,从根本上防止属性名的冲突

它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

注意,Symbol函数前不能使用new命令,否则会报错。也就是说,由于Symbol值不是对象,所以不能添加属性

Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

let s1 = Symbol('foo');
let s2 = Symbol('bar');

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

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

let a = Symbol({});
a; // Symbol([object Object])

let b = Symbol([1,2]);
b; // Symbol(1,2)

注意,Symbol函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();

s1 === s2 // false

// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');

s1 === s2 // false

Symbol不能与其他类型的值进行运算,会报错。

Symbol可以转换为字符串和布尔值,但是不能转换为数字

const s = Symbol('s');

s.toString(); //"Symbol(s)"

!!s; // true

+s; // TypeError: Cannot convert a Symbol value to a number

作为属性名的Symbol

由于每一个Symbol都是不相等的,意味着Symbol可以用作标示符,用于对象的属性名,能够保证不会出现同名的属性

在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中

let s = Symbol('s');

let a = {
  [s]: 1
};

a[s] = 2;

Object.defineProperty(a, s, { value: 3})

注意,Symbol值作为对象属性名时,不能用点运算符,因为点运算符后面总是字符串。

Symbol类型用来定义常量时,可以保证这组常量的都是不相同的,

消除魔术字符串

魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。

function test(val) {
  switch (val) {
    case 'value1':
      {
        alert(1);
        break;
      }
    case 'value2':
      {
        alert(2);
        break
      }
  }
}
test('value1')

上面代码中,字符串value1就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护。

常用的消除魔术字符串的方法,就是把它写成一个变量。

const value = {
  value1: 'value1',
  value2: 'value2'
};

function test(val) {
  switch (val) {
    case value.value1:
      {
        alert(1);
        break;
      }
    case value.value2:
      {
        alert(2);
        break
      }
  }
}
test(value.value1)

如果仔细分析,可以发现value.value1等于哪个值并不重要,只要确保不会跟其他属性的值冲突即可。因此,这里就很适合改用Symbol 值。

const value = {
  value1: Symbol(),
  value2: Symbol()
};

function test(val) {
  switch (val) {
    case value.value1:
      {
        alert(1);
        break;
      }
    case value.value2:
      {
        alert(2);
        break
      }
  }
}
test(value.value1)

属性名遍历

Symbol作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keysObject.getOwnPropertyNamesJSON.stringify()返回

需要使用Object.getOwnPropertySymbols()进行遍历,也可以使用Reflect.ownKeys进行遍历

Symbol.for()Symbol.keyFor()

Symbol.for()Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。

Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false

Symbol.for()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。

比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个Symbol值,但是调用Symbol("cat")30次,会返回30个不同的Symbol值。

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

Symbol.keyFor方法返回一个已登记的Symbol类型值的key

Symbol.keyFor(Symbol.for('x')); // "x"

Symbol.keyFor(Symbol('x')); // undefined

模块的Singleton模式

对于Node来说,每个模块可以看成一个类,每次调用都返回同一个实例,就是模块的Singleton模式

原来的实现方法是将返回的实例放在全局(顶层)对象global

// mod.js
function A(){
  this.foo = 'hello'
}

if(!global._foo){
    global._foo = new A()
}

module.exports = global._foo;

然后加载mod.js

const mod = require('./mod.js');
console.log(mod.foo)

但是全局变量global._foo很容易被改写

为了解决这个问题可以使用Symbol.for

// mod.js
const FOO_KEY = Symbol.for('foo');

function A() {
  this.foo = 'hello'
}

if(!global[FOO_KEY]){
    global[FOO_KEY] = new A()
}

module.exports = global[FOO_KEY];

这样保证global[Symbol.for('foo')]不会被无意间覆盖,但还是可以被改写,因为n个Symbol.for('foo')都指向同一个注册值

如果使用Symbol作为属性名

// mod.js
const FOO_KEY = Symbol('foo');

function A(){
  this.foo = 'hello'
}

if(!global[FOO_KEY]){
  global[FOO_KEY] = new A()
}

module.exports = global[FOO_KEY];

n个Symbol('foo')指向n个值,那么在外部是无法引用global[Symbol('foo')]的,因为每次引用global[Symbol('foo')]都相当于创建了一个新值(在mod.js内部通过global[FOO_KEY]是指向同一个值的,但FOO_KEY是内部变量,外部访问不到)

外部将无法引用这个值,当然也就无法改写。

原理类似于:

let s = Symbol('a');
let a = {};
a[s] = 123;
a[s] = 555;
console.log(a); // {Symbol(a): 555}

let b = {};
b[Symbol('a')] = 123;
b[Symbol('a')] = 456;
console.log(b); // {Symbol(a): 123, Symbol(a): 456}

变量s作为属性名,指向同一个值,而Symbol('a')作为属性名,每次指向新值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值