JavaScript之Symbol

2021-06-28  本文已影响0人  又菜又爱分享的小肖

前面说过,ES5中的对象属性名都是字符串,容易造成属性之间的冲突。比如我们在使用一个别人提供的对象,但是我想给这个对象添加自己想要的方法,这个方法的名字可能与现有的方法名字产生冲突,从而会覆盖原有的方法&属性值。如果有一种方式,可以保证每个属性都是独一无二的就好了,这样就能解决属性冲突的问题。这也是ES6推出类型Symbol的原因。

JavaScript在ES5有5种基本数据类型,分别是:Null,Undefined,String,Boolean,Number,ES6新增了一个基本数据类型Symbol,表示独一无二的值。

        let s = Symbol();
        console.log(typeof s); // "Symbol";

Symbol函数前不能使用new命令,否则会报错。因为生成Symbol是一个基本数据类型,不是对象。也可以这样说,因为不是对象,所以不能给该值添加属性。

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

        const obj = {
            toString() {
                return "1"
            }
        }
        const sym = Symbol(obj);
        console.log(sym); // Symbol(1)

上面说过Symbol类型是一个独一无二的值。那就说明Symbol类型不等于自己本身。
无论是没有参数还是加了参数,都不等于自己相同的值。

console.log(Symbol() === Symbol()); //false

Symbol值不能与其他类型的值进行运算,否则会报错。
Symbol值可以转为布尔值,但是不能转为数值。

写法

        var mysymbol = Symbol();
        var a = new Object();
        a.[mysymbol] = '1';

        var mysymbol = Symbol();
        var a = {
            [mysymbol]: '2' 
        }

        var mysymbol = Symbol();
        var a = {};
        Object.defineProperty(a, mysymbol, {
            value: '3'
        })

不能使用点运算符,因为点运算符后面总是字符串,所以不会读取mysymbol作为标识名所指代的值,导致a的属性名实际上是一个字符串,而不是一个symbol值。

消除魔法字符串

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

        function getArea(shape, options){
            var area = 0;
            switch (shape) {
                case 'Triangle': //魔术字符串
                area = .5 * options.width * options.height;
                break;  
            }
            return area;
        }
       console.log(getArea('Triangle', {width: 100, height: 100})) ;

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

        var shapeType = {
            shape: 'Triangle'
        }
        function getArea(shape, options){
            var area = 0;
            switch (shape) {
                case shapeType.shape ://魔术字符串
                area = .5 * options.width * options.height;
                break;  
            }
            return area;
        }
       console.log(getArea(shapeType.shape, {width: 100, height: 100})) ;

如果仔细分析,可以发现,可以发现shapeType.shape等于那个值并不重要,只要确保不会和其他shapeType的属性冲突就好了。

        var shapeType = {
            shape: Symbol()
        }
        function getArea(shape, options){
            var area = 0;
            switch (shape) {
                case shapeType.shape ://魔术字符串
                area = .5 * options.width * options.height;
                break;  
            }
            return area;
        }
       console.log(getArea(shapeType.shape, {width: 100, height: 100})) ;

属性名的遍历

Symbol 作为属性名,该属性名不会出现在for...in,for...of 循环中。但它也不是私有属性,有一个Objeck.getOwnPropertySymbols 方法可以获取指定对象的所有Symbol属性名。
Objeck.getOwnPropertySymbol返回一个数组,成员是当前对象的所有用作属性名的Symbol值。

        var obj = {}, a = Symbol('a'), b = Symbol('b');
        obj[a] = 'hellow';
        obj[b] = 'word';
        obj['c'] = '111';
        var arr = Object.getOwnPropertySymbols(obj);
        console.log(arr); // [Symbol(a), Symbol(b)]

将Objeck.getOwnPropertySymbols() 方法与for...in 循环,Objeck.getOwnPropertyNames方法进行了对比。

        var obj = {}, foo = Symbol('foo');
        Object.defineProperty(obj, foo , {
            value: 'foobar'
        });

        for (let i in obj) {
            console.log(i); //没有输出
        }

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

还有一个api——Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol。

        var obj = {
            [Symbol('name')]: '肖',
            age: 21
        }
        var num = Reflect.ownKeys(obj);
        console.log(num); // ["age", Symbol(name)]

重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接收一个字符串为参数,然后搜索有没有以改参数命名的Symbol值,如果有,就返回该值,没有就新建并返回一个以该字符串为名称的Symbol值。

        var s = Symbol.for('foo');
        var s2 = Symbol.for('foo');
        console.log(s === s2); //true

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

        var s = Symbol.for('foo');
        Symbol.keyFor(s); // "foo"

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

上一篇下一篇

猜你喜欢

热点阅读