JavaScript中的字符串原语和String对象有什么区别?

The*_*pha 106 javascript string object

取自MDN

字符串文字(用双引号或单引号表示)和从非构造函数上下文中的String调用返回的字符串(即,不使用new关键字)是原始字符串.JavaScript会自动将基元转换为String对象,因此可以对原始字符串使用String对象方法.在要在原始字符串上调用方法或发生属性查找的上下文中,JavaScript将自动包装字符串原语并调用方法或执行属性查找.

因此,我认为(逻辑上)字符串基元上的操作(方法调用)应该比字符串对象上的操作慢,因为任何字符串基元method在应用于字符串之前都会转换为字符串对象(额外工作).

但在这个测试案例中,结果却相反.所述码块1的运行速度比较快的块-2代码,两个代码块在下面给出:

代码块-1:

var s = '0123456789';
for (var i = 0; i < s.length; i++) {
  s.charAt(i);
}
Run Code Online (Sandbox Code Playgroud)

代码块-2:

var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
    s.charAt(i);
}
Run Code Online (Sandbox Code Playgroud)

结果在浏览器中有所不同,但代码块-1总是更快.任何人都可以解释一下,为什么代码块-1代码块-2快.

fla*_*ian 139

JavaScript有两个主要类型类别,即原始文件和对象.

var s = 'test';
var ss = new String('test');
Run Code Online (Sandbox Code Playgroud)

单引号/双引号模式在功能方面是相同的.除此之外,您尝试命名的行为称为自动装箱.所以实际发生的是,当调用包装类型的方法时,将基元转换为其包装类型.简单来说:

var s = 'test';
Run Code Online (Sandbox Code Playgroud)

是原始数据类型.它没有方法,只不过是指向原始数据存储器引用的指针,它解释了更快的随机访问速度.

那么当你这样做时会发生什么s.charAt(i)

由于s不是一个实例String,JavaScript就自动箱s,里面有typeof string它的包装类型,Stringtypeof object或更精确地s.valueOf(s).prototype.toString.call = [object String].

自动装箱行为s根据需要来回转换为其包装类型,但标准操作非常快,因为您处理的是更简单的数据类型.然而自动拳击和Object.prototype.valueOf具有不同的效果.

如果要强制自动装箱或将基元强制转换为其包装类型,则可以使用Object.prototype.valueOf,但行为不同.基于各种测试场景,自动装箱仅应用"必需"方法,而不改变变量的原始性质.这就是为什么你获得更好的速度.


Fab*_*tté 33

这是依赖于实现的,但我会采取行动.我将举例说明V8,但我认为其他引擎使用类似的方法.

字符串基元被解析为v8::String对象.因此,如jfriend00所述,可以直接在其上调用方法.

另一方面,String对象被解析为一个v8::StringObject扩展的Object对象,除了是一个完整的对象之外,它还可以作为包装器v8::String.

现在,它仅仅是逻辑上,呼叫到new String('').method()具有拆箱本v8::StringObjectv8::String执行该方法之前,因此它是较慢的.


在许多其他语言中,原始值没有方法.

MDN的方式似乎是解释原语自动装箱如何工作的最简单方法(也在黄牌的答案中也有提到),也就是JavaScript的原始y值如何调用方法.

但是,每次需要调用方法时,智能引擎都不会将字符串primitive-y转换为String对象.这也在Annotated ES5规范中提供了信息.关于解析原始值的属性(和"方法"¹):

注意在上述方法之外无法访问可能在步骤1中创建的对象.实现可能会选择避免实际创建对象.[...]

在非常低的级别,字符串通常被实现为不可变的标量值.示例包装结构:

StringObject > String (> ...) > char[]
Run Code Online (Sandbox Code Playgroud)

你离原始的距离越远,就越需要它.在实践中,String原语是不是更加频繁StringObjectS,所以它不是一个惊喜引擎方法添加到字符串原语对应的(解释)对象的类,而不是之间来回转换的String,并StringObject为MDN的解释说明.


¹在JavaScript中,"method"只是属性的命名约定,它解析为函数类型的值.


ref*_*tor 15

在字符串文字的情况下,我们无法分配属性

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined
Run Code Online (Sandbox Code Playgroud)

而在String Object的情况下,我们可以分配属性

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world
Run Code Online (Sandbox Code Playgroud)

  • 为什么会有人需要这样做? (9认同)
  • 最后有人提出了为什么我们也需要“String”对象。谢谢你! (2认同)

小智 10

字符串字面量:

字符串文字是不可变的,这意味着,一旦创建它们,它们的状态就不能被改变,这也使它们的线程安全.

var a = 's';
var b = 's';
Run Code Online (Sandbox Code Playgroud)

a==b 结果将为'true'两个字符串引用相同的对象.

字符串对象:

这里创建了两个不同的对象,它们有不同的引用:

var a = new String("s");
var b = new String("s");
Run Code Online (Sandbox Code Playgroud)

a==b 结果将为false,因为它们具有不同的引用.


Pau*_* S. 9

如果您使用new,则明确声明您要创建Object的实例.因此,new String正在生成一个包装String原语的Object,这意味着对它的任何操作都涉及额外的工作层.

typeof new String(); // "object"
typeof '';           // "string"
Run Code Online (Sandbox Code Playgroud)

由于它们属于不同类型,因此您的JavaScript解释器也可能以不同方式对它们进行优化,如注释中所述.


jfr*_*d00 5

当你声明:

var s = '0123456789';
Run Code Online (Sandbox Code Playgroud)

你创建一个字符串基元.该字符串原语具有允许您在其上调用方法而无需将原语转换为第一类对象的方法.所以你的假设是因为字符串必须转换为对象而这会更慢是不正确的.它不必转换为对象.原语本身可以调用方法.

将它转换为一个完整的对象(允许你向它添加新属性)是一个额外的步骤,并不会使字符串更快(事实上你的测试显示它使它们变慢).

  • @SheikhHeera - 原语构建在语言实现中,因此解释器可以赋予它们特殊的权力. (2认同)

lua*_*ped 5

我可以看到这个问题很久以前就已经解决了,字符串文字和字符串对象之间还有另一个微妙的区别,因为似乎没有人触及它,我想我只是为了完整性而写它。

基本上,两者之间的另一个区别是使用 eval 时。eval('1 + 1') 给出 2,而 eval(new String('1 + 1')) 给出 '1 + 1',因此如果某些代码块可以“正常”执行或使用 eval 执行,则可以导致奇怪的结果