《火炬之光:無限》手遊月底封測
Symbol基本使用
ES6 引入了一種新的原始資料型別 Symbol,表示獨一無二的值。它是JavaScript 語言的第七種資料型別(其餘六種資料型別:undefined 、number 、boolean、string、object、function),是一種類似於字串的資料型別。
Symbol特點
1) Symbol的值是唯一的,用來解決命名衝突的問題
2) Symbol值不能與其他資料進行運算
3) Symbol定義的物件屬性不能使用 for…in迴圈遍歷,但是可以使用Reflect.ownKeys來獲取物件的所有鍵名
<script> //建立Symbollet 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
繼承了父類Array
。a.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
將指向外層作用域的變數。