ES6-Symbol

Symbol

一种新的原始数据类型,表示独一无二的值。它是JavaScript语言的第七种数据类型。

特性

  • 表示独一无二的值
let a = Symbol();
let b = Symbol();
console.log(a === b); // false
console.log(a == b); // false
  • 新的原始数据类型
var a = Symbol();
typeof a; // "symbol"
  • 不能使用new关键字
var a = new Symbol(); // 报错:Uncaught TypeError: Symbol is not a constructor
  • 可以接受一个字符串作为参数,表示对Symbol实例的描述
var a = Symbol('a'); // Symbol(a)
a.toString() // "Symbol(a)"
  • Symbol值不能与其他类型的值进行运算
var a = Symbol('a');
'hello ' + a; // 报错:Cannot convert a Symbol value to a string
\`hello ${a}\`; // 报错:Cannot convert a Symbol value to a string
  • Symbol值可以显式转为字符串
var a = Symbol('a');
String(a); // "Symbol(a)"
a.toString(); // "Symbol(a)"
  • Symbol值也可以转为布尔值。
var a = Symbol('a');
Boolean(a); // true
  • 不能转为数值
var a = Symbol('a');
Number(a); // 报错:Cannot convert a Symbol value to a number

作为对象属性名

  • 作为属性名的几种写法
var a = Symbol();

var obj = {};
obj[a] = 'hello';

var obj = {
    [a]: 'hello'
};

var obj = {};
Object.defineProperty(obj, a, {
    value: 'hello'
});
  • Symbol值作为对象属性名时,不能用点运算符,因为点运算符后面总是字符串
var a = Symbol();
var obj = {};

obj.a = 'hello';
obj[a] // undefined
obj['a'] // "hello"
  • 不能被for...in、for...of遍历,也不会被Object.keys()、Object.getOwnPropertyNames()等方法返回,可以通过Object.getOwnPropertySymbols()获取,或者通过Reflect.ownKey(属于ES7的范畴,它代理了大部分的Object功能,对它不再继续深入,有兴趣的同学可以自行查阅资料)
var obj = {
    [Symbol('a')]: 'a',
    [Symbol('b')]: 'b',
    c: 'c'
}

Object.getOwnPropertyNames(obj); // []

for (var i in obj) {
  console.log(i); // 无输出
}

Object.getOwnPropertySymbols(obj); // [Symbol(a), Symbol(b)]

Reflect.ownKeys(obj); // ["c", Symbol(a), Symbol(b)]

Symbol方法和内置Symbol值

for()

  • 使用for方法可以获取相同Symbol值的不同变量
  • 传入的字符串需要相同,可以为空
  • 必须全为for定义
var a = Symbol.for();
var b = Symbol.for();
var c = Symbol.for('c');
var d = Symbol.for('c');
var e = Symbol('e');
var f = Symbol.for('e');

a === b; // true
c === d; // true
e === f; // false

keyFor()

  • 返回一个已登记的Symbol类型值的key
  • 只有通过for方法生成的Symbol才能使用该方法返回key
var a = Symbol.for('a');
Symbol.keyFor(a); // 'a'

var a = Symbol('a'); // 未登记
Symbol.keyFor(a); // undefined

hasInstance

  • 此方法至少暂时不可用,最新版的chrome和node中均不能正常工作
  • 该方法会被instanceof运算符调用
class Demo{
    static [Symbol.hasInstance](foo){
        return true
    }
}
[] instanceof Demo // 理论上返回true,但是实际chrome中测试返回false

isConcatSpreadable

  • 对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象使用Array.prototype.concat()时,是否可以展开。
var a = ['a']; // 数组的Symbol.isConcatSpreadable属性默认值为true
['b'].concat(a, 'c'); // ['b', 'a', 'c']

var a = ['a'];
a[Symbol.isConcatSpreadable] = false;
['b'].concat(a, 'c'); // ['b', ['a'], 'c']

var a = {
    0: 'a',
    length: 1
};
['c'].concat(a, 'd'); // ['c', {0: 'a', length: 1}, 'd']
a[Symbol.isConcatSpreadable] = true;
['c'].concat(a, 'd'); // ['c', 'a', 'd']
  • 这种方法是行不通的,至少我验证了最新的chrome和最新版的node,都无法正常工作
class Demo extends Array{[Symbol.isConcatSpreadable](){return false}}
var a = new Demo('a');
['b'].concat(a); // ['b', 'a']

species

  • 蛋疼啊。。。。
  • 最新版Chrome和node中验证失败
class Demo extends Array{
    constructor(props){
        super();
        this.props = props
    }
    [Symbol.species](){
        return Array;
    }
}
var a = new Demo(1);
var b = a.map(function(item, i, arr){ // 都是一个不同的数组了,咋可能还是Demo的实例呢!
    return item * 2;
});
b instanceof Demo; // 他们说这个地方应该返回true,但实际上返回false
b instanceof Array;

match

  • 指定字符串调用match方法时的行为
class Match{
    constructor(props){
        this.props = props || 'hello'
    }
    [Symbol.match](string){
        return this.props.match(string);
    }
}

var match = new Match();
'e'.match(match); // ['e']

var match = new Match('111');
'e'.match(match); // null

replace

  • 指定字符串调用replace方法时的行为
class Replace{
    [Symbol.replace](string){
        console.log(string); // 还可以干点别的哦。。
        return string.replace(/b/g, 'a');
    }
}
var replace = new Replace();
'abaaaab'.replace(replace); // 'aaaaaaa'
  • 指定字符串调用search方法时的行为
class Search{
    [Symbol.search](string){
        string = 'aaaaab';
        return string.search(/b/g);
    }
}
var search = new Search();
'abaaaab'.search(search); // 5

split

  • 指定字符串调用split方法时的行为
class Split{
    [Symbol.split](string){
        return string.split('b', 1);
    }
}
var split = new Split();
'abaaaab'.split(split); // ['a']

iterator

  • 见Iterator.md

toPrimitive

  • 当数据被进行类型转换时,调用该数据的Symbol.toPrimitive方法
var obj = {
    [Symbol.toPrimitive](type){
        switch(type){
            case 'string': // 只能转换成string时
                return 'aaa';
                break;
            case 'number': // 只能转换成number时
                return 123;
                break;
            case 'default': // 既可以转换成string,又可以转换成number时
                return 'default';
                break;
            default:
                throw new Error()
        }
    }
}
2 * obj // 246
'a' + obj // 'adefault'
String(obj) // 'aaa'

toStringTag

  • 控制在调用toString方法时返回的字符串
class Demo{
    get [Symbol.toStringTag](){
        return 'Demo'
    }
}
var a = new Demo();
a.toString(); // "[object Demo]"

应用

消除魔术字符串

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

  • 魔术字符串的栗子
function demo(str){
    switch(str){
        case 'abc': // 魔术字符串
            console.log('hello Symbol');
            break;
    }
}
demo('abc'); // 魔术字符串
  • 消除魔术字符串的方法,把它变成一个变量。大家都懂的,就没栗子了
  • 但是,上面栗子上'abc'字符串真的有实际意义吗?是否可以这样?当然也是需要赋值给一个变量
var a = Symbol('a'); // 传字符串'a'只是为了方便查看下面的打印结果,其实可以不传
var b = Symbol('b');
function demo(v){
    switch(v){
        case a: 
            console.log(\`hello Symbol, I am ${String(a)}\`); // ES6的模板功能,下次再带大家认识。
            break;
        case b: 
            console.log(\`hello Symbol, I am ${String(b)}\`);
            break;
    }
}
demo(a);
原文地址:https://www.cnblogs.com/ddfe/p/5609733.html