string JavaScript 中的字符串基元和字符串对象有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17256182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 01:59:34  来源:igfitidea点击:

What is the difference between string primitives and String objects in JavaScript?

javascriptstringobject

提问by The Alpha

Taken from MDN

摘自MDN

String literals (denoted by double or single quotes) and strings returned from String calls in a non-constructor context (i.e., without using the new keyword) are primitive strings. JavaScript automatically converts primitives to String objects, so that it's possible to use String object methods for primitive strings. In contexts where a method is to be invoked on a primitive string or a property lookup occurs, JavaScript will automatically wrap the string primitive and call the method or perform the property lookup.

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

So, I thought (logically) operations (method calls) on string primitives should be slower than operations on string Objects because any string primitive is converted to string Object (extra work) before the methodbeing applied on the string.

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

But in this test case, the result is opposite. The code block-1runs faster than the code block-2, both code blocks are given below:

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

code block-1 :

代码块 1 :

var s = '0123456789';
for (var i = 0; i < s.length; i++) {
  s.charAt(i);
}

code block-2 :

代码块 2 :

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

The results varies in browsers but the code block-1is always faster. Can anyone please explain this, why the code block-1is faster than code block-2.

结果在浏览器中有所不同,但代码块 1总是更快。谁能解释一下,为什么代码块 1代码块 2快。

回答by flavian

JavaScript has two main type categories, primivites and objects.

JavaScript 有两个主要的类型类别,原始类型和对象。

var s = 'test';
var ss = new String('test');

The single quote/double quote patterns are identical in terms of functionality. That aside, the behaviour you are trying to name is called auto-boxing. So what actually happens is that a primitive is converted to its wrapper type when a method of the wrapper type is invoked. Put simple:

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

var s = 'test';

Is a primitive data type. It has no methods, it is nothing more than a pointer to a raw data memory reference, which explains the much faster random access speed.

是一种原始数据类型。它没有方法,它只不过是一个指向原始数据内存引用的指针,这解释了随机访问速度要快得多。

So what happens when you do s.charAt(i)for instance?

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

Since sis not an instance of String, JavaScript will auto-box s, which has typeof stringto its wrapper type, String, with typeof objector more precisely s.valueOf(s).prototype.toString.call = [object String].

由于s不是 的实例String,JavaScript 将自动装箱stypeof string其包装器类型必须为 、Stringtypeof object或更准确地说s.valueOf(s).prototype.toString.call = [object String]

The auto-boxing behaviour casts sback and forth to its wrapper type as needed, but the standard operations are incredibly fast since you are dealing with a simpler data type. However auto-boxing and Object.prototype.valueOfhave different effects.

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

If you want to force the auto-boxing or to cast a primitive to its wrapper type, you can use Object.prototype.valueOf, but the behaviour is different. Based on a wide variety of test scenarios auto-boxing only applies the 'required' methods, without altering the primitive nature of the variable. Which is why you get better speed.

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

回答by Fabrício Matté

This is rather implementation-dependent, but I'll take a shot. I'll exemplify with V8 but I assume other engines use similar approaches.

这相当依赖于实现,但我会试一试。我将以 V8 为例,但我假设其他引擎使用类似的方法。

A string primitive is parsed to a v8::Stringobject. Hence, methods can be invoked directly on it as mentioned by jfriend00.

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

A String object, in the other hand, is parsed to a v8::StringObjectwhich extends Objectand, apart from being a full fledged object, serves as a wrapper for v8::String.

另一方面,一个 String 对象被解析为一个v8::StringObject扩展,Object并且除了是一个完整的对象之外,还充当v8::String.

Now it is only logical, a call to new String('').method()has to unbox this v8::StringObject's v8::Stringbefore executing the method, hence it is slower.

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



In many other languages, primitive values do not have methods.

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

The way MDN puts it seems to be the simplest way to explain how primitives' auto-boxing works (as also mentioned in flav's answer), that is, how JavaScript's primitive-yvalues can invoke methods.

MDN 的说法似乎是解释基元的自动装箱如何工作的最简单方法(在flav的回答中也提到过),即 JavaScript 的基元 y值如何调用方法。

However, a smart engine will not convert a string primitive-yto String object every time you need to call a method. This is also informatively mentioned in the Annotated ES5 spec.with regard to resolving properties (and "methods"1) of primitive values:

但是,智能引擎不会在每次需要调用方法时将字符串原语 y转换为 String 对象。这也在Annotated ES5 规范中提供了翔实的提及关于解析原始值的属性(和“方法”1):

NOTEThe object that may be created in step 1 is not accessible outside of the above method. An implementation might choose to avoid the actual creation of the object. [...]

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

At very low level, Strings are most often implemented as immutable scalar values. Example wrapper structure:

在非常低的层次上,字符串通常被实现为不可变的标量值。示例包装器结构:

StringObject > String (> ...) > char[]

The more far you're from the primitive, the longer it will take to get to it. In practice, Stringprimitives are much more frequent than StringObjects, hence it is not a surprise for engines to add methods to the String primitives' corresponding (interpreted) objects' Class instead of converting back and forth between Stringand StringObjectas MDN's explanation suggests.

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



1 In JavaScript, "method" is just a naming convention for a property which resolves to a value of type function.

1 在 JavaScript 中,“方法”只是解析为函数类型值的属性的命名约定。

回答by refactor

In case of string literal we cannot assign properties

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

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined

Whereas in case of String Object we can assign properties

而在字符串对象的情况下,我们可以分配属性

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world

回答by Wajahat Ali Qureshi

String Literal:

字符串字面量:

String literals are immutable, which means, once they are created, their state can't be changed, which also makes them thread safe.

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

var a = 's';
var b = 's';

a==bresult will be 'true' both string refer's same object.

a==b结果将是 'true' 两个字符串都指向同一个对象。

String Object:

字符串对象:

Here, two different objects are created, and they have different references:

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

var a = new String("s");
var b = new String("s");

a==bresult will be false, because they have different references.

a==b结果将是错误的,因为它们有不同的引用。

回答by Paul S.

If you use new, you're explicitly stating that you want to create an instance of an Object. Therefore, new Stringis producing an Objectwrapping the Stringprimitive, which means any action on it involves an extra layer of work.

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

typeof new String(); // "object"
typeof '';           // "string"

As they are of different types, your JavaScriptinterpreter may also optimise them differently, as mentioned in comments.

由于它们的类型不同,您的JavaScript解释器也可能会以不同的方式优化它们,如注释中所述

回答by jfriend00

When you declare:

当您声明:

var s = '0123456789';

you create a string primitive. That string primitive has methods that let you call methods on it without converting the primitive to a first class object. So your supposition that this would be slower because the string has to be converted to an object is not correct. It does not have to be converted to an object. The primitive itself can invoke the methods.

您创建一个字符串原语。该字符串原语具有一些方法,可让您调用其上的方法,而无需将原语转换为第一类对象。因此,您认为这会变慢,因为必须将字符串转换为对象的假设是不正确的。它不必转换为对象。原语本身可以调用方法。

Converting it to an full-blown object (which allows you to add new properties to it) is an extra step and does not make the string oeprations faster (in fact your test shows that it makes them slower).

将其转换为完整的对象(允许您向其添加新属性)是一个额外的步骤,并且不会使字符串操作更快(实际上您的测试表明它使它们变慢)。

回答by luanped

I can see that this question has been resolved long ago, there is another subtle distinction between string literals and string objects, as nobody seems to have touched on it, I thought I'd just write it for completeness.

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

Basically another distinction between the two is when using eval. eval('1 + 1') gives 2, whereas eval(new String('1 + 1')) gives '1 + 1', so if certain block of code can be executed both 'normally' or with eval, it could lead to weird results

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

回答by personal_cloud

The biggest difference between a string primitive and a string object is that objects must follow this rule for the ==operator:

字符串原语和字符串对象之间的最大区别在于,对象必须遵循运算符的以下规则==

An expression comparing Objects is only true if the operands reference the same Object.

比较对象的表达式仅在操作数引用同一个对象时才为真。

So, whereas string primitives have a convenient ==that compares the value, you're out of luck when it comes to making any other immutable object type (including a string object) behave like a value type.

因此,虽然字符串原语有一个方便的==比较值的方法,但当涉及到使任何其他不可变对象类型(包括字符串对象)表现得像值类型时,你就不走运了。

"hello" == "hello"
-> true
new String("hello") == new String("hello") // beware!
-> false

(Others have noted that a string object is technically mutable because you can add properties to it. But it's not clear what that's useful for; the string value itself is not mutable.)

(其他人已经注意到字符串对象在技术上是可变的,因为您可以向它添加属性。但不清楚它的用途是什么;字符串值本身是不可变的。)

回答by clearwater

The existence of an object has little to do with the actual behaviour of a String in ECMAScript/JavaScript engines as the root scope will simply contain function objects for this. So the charAt(int) function in case of a string literal will be searched and executed.

对象的存在与 ECMAScript/JavaScript 引擎中字符串的实际行为几乎没有关系,因为根范围将仅包含为此的函数对象。所以在字符串文字的情况下 charAt(int) 函数将被搜索和执行。

With a real object you add one more layer where the charAt(int) method also are searched on the object itself before the standard behaviour kicks in (same as above). Apparently there is a surprisingly large amount of work done in this case.

对于真实的对象,您可以再添加一层,在标准行为开始之前,还会在对象本身上搜索 charAt(int) 方法(同上)。显然,在这种情况下完成了大量令人惊讶的工作。

BTW I don't think that primitives are actually converted into Objects but the script engine will simply mark this variable as string type and therefore it can find all provided functions for it so it looks like you invoke an object. Don't forget this is a script runtime which works on different principles than an OO runtime.

顺便说一句,我不认为原语实际上被转换为对象,但脚本引擎只会将此变量标记为字符串类型,因此它可以找到为其提供的所有函数,因此看起来您调用了一个对象。不要忘记这是一个脚本运行时,它的工作原理与 OO 运行时不同。

回答by Arc

The code is optimized before running by the javascript engine. In general, micro benchmarks can be misleading because compilers and interpreters rearrange, modify, remove and perform other tricks on parts of your code to make it run faster. In other words, the written code tells what is the goal but the compiler and/or runtime will decide how to achieve that goal.

代码在由 javascript 引擎运行之前进行了优化。一般而言,微基准测试可能会产生误导,因为编译器和解释器会重新排列、修改、删除部分代码并对其执行其他技巧,以使其运行得更快。换句话说,编写的代码说明目标是什么,但编译器和/或运行时将决定如何实现该目标。

Block 1 is faster mainly because of: var s = '0123456789'; is always faster than var s = new String('0123456789'); because of the overhead of object creation.

块 1 更快,主要是因为: var s = '0123456789'; 总是比 var s = new String('0123456789'); 快 因为对象创建的开销。

The loop portion is not the one causing the slowdown because the chartAt() can be inlined by the interpreter. Try removing the loop and rerun the test, you will see the speed ratio will be the same as if the loop were not removed. In other words, for these tests, the loop blocks at execution time have exactly the same bytecode/machine code.

循环部分不是导致速度变慢的部分,因为 chartAt() 可以由解释器内联。尝试移除回路并重新运行测试,您将看到速比与未移除回路相同。换句话说,对于这些测试,执行时的循环块具有完全相同的字节码/机器码。

For these types of micro benchmarks, looking at the bytecode or machine code wil provide a clearer picture.

对于这些类型的微基准测试,查看字节码或机器代码将提供更清晰的图片。