ES6_Symbol

Symbol基本使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型(其余六种数据类型:undefined 、number 、boolean、string、object、function),是一种类似于字符串的数据类型。
Symbol特点
  1) Symbol的值是唯一的,用来解决命名冲突的问题
  2) Symbol值不能与其他数据进行运算
  3) Symbol定义的对象属性不能使用 for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名

    <script>
        //创建Symbol
        let s = Symbol();
        // console.log(s, typeof s);
        let s2 = Symbol('哈哈');
        let s3 = Symbol('哈哈');
        //Symbol.for 创建
        let s4 = Symbol.for('哈哈');
        let s5 = Symbol.for('哈哈');

        console.log(s2.toString()); // Symbol(哈哈)
        console.log(s2.description); // 哈哈
        console.log(s4.description); // 哈哈
        console.log(Symbol.keyFor(s2)); // undefined
        console.log(Symbol.keyFor(s4)); // 哈哈
        console.log(s2 === s3); // false
        console.log(s4 === s5); // true

        //   不能与其他数据进行运算
        //    let result = s + 100;
        //    let result = s > 100;
        //    let result = s + s;
    </script>

Symbol创建对象属性

    <script>
        //向对象中添加方法 up down
        let game = {
            name: '俄罗斯方块',
            up: function() {},
            down: function() {}
        };

        //声明一个对象
        // let methods = {
        //     up: Symbol(),
        //     down: Symbol()
        // };

        // game[methods.up] = function(){
        //     console.log("我可以改变形状");
        // }

        // game[methods.down] = function(){
        //     console.log("我可以快速下降!!");
        // }
        // console.log(game);

        let youxi = {
            name: "狼人杀",
            [Symbol('say')]: function() {
                console.log("我可以发言")
            },
            [Symbol('zibao')]: function() {
                console.log('我可以自爆');
            }
        }
        console.log(youxi)
    </script>

 Symbol内置值

除了定义自己使用的Symbol 值以外, ES6 还提供了 11个内置的 Symbol值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

内置值 简介
Symbol.hasInstance 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。
Symbol.species 创建衍生对象时,会使用该属性
Symbol.match 当执行str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。
Symbol.replace 当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值。
Symbol.search 当该对象被str. search (myObject)方法调用时,会返回该方法的返回值。
Symbol.split 当该对象被str. split (myObject)方法调用时,会返回该方法的返回值。
Symbol.iterator 对象进行for...of循环时,会调用 Symbol.iterator方法,返回该对象的默认遍历器
Symbol.toPrimitive 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol. toStringTag 在该对象上面调用toString方法时 ,返回该方法的返回值
Symbol. unscopables 该对象指定了使用with关键字时,哪些属性会被 with环境排除。

Symbol.hasInstance

class Person{ 
    static [Symbol.hasInstance](param) {
        console.log(param);
        console.log('我被用来检测类型');
        return true;
    }
}
let o = {}
console.log(o instanceof Person); // {} '我被用来检测类型' true

 

Symbol.isConcatSpreadable

ES6中的Array.prototype.concat()方法会根据接收到的对象类型选择如何将一个类数组对象拼接成数组实例。

覆盖Symbol.isConcatSpreadable的值可以修改这个行为对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。

const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
console.log(arr.concat(arr2)); // [1, 2, 3, 4, 5, 6]
console.log(arr2[Symbol.isConcatSpreadable]); // undefined
arr2[Symbol.isConcatSpreadable] = false; 
console.log(arr.concat(arr2)); //[1, 2, 3, [4, 5, 6]]

 

上面代码说明,数组的默认行为是可以展开,Symbol.isConcatSpreadable默认等于undefined。该属性等于true时,也有展开的效果。

类似数组的对象正好相反,默认不展开。它的Symbol.isConcatSpreadable属性设为true,才可以展开。

let obj = {length: 2, 0: 'c', 1: 'd'};
console.log(['a', 'b'].concat(obj, 'e')) // ['a', 'b', obj, 'e']

obj[Symbol.isConcatSpreadable] = true;
console.log(['a', 'b'].concat(obj, 'e')) // ['a', 'b', 'c', 'd', 'e']

 

Symbol.isConcatSpreadable属性也可以定义在类里面。

class A1 extends Array {
  constructor(args) {
    super(args);
    this[Symbol.isConcatSpreadable] = true;
  }
}
class A2 extends Array {
  constructor(args) {
    super(args);
  }
  get [Symbol.isConcatSpreadable] () {
    return false;
  }
}
let a1 = new A1();
a1[0] = 3;
a1[1] = 4;
let a2 = new A2();
a2[0] = 5;
a2[1] = 6;
console.log([1, 2].concat(a1).concat(a2))  // [1, 2, 3, 4, [5, 6]]

 

上面代码中,类A1是可展开的,类A2是不可展开的,所以使用concat时有不一样的结果。

注意,Symbol.isConcatSpreadable的位置差异,A1是定义在实例上,A2是定义在类本身,效果相同。

Symbol.species

对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。

class MyArray extends Array {
}
const a = new MyArray();
console.log(a.map(x => x) instanceof MyArray) // true

 

上面代码中,子类MyArray继承了父类Arraya.map(x => x)会创建一个MyArray的衍生对象,该衍生对象还是MyArray的实例。

现在,MyArray设置Symbol.species属性。

class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}

 

上面代码中,由于定义了Symbol.species属性,创建衍生对象时就会使用这个属性返回的的函数,作为构造函数。

这个例子也说明,定义Symbol.species属性要采用get读取器。

默认的Symbol.species属性等同于下面的写法。

static get [Symbol.species]() {
  return this;
}

 

现在,再来看前面的例子。

class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}
const a = new MyArray();
console.log(a.map(x => x) instanceof MyArray) // false
console.log(a.map(x => x) instanceof Array) // true

 

上面代码中,a.map(x => x)创建的衍生对象,就不是MyArray的实例,而直接就是Array的实例。

再看一个例子。

class T1 extends Promise {
}
class T2 extends Promise {
  static get [Symbol.species]() {
    return Promise;
  }
}
console.log(new T1(r => r()).then(v => v) instanceof T1) // true
console.log(new T2(r => r()).then(v => v) instanceof T2) // false

 

上面代码中,T2定义了Symbol.species属性,T1没有。结果就导致了创建衍生对象时(then方法),T1调用的是自身的构造方法,而T2调用的是Promise的构造方法。

总之,Symbol.species的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。

它主要的用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。

Symbol.match

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。

// String.prototype.match(regexp)
// 等同于
// regexp[Symbol.match](this)
class MyMatcher {
  [Symbol.match](string) {
    return 'hello world'.indexOf(string);
  }
}
console.log('e'.match(new MyMatcher())) // 1

 

Symbol.replace

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

// String.prototype.replace(searchValue, replaceValue)
// 等同于
// searchValue[Symbol.replace](this, replaceValue)
const x = {};
x[Symbol.replace] = (...s) => console.log(s);
console.log('Hello'.replace(x, 'World')) // ["Hello", "World"]

 

Symbol.replace方法会收到两个参数:

第一个参数是replace方法正在作用的对象,上面例子是Hello

第二个参数是替换后的值,上面例子是World

Symbol.search

对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

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

Symbol.split

对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。

// String.prototype.split(separator, limit)
// 等同于
// separator[Symbol.split](this, limit)
class MySplitter {
  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('foobar'.split(new MySplitter('foo')))  // ['', 'bar']
console.log('foobar'.split(new MySplitter('bar')))  // ['foo', '']
console.log('foobar'.split(new MySplitter('baz')))  // 'foobar'

 

上面方法使用Symbol.split方法,重新定义了字符串对象的split方法的行为

Symbol.iterator

对象的Symbol.iterator属性,指向该对象的默认遍历器方法。

对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器

 //声明一个数组
 const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];

 //使用 for...of 遍历数组
 // for(let v of xiyou){
 //     console.log(v);
 // }

 let iterator = xiyou[Symbol.iterator]();

 //调用对象的next方法
 console.log(iterator.next());
 console.log(iterator.next());
 console.log(iterator.next());
 console.log(iterator.next());
 console.log(iterator.next());

 

 

 迭代器自定义遍历对象

     <script>
        //声明一个对象
        // const banji = {
        //     name: "终极一班",
        //     stus: [
        //         'xiaoming',
        //         'xiaoning',
        //         'xiaotian',
        //         'knight'
        //     ],
        //     [Symbol.iterator]() {
        //         //索引变量
        //         let index = 0;
        //         //
        //         let _this = this;
        //         return {
        //             next: function() {
        //                 if (index < _this.stus.length) {
        //                     const result = {
        //                         value: _this.stus[index],
        //                         done: false
        //                     };
        //                     //下标自增
        //                     index++;
        //                     //返回结果
        //                     return result;
        //                 } else {
        //                     return {
        //                         value: undefined,
        //                         done: true
        //                     };
        //                 }
        //             }
        //         };
        //     }
        // }

        //遍历这个对象 
        // for (let v of banji) {
        //     console.log(v);
        // }
        // for (let v in banji) {
        //     console.log(v);
        // }



        //简洁版
        let obj = {
            name: "Ges",
            age: 21,
            hobbies: ["ESgsg", "Sfgse", "Egs", "SEGSg"],
            *[Symbol.iterator]() {
                for (let arg of Object.values(this)) {
                    yield arg;
                }
            }

        }

        for (let item of obj) {
            console.log(item)
        }
    </script>

 

 

 

Symbol.toPrimitive

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

  • Number:该场合需要转成数值
  • String:该场合需要转成字符串
  • Default:该场合可以转成数值,也可以转成字符串
let obj = {
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number':
        return 123;
      case 'string':
        return 'str';
      case 'default':
        return 'default';
      default:
        throw new Error();
     }
   }
};
console.log(2 * obj) // 246
console.log(3 + obj) // '3default'
console.log(obj == 'default') // true
console.log(String(obj)) // 'str'

 

Symbol. toStringTag

对象的Symbol.toStringTag属性,指向一个方法。

在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。

也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串。

// 例一
console.log({[Symbol.toStringTag]: 'Foo'}.toString())  // "[object Foo]"
// 例二
class Collection {
  get [Symbol.toStringTag]() {
    return 'xxx';
  }
}
let x = new Collection();
console.log(Object.prototype.toString.call(x)) // "[object xxx]"

 

ES6 新增内置对象的Symbol.toStringTag属性值如下。

  • JSON[Symbol.toStringTag]:'JSON'
  • Math[Symbol.toStringTag]:'Math'
  • Module 对象M[Symbol.toStringTag]:'Module'
  • ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
  • DataView.prototype[Symbol.toStringTag]:'DataView'
  • Map.prototype[Symbol.toStringTag]:'Map'
  • Promise.prototype[Symbol.toStringTag]:'Promise'
  • Set.prototype[Symbol.toStringTag]:'Set'
  • %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
  • WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
  • WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
  • %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
  • %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
  • %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
  • Symbol.prototype[Symbol.toStringTag]:'Symbol'
  • Generator.prototype[Symbol.toStringTag]:'Generator'
  • GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'

Symbol. unscopables

对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

console.log(Array.prototype[Symbol.unscopables])
// {
//   copyWithin: true,
//   entries: true,
//   fill: true,
//   find: true,
//   findIndex: true,
//   includes: true,
//   keys: true
// }
console.log(Object.keys(Array.prototype[Symbol.unscopables]))
// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

 

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

// 没有 unscopables 时
class MyClass {
  foo() { return 1; }
}
var foo = function () { return 2; };
with (MyClass.prototype) {
  foo(); // 1
}
// 有 unscopables 时
class MyClass {
  foo() { return 1; }
  get [Symbol.unscopables]() {
    return { foo: true };
  }
}
var foo = function () { return 2; };
with (MyClass.prototype) {
  foo(); // 2
}

 上面代码通过指定Symbol.unscopables属性,使得with语法块不会在当前作用域寻找foo属性,即foo将指向外层作用域的变量。

posted @ 2022-04-19 11:47  城南以南123  阅读(20)  评论(0编辑  收藏  举报