繁体   English   中英

为什么字符串文字在 JavaScript 中被视为原始类型?

[英]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 对象,与原始字符串不同。 (同样有原始numberbooleansymbol类型,以及 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.prototypeObject.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM