<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
符號(Symbol
)是JavaScript中的一個原始資料型別,是ECMAScript 6標準引入的新特性。符號是一種類似於字串的資料型別,但與字串不同的是,符號是唯一的並且不可變的。
Symbol
的定義方法如下:
const mySymbol = Symbol('my symbol');
每次呼叫Symbol
建立的值都是唯一的,即使對同一個引數呼叫兩遍Symbol
它們的值還是不一樣的:
Symbol("foo") === Symbol("foo"); // false
在Symbol
出現之前,Javascript
已經有五種內建的基本資料型別:
Boolean
):表示真或假,只有兩個取值:true和false。Number
):表示整數或浮點數,可以使用十進位制、十六進位制、八進位制、科學計數法等多種表示方式。String
):表示文字字串,可以使用單引號、雙引號、反引號等方式表示。Null
):表示一個空值或不存在的物件。Undefined
):表示一個未定義的值或未宣告的變數。Symbol
則作為第六種基本資料型別加入到語言中:
Symbol
):表示唯一的、不可變的值,用於保護屬性名、實現私有屬性或方法等場景。在JavaScript誕生之初,物件屬性只能使用字串作為鍵,這導致了一些問題。例如,當兩個不同的物件試圖使用相同的字串作為屬性名時,可能會導致屬性名衝突。此外,JavaScript
中沒有一種簡單的方法來實現私有屬性或方法。
其實對於Symbol
的追溯早在Lisp
語言中就有體現:
(setq x (intern "my-symbol"))
這裡其實就是建立了一個名為my-symbol
的符號物件,並將其賦值給變數x
。
另外,ES6引入Symbol
其實離不開Ruby
的身影,在Ruby
中,可以使用冒號(:
)來建立符號。冒號後面跟著符號的名稱,如:
:my_symbol
可以看到其實Ruby
的語法更加簡潔,定義和使用都是用冒號區分:
person = { 'name' => 'John', 'age' => 30, :gender => 'Male' } puts person[:gender] # 輸出:'Male'
所以,在這樣的需求背景下,ES6
在首批特性中包含了Symbol
也不足為奇了。
在JavaScript
中,可以使用Symbol()
函數來建立一個符號,如下所示:
const mySymbol = Symbol();
Symbol
函數可以接受一個描述性字串作為引數,用於識別符號號的含義,如下所示:
const mySymbol = Symbol('my symbol');
需要注意的是,每個Symbol()
函數呼叫都會返回一個唯一的符號,即使描述性字串相同,它們也是不同的符號。
Symbol
型別的值可以用作物件的屬性名,如下所示:
const mySymbol = Symbol('my symbol'); const myObject = { [mySymbol]: 'hello' }; console.log(myObject[mySymbol]); // 輸出:'hello'
在上面的程式碼中,我們使用符號mySymbol
作為物件myObject
的屬性名,並將其值設定為'hello'
。使用符號作為屬性名的好處是它們不會與其他屬性名衝突,並且對外不可見,因此可以用於實現私有屬性或方法等場景。
另外,JavaScript中的Symbol型別有兩個特殊的方法Symbol.for()
和Symbol.keyFor()
,用於建立全域性符號和獲取已經存在的全域性符號。
Symbol.for()
: 用於建立或獲取一個全域性符號,如果全域性符號已經存在,則返回已經存在的符號,否則建立一個新的全域性符號。例如:const mySymbol = Symbol.for('my symbol'); const sameSymbol = Symbol.for('my symbol'); console.log(mySymbol === sameSymbol); // 輸出:true
在上面的程式碼中,我們使用Symbol.for()
方法來建立一個全域性符號'my symbol'
,並將其賦值給mySymbol
變數。然後,我們再次使用Symbol.for()
方法來獲取同一個全域性符號,賦值給sameSymbol
變數。由於全域性符號已經存在,因此sameSymbol
變數的值等於mySymbol
變數的值,輸出true
。
const myObject = { *[Symbol.iterator]() { yield 1; yield 2; yield 3; } }; for (const value of myObject) { console.log(value); } // 輸出:1 2 3
在上面的程式碼中,我們為myObject
物件設定了Symbol.iterator
符號,並指定了一個生成器函數作為迭代器的實現。然後,我們可以使用for...of
迴圈迭代myObject
物件,並輸出其中的值。
Symbol.hasInstance
方法接受一個引數,表示要檢查的物件。該方法需要返回一個布林值,表示該物件是否為該建構函式的範例。例如:
class MyClass { static [Symbol.hasInstance](obj) { return obj instanceof Array; } } console.log([] instanceof MyClass); // 輸出:true console.log({} instanceof MyClass); // 輸出:false
在上面的程式碼中,我們定義了一個MyClass
類,並使用Symbol.hasInstance
方法自定義了instanceof
運運算元的行為,使其檢查物件是否為陣列。當檢查[]物件時,instanceof
運運算元返回true
,因為[]是Array
的範例;當檢查{}
物件時,instanceof
運運算元返回false
,因為{}不是Array
的範例。
需要注意的是,Symbol.hasInstance
方法是一個靜態方法,需要定義在建構函式的靜態屬性中。另外,Symbol.hasInstance
方法不能被繼承,因此子類需要重新定義該方法。
當呼叫Object.prototype.toString()
方法時,會使用該物件的Symbol.toStringTag
屬性作為預設的字串描述,例如:
class MyObject { get [Symbol.toStringTag]() { return 'MyObject'; } } const obj = new MyObject(); console.log(Object.prototype.toString.call(obj)); // 輸出:'[object MyObject]'
在上面的程式碼中,我們定義了一個MyObject
類,並使用Symbol.toStringTag
屬性自定義了該類的預設字串描述。然後,我們建立了一個obj
物件,並使用Object.prototype.toString()
方法獲取其字串描述,輸出'[object MyObject]'
。
需要注意的是,Symbol.toStringTag
屬性只有在呼叫Object.prototype.toString()
方法時才會生效,對其他方法沒有影響。另外,如果沒有定義Symbol.toStringTag
屬性,則預設使用建構函式的名稱作為字串描述。
當使用for await...of
迴圈迭代一個物件時,會呼叫該物件的Symbol.asyncIterator
方法獲取非同步迭代器。
Symbol.asyncIterator
方法需要返回一個非同步迭代器物件,該物件實現了next()
方法,並返回一個Promise
物件。當迭代器迭代到結束時,next()
方法應該返回一個Promise
物件,該Promise
物件的value
屬性為undefined
,done
屬性為true
。
例如,下面的程式碼演示瞭如何使用Symbol.asyncIterator屬性定義一個非同步迭代器:
const myObject = { async *[Symbol.asyncIterator]() { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } }; (async function() { for await (const value of myObject) { console.log(value); } })(); // 輸出:1 2 3
在上面的程式碼中,我們為myObject
物件設定了Symbol.asyncIterator
符號,並指定了一個非同步生成器函數作為非同步迭代器的實現。然後,我們使用for await...of
迴圈迭代myObject
物件,並輸出其中的值。
需要注意的是,使用Symbol.asyncIterator
屬性定義的非同步迭代器只能使用for await...of
迴圈進行迭代,不能使用普通的for...of
迴圈。此外,Symbol.asyncIterator
屬性只有在支援非同步迭代器的環境中才能使用,例如Node.js
的版本必須在10.0.0
以上才支援非同步迭代器。
symbol
作為基本資料型別實現比較簡單,在最新的v8
程式碼實現如下:
Symbol Factory::NewSymbolInternal(AllocationType allocation) { DCHECK(allocation != AllocationType::kYoung); // Statically ensure that it is safe to allocate symbols in paged spaces. STATIC_ASSERT(Symbol::kSize <= kMaxRegularHeapObjectSize); Symbol symbol = Symbol::cast(AllocateRawWithImmortalMap( Symbol::kSize, allocation, read_only_roots().symbol_map())); DisallowGarbageCollection no_gc; // Generate a random hash value. int hash = isolate()->GenerateIdentityHash(Name::kHashBitMask); symbol.set_raw_hash_field(Name::kIsNotIntegerIndexMask | (hash << Name::kHashShift)); symbol.set_description(read_only_roots().undefined_value(), SKIP_WRITE_BARRIER); symbol.set_flags(0); DCHECK(!symbol.is_private()); return symbol; }
該函數使用AllocateRawWithImmortalMap()
方法為新的Symbol
物件分配記憶體,並將其強制轉換為Symbol
型別。接著,該函數使用DisallowGarbageCollection
類禁用垃圾回收器,以確保不會在生成雜湊值的過程中觸發垃圾回收。接下來,該函數使用GenerateIdentityHash()
方法生成一個隨機的雜湊值,並將其儲存在新的Symbol
物件中。然後,該函數將Symbol
物件的描述設定為undefined
,並將其標誌設定為0
。最後,該函數返回新建立的Symbol物件。
所以使用hash
來唯一標識一個symbol
,在v8
內部還實現了symbol-table
來實現Symbol.for
的查詢,本質上也是一個雜湊表。
為了簡單起見,我們用js
來模擬一下Symbol
的實現:
const registry = {}; function createSymbol(description) { const symbol = Object.create(null); symbol.toString = () => `Symbol(${description || ''})`; Object.defineProperty(symbol, 'description', { value: description, writable: false, configurable: false, enumerable: false, }); return symbol; } function Symbol(description) { if (typeof description !== 'undefined') { description = String(description); } if (registry[description]) { return registry[description]; } const symbol = createSymbol(description); registry[description] = symbol; return symbol; } Symbol.for = function (key) { if (registry[key]) { return registry[key]; } const symbol = createSymbol(key); registry[key] = symbol; return symbol; }; Symbol.keyFor = function (symbol) { for (const key in registry) { if (registry.hasOwnProperty(key) && registry[key] === symbol) { return key; } } }; export default Symbol;
我們使用一個全域性物件registry
來儲存Symbol
物件及其描述符資訊。createSymbol()
函數用於建立新的Symbol
物件,其中使用了Object.create()
方法來建立一個沒有原型的物件,並通過定義toString()
和description
屬性來實現Symbol
物件的基本功能。Symbol()
函數用於建立新的Symbol
物件,它根據傳入的描述符資訊從registry
中查詢Symbol
物件,如果找到了則返回已有的Symbol
物件,否則建立新的Symbol
物件並新增到registry
中。
Symbol
在VSCode
的應用其實不多,最新的程式碼只有:
/** * Can be passed into the Delayed to defer using a microtask * */ export const MicrotaskDelay = Symbol('MicrotaskDelay');
在實際中,Symbol
經常被用於:
這在建立第三方庫或外掛時非常有用,因為可以確保庫或外掛的屬性不會與其他程式碼意外衝突。
const uniqueKey = Symbol('uniqueKey'); const obj = { [uniqueKey]: 'This value is uniquely keyed' };
使用Symbol可以在物件上建立"私有"屬性,它們不會被常規的屬性列舉(如for...in
,Object.keys()
或JSON.stringify()
)包含在內。這有助於保護物件內部實現細節。
JavaScript內建了一些具有特定功能的Symbol
。例如,Symbol.iterator
可以定義物件的迭代行為,Symbol.toStringTag
可以自定義Object.prototype.toString.call()
方法的輸出。
Symbol.for()
方法允許在全域性Symbol
登入檔中建立或獲取Symbol
。這對於跨多個地方或模組使用相同的Symbol
時非常有用。
const globalSymbol = Symbol.for('globalSymbol'); const sameGlobalSymbol = Symbol.for('globalSymbol'); console.log(globalSymbol === sameGlobalSymbol); // true
在tc39
上已經有兩個關於Symbol
的提案:
Symbol
作為一種新的資料型別,其功能和用途都比較有限,因此tc39
在Symbol
的基礎上提出了一些新的提案,以擴充套件其功能和用途。其中一個比較重要的提案是Symbols as WeakMap keys
,該提案已經進入到Stage3
階段。
WeakMap
是一種新的集合型別,可以用於儲存物件和關聯的後設資料。WeakMap
的特點是鍵必須是物件,值可以是任意型別。WeakMap
的另一個特點是,當鍵物件不再被參照時,WeakMap
會自動刪除該鍵值對,以避免記憶體漏失。
Symbols as WeakMap keys
提案的目的是將Symbol
作為WeakMap
的鍵。這樣,就可以在不影響WeakMap
的自動垃圾回收機制的情況下,將Symbol
作為物件的後設資料來使用。
const weak = new WeakMap(); // Pun not intended: being a symbol makes it become a more symbolic key const key = Symbol('my ref'); const someObject = { /* data data data */ }; weak.set(key, someObject);
這是另一個關於Symbol的提案,新增了以下判斷方法:Symbol.isRegistered(symbol)
和Symbol.isWellKnown(symbol)
。
其實對於庫作者而言,瞭解更多關於Symbol
的資訊是很重要的。根據使用情況,瞭解一個Symbol
是否真正唯一、可偽造(已註冊)或跨域共用(眾所周知)可能非常關鍵。例如,將Symbol
用作WeakMap
鍵需要確保Symbol
未被註冊。該提案處於第二階段,正在受到JavaScript
社群的廣泛關注。如果被採納,它將為Symbol
的應用帶來更多的靈活性。
function isWeakMapKey(key) { switch (typeof key) { case "object": return key !== null; case "function": return true; case "symbol": return !Symbol.isRegistered(sym); } return false; } isWeakMapKey({}); // true isWeakMapKey(Symbol()); // true isWeakMapKey("foo"); // false isWeakMapKey(Symbol.for("foo")); // false isWeakMapKey(Symbol.asyncIterator); // true
您還可以檢查是否獲得了真正唯一的Symbol
:
const isUniqueSymbol = sym => typeof sym === "symbol" && !(Symbol.isRegistered(sym) || Symbol.isWellKnown(sym)); isUniqueSymbol(Symbol()); // true isUniqueSymbol(Symbol.for("foo")); // false isUniqueSymbol(Symbol.asyncIterator); // false isUniqueSymbol({}); // false
本文介紹了JavaScript
中的Symbol
型別,包括Symbol
的建立、使用場景以及實現原理。Symbol
是一種新的基本資料型別,用於表示唯一識別符號。與字串和數位不同,Symbol
值是唯一的,不可修改和可列舉的。Symbol
的主要用途包括:定義唯一屬性鍵、定義私有屬性、內建Symbol
和註冊全域性Symbol
。
此外,文章還介紹了兩個關於Symbol
的提案:Symbols as WeakMap keys
和Symbol Predicates Proposal
。這些提案旨在擴充套件Symbol
的功能和用途,併為JavaScript
開發人員提供更多的選項。
總之,Symbol
為JavaScript
新增了一個新的基本資料型別,為開發人員提供了一種新的表示唯一識別符號的方式,可以用於建立唯一屬性鍵、定義私有屬性、內建Symbol
和註冊全域性Symbol
等用途。
在實際專案中,如果遇到定義一個唯一的key
的場景,就可以考慮使用 Symbol
來完成,可以避免衝突。
以上就是vscode工具函數Symbol使用深入解析的詳細內容,更多關於vscode工具函數Symbol的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45