什麼是 Symbol?為什麼會有這麼東西?以下這篇文章跟大家介紹一下JavaScript中的Symbol類型,聊聊使用方法,希望對大家有幫助!
Symbol(符號)是 ES6 新增的資料型別。 Symbol 是原始值(基礎資料型別),且 Symbol 實例是唯一、不可變的。它的產生是因為要用來唯一的標記,進而用作非字串形式的物件屬性,是確保物件屬性使用唯一標識符,不會發生屬性衝突的危險。 【相關推薦:javascript學習教學】
符號需要使用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
如果在程式碼中有多個地方需要使用同一個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
物件中的屬性一般都是字串的形式,但其實也是可以使用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)
#ES6 也引入了一批常用內建符號(well-known symbol),用於暴露語言內部行為,開發者可以直接存取、重寫或模擬這些行為。如果對這些預設的屬性進行了修改的話,是可以改變一些操作最後執行的結果的。例如 for-of 迴圈會在相關物件上使用 Symbol.iterator 屬性,那麼就可以透過在自訂物件上重新定義 Symbol.iterator 的值,來改變 for-of 在迭代該物件時的行為。
其實就是一個回傳Promise 的Generator,一般配合for await of 使用
根據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
這個屬性定義在Array 的原型上
根據ECMAScript 規範,這個符號作為一個屬性表示「一個布林值,如果是true,則表示物件應該用Array.prototype.concat()打平其陣列元素」。 ES6 中的 Array.prototype.concat()方法會 根據接收的物件類型選擇如何將一個類別數組(偽數組)物件拼接成數組實例。所以修改 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']
根據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
根据 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
这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中 匹配正则表达式的索引。由 String.prototype.search()方法使用”
这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构 造函数”。
这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表 达式的索引位置拆分字符串。由 String.prototype.split()方法使用”。
这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始 值。由 ToPrimitive 抽象操作使用”
这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认 字符串描述。由内置方法 Object.prototype.toString()使用”
这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性, 都会从关联对象的 with 环境绑定中排除
更多编程相关知识,请访问:编程视频!!
以上是為什麼會有Symbol類型?怎麼使用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!