JavaScript中的string文字和string对象有什么区别?
采取MDN
在非构造函数上下文中(即,不使用new关键字)从String调用返回的string文字(用双引号或单引号表示)和string是原始string。 JavaScript会自动将基元转换为string对象,以便可以为基元string使用string对象方法。 在一个方法被调用的原始string或属性查找的上下文中,JavaScript将自动包装string原语并调用方法或执行属性查找。
所以,我认为(逻辑上)对string文字的操作(方法调用)应该比对string对象的操作要慢,因为在将string应用到method
之前,任何string文字都被转换为string对象(额外工作)。
但是在这个testing用例中 ,结果是相反的。 代码块1的运行速度比代码块2的快,两个代码块在下面给出:
代码块-1:
var s = '0123456789'; for (var i = 0; i < s.length; i++) { s.charAt(i); }
代码块2:
var s = new String('0123456789'); for (var i = 0; i < s.length; i++) { s.charAt(i); }
结果在浏览器中有所不同,但代码块1总是更快。 任何人都可以请解释一下,为什么代码块1比代码块2更快。
JavaScript有两个主要types类别,即原始对象和对象。
var s = 'test'; var ss = new String('test');
单引号/双引号模式在function上是相同的。 除此之外,你试图命名的行为被称为自动装箱。 所以实际上发生的是当一个包装types的方法被调用时,一个原语被转换为它的包装types。 简单的说:
var s = 'test';
是一种原始数据types。 它没有方法,它只不过是指向原始数据存储器引用的指针,它解释了更快的随机访问速度。
那么当你做s.charAt(i)
时会发生什么?
由于s
不是String
一个实例,所以JavaScript会自动将s
s.valueOf(s).prototype.toString.call = [object String]
给它的包装typesString
,其中包含typeof object
,更确切的说是s.valueOf(s).prototype.toString.call = [object String]
。
自动装箱行为根据需要来回转换为其包装types,但是由于您正在处理更简单的数据types,所以标准操作的速度非常快。 但是自动装箱和Object.prototype.valueOf
有不同的效果。
如果你想强制自动装箱或者将一个图元转换为包装types,你可以使用Object.prototype.valueOf
,但是行为是不同的。 基于各种各样的testing场景,自动装箱只应用“必需的”方法,而不改变variables的原始性质。 这就是为什么你获得更好的速度。
这是相当实施,但我会拍。 我将以V8为例,但我假设其他引擎使用类似的方法。
一个string原语被parsing为一个v8::String
对象。 因此,可以像jfriend00所提到的那样直接调用方法。
另一方面,String对象被parsing为一个扩展了Object
的v8::StringObject
,除了是一个完整的对象之外, v8::StringObject
可以作为v8::String
的包装器。
现在只是合乎逻辑的,在执行方法之前,调用new String('').method()
必须取消这个v8::StringObject
的v8::String
,因此速度较慢。
在许多其他语言中,原始值没有方法。
MDN的方式似乎是解释原语的自动装箱function的最简单方法(也就是在答案中提到的),也就是说,JavaScript的原始值可以如何调用方法。
但是,每次需要调用方法时,智能引擎都不会将stringprimitive-y转换为String对象。 这也在注释的ES5规范中提到。 关于解决原始价值的性质(和“方法”¹):
注意步骤1中可能创build的对象不能在以上方法之外访问。 实现可能会select避免实际创build对象。 […]
在非常低的层次上,string通常被实现为不可变的标量值。 示例包装结构:
StringObject > String (> ...) > char[]
你离原始的距离越远,所需的时间就越长。 实际上, String
基元比StringObject
更为频繁,因此引擎将方法添加到String基元的相应(解释的)对象的Class中并不StringObject
,而StringObject
像MDN的解释所暗示的那样在String
和StringObject
之间来回转换。
¹在JavaScript中,“method”只是一个属性的命名约定,该属性可以parsing为函数types的值。
如果使用new
,则明确指出要创buildObject的实例。 因此, new String
产生了一个封装了String原语的对象 ,这意味着它的任何操作都涉及到额外的工作层。
typeof new String(); // "object" typeof ''; // "string"
由于它们的types不同,因此您的JavaScript解释器也可能会对它们进行不同的优化, 正如评论中所述 。
string字面量:
string文字是不可变的,这意味着一旦创build了它们的状态就不能改变,这也使得它们是线程安全的。
var a = 's'; var b = 's';
a==b
结果将是'true',这两个string都是引用同一个对象。
string对象:
这里创build了两个不同的对象,它们具有不同的引用:
var a = new String("s"); var b = new String("s");
a==b
结果将是错误的,因为它们具有不同的引用。
在string文字的情况下,我们不能分配属性
var x = "hello" ; xy = "world"; console.log(xy); // this will print undefined
而在string对象的情况下,我们可以分配属性
var x = new String("hello"); xy = "world"; console.log(xy); // this will print world
当你声明:
var s = '0123456789';
你创build一个string原语。 这个string原始的方法可以让你调用方法,而不需要把原语转换成第一个类对象。 所以你的假设,这将是慢的,因为string已被转换为一个对象是不正确的。 它不必转换成一个对象。 基元本身可以调用这些方法。
把它转换成一个完整的对象(允许你添加新的属性)是一个额外的步骤,并不会使string操作更快(实际上你的testing显示它使得它们变慢)。
在ECMAScript / JavaScript引擎中,对象的存在与string的实际行为没有多大关系,因为根作用域将仅包含用于此的函数对象。 所以charAt(int)函数在string文字的情况下将被search并执行。
使用一个真实的对象,你可以在标准行为开始之前添加一个charAt(int)方法在对象本身上进行search的层(与上面相同)。 显然在这种情况下完成了大量的工作。
顺便说一句,我不认为原语实际上被转换成对象,但脚本引擎将简单地标记为字符串types的variables,因此它可以find所有提供的function,因此它看起来像你调用一个对象。 不要忘记这是一个脚本运行时,它运行在不同于OO运行时的原理上。