首頁 >web前端 >js教程 >為什麼會有Symbol類型?怎麼使用?

為什麼會有Symbol類型?怎麼使用?

青灯夜游
青灯夜游轉載
2022-03-22 11:04:512759瀏覽

什麼是 Symbol?為什麼會有這麼東西?以下這篇文章跟大家介紹一下JavaScript中的Symbol類型,聊聊使用方法,希望對大家有幫助!

為什麼會有Symbol類型?怎麼使用?

什麼是 Symbol?為什麼會有這麼東西?

Symbol(符號)是 ES6 新增的資料型別。 Symbol 是原始值(基礎資料型別),且 Symbol 實例是唯一、不可變的。它的產生是因為要用來唯一的標記,進而用作非字串形式的物件屬性,是確保物件屬性使用唯一標識符,不會發生屬性衝突的危險。 【相關推薦:javascript學習教學

用法

1. 基本用法

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

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

Symbol()函數可以接收一個字串參數用來描述,後,後續可以透過這個字串來偵錯程式碼。但值得注意的是,多個 Symbol()函數即使接受的參數是一樣的,他們的值也是不相等的。

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()方法來創建一個可以重複使用的Symbol,類似於單例模式,在第一次使用Symbol.for()的時候,它會根據傳入的參數會全局的去尋找是否使用Symbol.for()曾經建立過相同的實例,如果有,則重複使用,如果沒有,則新建

let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号
let otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

Symbol.for()所建立的實例和Symbol()所建立的實例差異: Symbol()創建的實例永遠都是唯一的,不會因為你傳入的參數相同而跟其他的實例相等,但是Symbol.for()創建的實例如果參數相同的話他們是會相等的,因為他們會公用同一個Symbol 實例

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

let fooGlobalSymbol = Symbol.for("foo"); // 创建新符号
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}

注意: 建立Symbol 實例作為物件屬性的時候,如果改symbol 一開始沒有宣告一個變數接收的話,後續就必須遍歷物件的所有符號屬性才能找到對應的屬性鍵:

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. 常用內建符號

#ES6 也引入了一批常用內建符號(well-known symbol),用於暴露語言內部行為,開發者可以直接存取、重寫或模擬這些行為。如果對這些預設的屬性進行了修改的話,是可以改變一些操作最後執行的結果的。例如 for-of 迴圈會在相關物件上使用 Symbol.iterator 屬性,那麼就可以透過在自訂物件上重新定義 Symbol.iterator 的值,來改變 for-of 在迭代該物件時的行為。

5. Symbol.asyncIterator

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

#6. Symbol. hasInstance

根據ECMAScript 規範,這個符號作為一個屬性表示「一個方法,該方法傳回物件預設的AsyncIterator。由for-await-of 語句使用」。換句話說,這個符號表示實作非同步迭代器 API 的函數。

這個屬性定義在 Function 的原型上。都知道 instanceof 運算子可以用來確定一個物件實例是否屬於某個建構子。其原理就是instanceof 運算子會使用Symbol.hasInstance 函數來決定關係式

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

如果你重新定義一個函數的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 的原型上

根據ECMAScript 規範,這個符號作為一個屬性表示「一個布林值,如果是true,則表示物件應該用Array.prototype.concat()打平其陣列元素」。 ES6 中的 Array.prototype.concat()方法會 根據接收的物件類型選擇如何將一個類別數組(偽數組)物件拼接成數組實例。所以修改 Symbol.isConcatSpreadable 的值可以修改這個行為。

Symbol.isConcatSpreadable 對應的效果

false: 將一整個物件新增進數組true: 將一整個對打平加進數組

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

根據ECMAScript 規範,這個符號作為一個屬性表示「一個方法,該方法傳回物件預設的迭代器。由for-of 語句使用”

该属性会返回一个 Generator 函数,for of 就会依次的去调用 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

根据 ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由 String.prototype.match()方法使用”。

String.prototype.match()方法会使用以 Symbol.match 为键的函数来对正则表达式求值。所以更改一个正则表达式的 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

这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构 造函数”。

13. Symbol.split

这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表 达式的索引位置拆分字符串。由 String.prototype.split()方法使用”。

14. Symbol.toPrimitive

这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始 值。由 ToPrimitive 抽象操作使用”

15. Symbol.toStringTag

这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认 字符串描述。由内置方法 Object.prototype.toString()使用”

16. Symbol.unscopables

这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性, 都会从关联对象的 with 环境绑定中排除

更多编程相关知识,请访问:编程视频!!

以上是為什麼會有Symbol類型?怎麼使用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除