JavaScript基础---symbol

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突

一、Symbol()

Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法

语法:Symbol([description])

参数:description 可选的,字符串。symbol的description可以用于调试,但无法访问到symbol本身

    let str = 'foo';
    let sym1 = Symbol();
    let sym2 = Symbol(str);
    let sym3 = Symbol(str);
    console.log(sym2 === sym3); //false

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型

二、Symbol静态属性

1.Symbol.hasInstance属性 

用于判断某对象是否为某构造器的实例,因此你可以用它自定义 instanceof 操作符在某个类上的行为。对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法

   class MyPrime {
        static  [Symbol.hasInstance](obj) {
            let element = Number(obj)
            for (let start = 2; start < element; start++) {
                if (element % start === 0){
                    return false;
                    break;
                }
            }
            return true;
        }
    }
    console.log(5 instanceof MyPrime);//true
    console.log(10 instanceof MyPrime);//false

2.Symbol.isConcatSpreadable属性

用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。对于数组对象,默认情况下,用于concat时,会按数组元素展开然后进行连接(数组元素作为新数组的元素)。重置Symbol.isConcatSpreadable可以改变默认行为。对于类似数组的对象,用于concat时,该对象整体作为新数组的元素,重置Symbol.isConcatSpreadable可改变默认行为。

    let array1 = ['a','b','c'];
    let array2 = ['d','e','f'];
    console.log(array1.concat(array2)); //['a','b','c','d','e','f']
    array2[Symbol.isConcatSpreadable] = false;
    console.log(array1.concat(array2)); //['a','b','c',['d','e','f']]

    let arrayLike = {length:2,0:'g',1:'h'};
    console.log(array1.concat(arrayLike)); //["a","b","c",{"0":"g","1":"h","length":2}]
    console.log(arrayLike[Symbol.isConcatSpreadable]); //undefined
    arrayLike[Symbol.isConcatSpreadable] = true;
    console.log(JSON.stringify(array1.concat(arrayLike))); //["a","b","c","g","h"]

3.Symbol.iterator属性

为每一个对象定义了默认的迭代器,该迭代器可以被 for...of 循环使用。对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器

    let iterator = {};
    iterator[Symbol.iterator] = function* () {
        yield 1;
        yield 2;
        yield 3;
    };
    console.log([...iterator]); //[1,2,3]
    class MyIterator {
        constructor(value) {
            this.value = value;
        }
        *[Symbol.iterator]() {
            let i = 0;
            while (this.value[i] !== undefined) {
                yield this.value[i];
                i++;
            }
        }
    }
    let myIterator = new MyIterator([1,2,3,4]);
    for (let value of myIterator) {
        console.log(value);//1 2 3 4
    }

4.Symbol.match属性

指定了匹配的是正则表达式而不是字符串。String.prototype.match() 方法会调用此函数;String.prototype.startsWith(),String.prototype.endsWith() 和 String.prototype.includes() 这些方法会检查其第一个参数是否是正则表达式,是正则表达式就抛出一个TypeError。 如果 match symbol 设置为 false(或者一个 假值),就表示该对象不打算用作正则表达式对象

    let reg = /foo/;
    reg[Symbol.match] = false;
    console.log("/foo/".startsWith(reg)); //true
    class MyMatch {
        [Symbol.match](string) {
            return '/foo/'.startsWith(string);
        }
    }
    console.log('/foo/'.match(new MyMatch()));//true

5.Symbol.replace属性

指定了当一个字符串替换所匹配字符串时所调用的方法。String.prototype.replace() 方法会调用此方法

    let x = {};
    x[Symbol.replace] = (...s) => {
        console.log(s);//["hello","world"]
    };
    'hello'.replace(x,'world'); 

6.Symbol.search属性

指定了一个搜索方法,这个方法接受用户输入的正则表达式,返回该正则表达式在字符串中匹配到的下标。这个方法由以下的方法来调用 String.prototype.search()

    class MySearch{
        constructor(value) {
            this.value = value;
        }
        [Symbol.search](string) {
            return string.indexOf(this.value);
        }
    }
    console.log('foobar'.search(new MySearch('foo'))); //0

7.species属性

 访问器属性允许子类覆盖对象的默认构造函数

    class MyArray extends Array {

    }
    let a = new MyArray(1,2,3);
    let b = a.map(x => x);
    let c = a.filter(x => x > 1);
    console.log(b instanceof MyArray);//true
    console.log(c instanceof MyArray);//true
    class MyArray extends Array {
        static get [Symbol.species]() {
            return Array;
        }
    }
    let a = new MyArray(1,2,3);
    let b = a.map(x => x);
    console.log(b instanceof MyArray);//false
    console.log(b instanceof Array);//true

8.Symbol.split属性

指向 一个正则表达式的索引处分割字符串的方法。这个方法通过 String.prototype.split() 调用

    class MySplit{
        constructor(value) {
            this.value = value;
        }
        [Symbol.split](string) {
            let index = string.indexOf(this.value);
            if (index === -1) {
                return string;
            }
            return [string.substr(0,index),string.substr(index+this.value.length)];
        }
    }
    console.log('hello,world,js'.split(new MySplit(',')));//["hello","world,js"]

9.Symbol.toPrimitive属性

指将被调用的指定函数值的属性转换为相对应的原始值。Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:

扫描二维码关注公众号,回复: 1589823 查看本文章
        Number:该场合需要转成数值
        String:该场合需要转成字符串
        Default:该场合可以转成数值,也可以转成字符串
    let obj1 = {};
    console.log(+obj1); //NaN
    console.log(`${obj1}`); //[object Object]
    console.log(obj1 + ''); //[object Object]
    let obj2 = {
        [Symbol.toPrimitive](hint) {
            if (hint == "number") {
                return 10;
            }
            if (hint == "string") {
                return "hello";
            }
            return true;
        }
    };
    console.log(+obj2); //10
    console.log(`${obj2}`); //hello
    console.log(obj2 + ''); //true

10.Symbol.toStringTag属性

通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签。通常只有内置的 Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。许多内置的 JavaScript 对象类型即便没有 toStringTag 属性,也能被 toString() 方法识别并返回特定的类型标签

Object.prototype.toString.call('foo');     // "[object String]"
Object.prototype.toString.call([1, 2]);    // "[object Array]"
Object.prototype.toString.call(3);         // "[object Number]"
Object.prototype.toString.call(true);      // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null);      // "[object Null]"
    class MyClass {}
    console.log(Object.prototype.toString.call(new MyClass())); // [object Object]
    class MyClass {
        get [Symbol.toStringTag]() {
            return "MyClass";
        }
    }
    console.log(Object.prototype.toString.call(new MyClass()));// [object MyClass]

11.Symbol.unscopables属性

指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称

Object.keys(Array.prototype[Symbol.unscopables])
// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

上面代码说明,数组有 7 个属性,会被with命令排除

    class MyWith {
        foo() { return 1; }
    }
    let foo = function () { return 2; };
    with (MyWith.prototype) {
        console.log(foo()); // 1
    }
    class MyWith {
        foo() { return 1; }
        get [Symbol.unscopables]() {
            return { foo: true };
        }
    }
    let foo = function () { return 2; };
    with (MyWith.prototype) {
        foo(); // 2
    }

三、Symbol静态方法

1.Symbol.for(key)

 方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。

语法:Symbol.for(key);

参数:key
一个字符串,作为 symbol 注册表中与某 symbol 关联的键(同时也会作为该 symbol 的描述)

返回值:返回由给定的 key 找到的 symbol,否则就是返回新创建的 symbol

和 Symbol() 不同的是,用 Symbol.for() 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个

    Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
    Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol

    Symbol.for("bar") === Symbol.for("bar"); // true
    Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol
    
    let sym = Symbol.for("mario");
    console.log(sym.toString());//Symbol(mario)

2.Symbol.keyFor(sym)

 方法用来获取 symbol 注册表中与某个 symbol 关联的键

语法:Symbol.keyFor(sym);

参数:sym
存储在 symbol 注册表中的某个 symbol

    let globalSym = Symbol.for("foo");
    Symbol.keyFor(globalSym); // "foo"

// 创建一个 symbol,但不放入 symbol 注册表中
    let localSym = Symbol();
    Symbol.keyFor(localSym); // undefined,所以是找不到 key 的

猜你喜欢

转载自blog.csdn.net/qq_30817073/article/details/80655310