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

标签 javascript language-design low-level value-type reference-type

官方文档以及互联网上的大量文章都说'some string'是一个原始值,这意味着每次我们将它分配给一个变量时它都会创建一个副本。

但是,这个问题(及其答案)How to force JavaScript to deep copy a string?证明实际上 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
enter image description here

最后,经过检查

let copy = someStringInitializedAbove

开发工具 很明显,该字符串的新副本尚未创建!

所以我很确定在分配时不会复制字符串。但是我仍然不明白为什么这么多像 JS Primitives vs Reference 这样的文章说它们是。

最佳答案

从根本上说,因为 the specification says so :

string value

primitive value that is a finite ordered sequence of zero or more 16-bit unsigned integer values



该规范还定义了 String 对象,与原始字符串不同。 (同样有原始的 numberbooleansymbol 类型,以及 Number 和 Boolean 和 Symbol 对象。)

原语字符串遵循其他原语的所有规则。在语言级别上,它们的处理方式与原始数字和 bool 值完全相同。出于所有意图和目的,它们都是原始值。但正如你所说,这对 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 上的 react 很合理),但我怀疑等价运算符的定义( =====!=!== )没有t 必须要么是可以被对象类型重载以用于其自身目的的东西,要么是字符串的特殊情况。

那么为什么要有字符串对象呢?拥有 String 对象(以及 Number 对象、Boolean 对象和 Symbol 对象)以及说明何时创建原语的临时对象版本的规则使得在原语上定义方法成为可能。当你这样做时:
console.log("example".toUpperCase());

在规范方面,创建一个 String 对象(由 GetValue operation ),然后是属性 toUpperCase在该对象上查找并(在上面)调用。因此,原始字符串得到它们的 toUpperCase (和其他标准方法)来自 String.prototypeObject.prototype .但是,除非在某些边缘情况下,否则代码无法访问创建的临时对象,并且 JavaScript 引擎可以避免在这些边缘情况之外创建对象。 的优势|那是新方法可以添加到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 引擎被迫创建 String 对象,因为 this在松散模式下不能是原语。

严格模式可以避免创建对象,因为在严格模式下 this不需要是对象类型,它可以是原始类型(在本例中为原始字符串):

"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

关于javascript - 为什么字符串文字在 JavaScript 中被视为原始类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61427733/

相关文章:

javascript - Selenium 与 JavaScript 和 Nodejs : How to count elements on page?

c - 3D数组如何存储在C中?

java - 在低级别对 Hadoop 作业进行基准测试

programming-languages - 哪些编程语言可以让我操纵方法中的指令序列?

javascript - JQuery 无法在单独的 .js 文件中工作

javascript - 如何用php重定向到主页

javascript - 操作 innerHTML 删除子元素的事件处理程序?

assembly - COBOL 是否强制一行的前 6 个字符为数字,因为它更容易编译为二进制可执行文件?

Java 语言设计与 toString

c++ - 异常处理和强制