[英]Why is the string literal considered a primitive type in JavaScript?
官方文檔以及互聯網上的大量文章都說'some string'
是一個原始值,這意味着每次我們將它分配給一個變量時它都會創建一個副本。
但是,這個問題(及其答案) 如何強制 JavaScript 深度復制字符串? 演示實際上 V8 甚至在substr
方法上也不會復制字符串。
每次將字符串傳遞給函數時都復制字符串也是很瘋狂的,而且沒有意義。 在 C#、Java 或 Python 等語言中,String 數據類型絕對是引用類型。
此外,這個鏈接顯示了層次結構,我們畢竟可以看到 HeapObject。 https://thlorenz.com/v8-dox/build/v8-3.25.30/html/d7/da4/classv8_1_1internal_1_1_sliced_string.html
最后,經過檢查
let copy = someStringInitializedAbove
在Devtools中,很明顯尚未創建該字符串的新副本!
所以我很確定在分配時不會復制字符串。 但是我仍然不明白為什么這么多像JS Primitives vs Reference這樣的文章說它們是。
從根本上說,因為規范是這樣說的:
字符串值
原始值是零個或多個 16 位無符號 integer 值的有限有序序列
該規范還定義了 String 對象,與原始字符串不同。 (同樣有原始number
、 boolean
和symbol
類型,以及 Number 和 Boolean 和 Symbol 對象。)
原語字符串遵循其他原語的所有規則。 在語言級別上,它們的處理方式與原始數字和布爾值完全相同。 出於所有意圖和目的,它們都是原始值。 但是正如您所說,如果a = b
從字面上復制b
中的字符串並將該副本放入a
中,那將是瘋狂的。 實現不必這樣做,因為原始字符串值是不可變的(就像原始數值一樣)。 您不能更改字符串中的任何字符,只能創建一個新字符串。 如果字符串是可變的,那么當您執行a = b
時,實現將不得不制作一個副本(但如果它們是可變的,則規范會以不同的方式編寫)。
請注意,原始字符串和 String 對象確實是不同的東西:
const s = "hey"; const o = new String("hey"); // Here, the string `s` refers to is temporarily // converted to a string object so we can perform an // object operation on it (setting a property). s.foo = "bar"; // But that temporary object is never stored anywhere, // `s` still just contains the primitive, so getting // the property won't find it: console.log(s.foo); // undefined // `o` is a String object, which means it can have properties o.foo = "bar"; console.log(o.foo); // "bar"
那么為什么要有原始字符串呢? 你必須問 Brendan Eich(他在 Twitter 上的反應很合理),但我懷疑這是因為等價運算符( ==
、 ===
、 !=
和!==
)的定義沒有要么是 object 類型出於自身目的而可能重載的東西,要么是字符串的特殊情況。
那么為什么要有字符串對象呢? 擁有 String 對象(和 Number 對象,以及 Boolean 對象和 Symbol 對象)以及說明何時創建原語的臨時 object 版本的規則使得在原語上定義方法成為可能。 當你這樣做時:
console.log("example".toUpperCase());
在規范方面,創建一個字符串 object(通過GetValue 操作),然后在 object 上查找屬性toUpperCase
並(在上面)調用。 因此,原始字符串從String.prototype
和Object.prototype
獲得它們的toUpperCase
(和其他標准方法)。 但生成的臨時 object 代碼無法訪問,除非在某些邊緣情況下¹,並且 JavaScript 引擎可以避免在這些邊緣情況之外創建 object。 這樣做的好處是可以將新方法添加到String.prototype
並用於原始字符串。
¹“什么邊緣情況?” 我聽到你問。 我能想到的最常見的一種方法是當您以松散模式代碼將自己的方法添加到String.prototype
(或類似)時:
Object.defineProperty(String.prototype, "example", { value() { console.log(`typeof this: ${typeof this}`); console.log(`this instance of String: ${this instanceof String}`); }, writable: true, configurable: true }); "foo".example(); // typeof this: object // this instance of String: true
在那里,JavaScript 引擎被迫創建字符串 object,因為this
不能是松散模式下的原語。
嚴格模式可以避免創建 object,因為在嚴格模式下, this
不需要是 object 類型,它可以是原語(在這種情況下,是原語字符串):
"use strict"; Object.defineProperty(String.prototype, "example", { value() { console.log(`typeof this: ${typeof this}`); console.log(`this instance of String: ${this instanceof String}`); }, writable: true, configurable: true }); "foo".example(); // typeof this: string // this instanceof String: false
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.