溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript中Symbol是什么

發(fā)布時間:2022-03-22 11:36:28 來源:億速云 閱讀:372 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了JavaScript中Symbol是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

什么是 Symbol?為什么會有這么個東西?

Symbol(符號)是 ES6 新增的數(shù)據(jù)類型。Symbol 是原始值(基礎(chǔ)數(shù)據(jù)類型),且 Symbol 實例是唯一、不可變的。它的產(chǎn)生是因為要用來唯一的標(biāo)記,進而用作非字符串形式的對象屬性,是確保對象屬性使用唯一標(biāo)識符,不會發(fā)生屬性沖突的危險?!鞠嚓P(guān)推薦:javascript學(xué)習(xí)教程】

用法

1. 基本用法

符號需要使用 Symbol()函數(shù)初始化。因為符號本身是原始類型,所以 typeof 操作符對符號返回 symbol。

let sym = Symbol();
console.log(typeof sym); // symbol

Symbol()函數(shù)可以接收一個字符串參數(shù)用來描述,后,后續(xù)可以通過這個字符串來調(diào)試代碼。但值得注意的是,多個 Symbol()函數(shù)即使接受的參數(shù)是一樣的,他們的值也是不相等的。

let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
let fooSymbol = Symbol("foo");
let otherFooSymbol = Symbol("foo");

console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false

2. 使用全局符號注冊表

如果在代碼中有多個地方需要使用同一個 Symbol 實例的時候,可以傳入一個字符串,然后使用 Symbol.for()方法來創(chuàng)建一個可以復(fù)用的 Symbol,類似于單例模式,在第一次使用 Symbol.for()的時候,它會根據(jù)傳入的參數(shù)會全局的去尋找是否使用 Symbol.for()創(chuàng)建過同樣的實例,如果有,則復(fù)用,如果沒有,則新建

let fooGlobalSymbol = Symbol.for("foo"); // 創(chuàng)建新符號
let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符號
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

Symbol.for()創(chuàng)建的實例和 Symbol()創(chuàng)建的實例區(qū)別: Symbol()創(chuàng)建的實例永遠(yuǎn)都是唯一的,不會因為你傳入的參數(shù)相同而跟其他的實例相等,但是 Symbol.for()創(chuàng)建的實例如果參數(shù)相同的話他們是會相等的,因為他們會公用同一個 Symbol 實例

let fooSymbol = Symbol("foo");
let otherFooSymbol = Symbol("foo");
console.log(fooSymbol == otherFooSymbol); // false

let fooGlobalSymbol = Symbol.for("foo"); // 創(chuàng)建新符號
let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符號
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

3. 使用符號作為屬性

對象中的屬性一般都是字符串的形式,但其實也是可以使用 Symbol 實例來作為屬性的,這樣的好處就是你新增的屬性不會覆蓋掉以前的任何屬性

let s1 = Symbol("foo"),
  s2 = Symbol("bar"),
  s3 = Symbol("baz"),
  s4 = Symbol("qux");
let o = {
  [s1]: "foo val",
};
// 這樣也可以:o[s1] = 'foo val';
console.log(o);
// {Symbol(foo): foo val}
Object.defineProperty(o, s2, { value: "bar val" });
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val}
Object.defineProperties(o, {
  [s3]: { value: "baz val" },
  [s4]: { value: "qux val" },
});
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val,
// Symbol(baz): baz val, Symbol(qux): qux val}

注意: 創(chuàng)建 Symbol 實例作為對象屬性的時候,如果改 symbol 一開始沒有聲明一個變量進行接收的話,后續(xù)就必須遍歷對象的所有符號屬性才能找到相應(yīng)的屬性鍵:

let o = {
  [Symbol("foo")]: "foo val",
  [Symbol("bar")]: "bar val",
};
console.log(o);
// {Symbol(foo): "foo val", Symbol(bar): "bar val"}
let barSymbol = Object.getOwnPropertySymbols(o).find(symbol => symbol.toString().match(/bar/));
console.log(barSymbol);
// Symbol(bar)

4. 常用內(nèi)置符號

ES6 也引入了一批常用內(nèi)置符號(well-known symbol),用于暴露語言內(nèi)部行為,開發(fā)者可以直接訪問、重寫或模擬這些行為。如果對這些默認(rèn)的屬性進行了修改的話,是可以改變一些操作最后執(zhí)行的結(jié)果的。比如 for-of 循環(huán)會在相關(guān)對象上使用 Symbol.iterator 屬性,那么就可以通過在自定義對象上重新定義 Symbol.iterator 的值,來改變 for-of 在迭代該對象時的行為。

5. Symbol.asyncIterator

其實就是一個返回 Promise 的 Generator,一般配合 for await of 使用

6. Symbol.hasInstance

根據(jù) ECMAScript 規(guī)范,這個符號作為一個屬性表示“一個方法,該方法返回對象默認(rèn)的 AsyncIterator。 由 for-await-of 語句使用”。換句話說,這個符號表示實現(xiàn)異步迭代器 API 的函數(shù)。

這個屬性定義在 Function 的原型上。都知道 instanceof 操作符可以用來確定一個對象實例是否屬于某個構(gòu)造函數(shù)。其原理就是 instanceof 操作符會使用 Symbol.hasInstance 函數(shù)來確定關(guān)系

function Foo() {}
let f = new Foo();
console.log(f instanceof Foo); // true
class Bar {}
let b = new Bar();
console.log(b instanceof Bar); // true

如果你重新定義一個函數(shù)的 Symbol.hasInstance 屬性,你就可以讓 instanceof 方法返回一些意料之外的東西

class Bar {}
class Baz extends Bar {
  static [Symbol.hasInstance]() {
    return false;
  }
}
let b = new Baz();
console.log(Bar[Symbol.hasInstance](b)); // true
console.log(b instanceof Bar); // true
console.log(Baz[Symbol.hasInstance](b)); // false
console.log(b instanceof Baz); // false

Symbol.isConcatSpreadabl

這個屬性定義在 Array 的原型上

根據(jù) ECMAScript 規(guī)范,這個符號作為一個屬性表示“一個布爾值,如果是 true,則意味著對象應(yīng)該用 Array.prototype.concat()打平其數(shù)組元素”。ES6 中的 Array.prototype.concat()方法會 根據(jù)接收到的對象類型選擇如何將一個類數(shù)組(偽數(shù)組)對象拼接成數(shù)組實例。所以修改 Symbol.isConcatSpreadable 的值可以修改這個行為。

Symbol.isConcatSpreadable 對應(yīng)的效果

false: 將一整個對象添加進數(shù)組true: 將一整個對打平添加進數(shù)組

let initial = ["foo"];
let array = ["bar"];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // ['foo', 'bar']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // ['foo', Array(1)]
let arrayLikeObject = { length: 1, 0: "baz" };
console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(arrayLikeObject)); // ['foo', {...}]

arrayLikeObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz']
let otherObject = new Set().add("qux");
console.log(otherObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(otherObject)); // ['foo', Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject)); // ['foo']

8. Symbol.iterator

根據(jù) ECMAScript 規(guī)范,這個符號作為一個屬性表示“一個方法,該方法返回對象默認(rèn)的迭代器。由 for-of 語句使用”

該屬性會返回一個 Generator 函數(shù),for of 就會依次的去調(diào)用 next()方法,這就是為什么 for of 可以使用在某些對象身上。

class Emitter {
  constructor(max) {
    this.max = max;
    this.idx = 0;
  }
  *[Symbol.iterator]() {
    while (this.idx < this.max) {
      yield this.idx++;
    }
  }
}
function count() {
  let emitter = new Emitter(5);
  for (const x of emitter) {
    console.log(x);
  }
}
count();
// 0
// 1
// 2
// 3
// 4

9. Symbol.match

根據(jù) ECMAScript 規(guī)范,這個符號作為一個屬性表示“一個正則表達式方法,該方法用正則表達式去匹配字符串。由 String.prototype.match()方法使用”。

String.prototype.match()方法會使用以 Symbol.match 為鍵的函數(shù)來對正則表達式求值。所以更改一個正則表達式的 Symbol.match 屬性,可以讓 String.prototype.match()得到你想要的值

console.log(RegExp.prototype[Symbol.match]);
// ? [Symbol.match]() { [native code] }
console.log("foobar".match(/bar/));
// ["bar", index: 3, input: "foobar", groups: undefined]

class FooMatcher {
  static [Symbol.match](target) {
    return target.includes("foo");
  }
}
console.log("foobar".match(FooMatcher)); // true
console.log("barbaz".match(FooMatcher)); // false
class StringMatcher {
  constructor(str) {
    this.str = str;
  }
  [Symbol.match](target) {
    return target.includes(this.str);
  }
}
console.log("foobar".match(new StringMatcher("foo"))); // true
console.log("barbaz".match(new StringMatcher("qux"))); // false

11. Symbol.search

這個符號作為一個屬性表示“一個正則表達式方法,該方法返回字符串中 匹配正則表達式的索引。由 String.prototype.search()方法使用”

12. Symbol.species

這個符號作為一個屬性表示“一個函數(shù)值,該函數(shù)作為創(chuàng)建派生對象的構(gòu) 造函數(shù)”。

13. Symbol.split

這個符號作為一個屬性表示“一個正則表達式方法,該方法在匹配正則表 達式的索引位置拆分字符串。由 String.prototype.split()方法使用”。

14. Symbol.toPrimitive

這個符號作為一個屬性表示“一個方法,該方法將對象轉(zhuǎn)換為相應(yīng)的原始 值。由 ToPrimitive 抽象操作使用”

15. Symbol.toStringTag

這個符號作為一個屬性表示“一個字符串,該字符串用于創(chuàng)建對象的默認(rèn) 字符串描述。由內(nèi)置方法 Object.prototype.toString()使用”

16. Symbol.unscopables

這個符號作為一個屬性表示“一個對象,該對象所有的以及繼承的屬性, 都會從關(guān)聯(lián)對象的 with 環(huán)境綁定中排除

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“JavaScript中Symbol是什么”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI