1. 程式人生 > 資訊 >《火炬之光:無限》手遊月底封測

《火炬之光:無限》手遊月底封測

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將指向外層作用域的變數。