我并不是要冒犯任何人,但这种事真的是不值得关注,恕我直言。几乎所有浏览器之间的速度差异都取决于JS引擎。例如,V8引擎非常擅长内存管理;尤其是当您将其与旧版IE的JScript引擎进行比较时。
考虑以下几点:
var closure = (function()
{
var closureVar = 'foo',
someVar = 'bar',
returnObject = {publicProp: 'foobar'};
returnObject.getClosureVar = function()
{
return closureVar;
};
return returnObject;
}());
我最后一次检查,铬实际上GC'ed someVar
,因为它不是由IIFE(由closure
引用)的返回值引用的,而这两个FF而Opera将整个功能范围保留在内存中。
在这个片段中,它并不重要,但是对于使用模块模式(AFAIK,这几乎全部是由几千行代码组成)编写的库,可以使有所作为。
总之,现代JS-引擎不仅仅是“哑巴”语法分析和执行的东西更多。正如你所说:JIT编译正在进行中,但也有很多欺骗手段要尽可能优化你的代码。很可能你发布的代码片段的写法是FF引擎喜欢。
这也是很重要的是要记住,有某种的速度战谁拥有最快的引擎Chrome和FF之间的事情。我上次查看Mozilla的Rhino引擎据说超越了Google的V8,如果今天仍然如此,我不能说...自那时以来,Google和Mozilla一直在努力开发他们的引擎...
Bottom线:存在各种浏览器之间的速度差异 - 没有人能否认这一点,但单点的差异是微不足道的:你永远不会写一个脚本,一次又一次地做一件事。重要的是整体表现。
你必须记住,JS是一个棘手的开溜的基准,也:只需打开控制台,写一些递归函数,并敲响它的100倍,在FF和铬。比较每次递归所需的时间和整体运行。然后等待几个小时,然后再试一次......有时候FF可能会排在前列,而其他时候Chrome可能会更快。我已经使用此功能试了一下:
var bench = (function()
{
var mark = {start: [new Date()],
end: [undefined]},
i = 0,
rec = function(n)
{
return +(n === 1) || rec(n%2 ? n*3+1 : n/2);
//^^ Unmaintainable, but fun code ^^\\
};
while(i++ < 100)
{//new date at start, call recursive function, new date at end of recursion
mark.start[i] = new Date();
rec(1000);
mark.end[i] = new Date();
}
mark.end[0] = new Date();//after 100 rec calls, first element of start array vs first of end array
return mark;
}());
但现在,要回你最初的问题(S):
第一关:你提供的并不完全比较,说的片段, jQuery的$.extend
方法:没有真正克隆回事,更不用说深克隆。它根本不检查循环引用,我查看过的大多数其他库都是这样做的。检查循环引用确实会减慢整个过程,但它可能会不时派上用场(下面的示例1)。部分性能差异可以通过以下事实来解释:此代码只是简单地少了,因此它需要更少的时间。其次:声明一个构造函数(类中不存在JS)和创建一个实例实际上是两个不同的事情(尽管声明一个构造函数本身就是创建一个对象的实例(一个Function
实例是准确的)如下面的例子2所示,你编写构造函数的方式可以产生一个巨大的差异,同样,这是一个泛化,并且可能不适用于某些引擎的某些使用情况:例如,V8倾向于创建所有实例单一功能的对象,即使该功能是构造的一部分 - 所以我告诉
三:遍历一个长原型链,你提到并不像你想象的那么不寻常,远离我实际上。如例3所示,你不断地遍历2或3个原型链。这不会让你放慢速度,因为它只是JS解析函数调用或解析表达式的固有方式。
最后:它可能是JIT编译的,但是说其他库不是JIT编译就是不堆栈。他们可能会再次,他们可能不会。正如我之前所说的:不同的引擎在某些任务上的表现会更好......其他可能是是FF JIT编译此代码的情况,其他引擎不会。
我明白为什么其他库不会进行JIT编译的主要原因是:检查循环引用,深层克隆功能,依赖关系(例如,由于各种原因,在所有地方都会使用extend
方法)。
例1:
var shallowCloneCircular = function(obj)
{//clone object, check for circular references
function F(){};
var clone, prop;
F.prototype = obj;
clone = new F();
for (prop in obj)
{//only copy properties, inherent to instance, rely on prototype-chain for all others
if (obj.hasOwnProperty(prop))
{//the ternary deals with circular references
clone[prop] = obj[prop] === obj ? clone : obj[prop];//if property is reference to self, make clone reference clone, not the original object!
}
}
return clone;
};
此功能克隆对象的第一级,即正由原始对象的属性引用的所有对象,仍然会共享。一个简单的解决将是只需拨打以上递归函数,但那么你就必须在各级处理循环引用的讨厌业务:
var circulars = {foo: bar};
circulars.circ1 = circulars;//simple circular reference, we can deal with this
circulars.mess = {gotcha: circulars};//circulars.mess.gotcha ==> circular reference, too
circulars.messier = {messiest: circulars.mess};//oh dear, this is hell
当然,这还不是最常见的的情况,但如果你想防守编写代码,你不得不承认,很多人写的疯狂代码所有的时间其实...
例2:
function CleanConstructor()
{};
CleanConstructor.prototype.method1 = function()
{
//do stuff...
};
var foo = new CleanConstructor(),
bar = new CleanConstructor);
console.log(foo === bar);//false, we have two separate instances
console.log(foo.method1 === bar.method1);//true: the function-object, referenced by method1 has only been created once.
//as opposed to:
function MessyConstructor()
{
this.method1 = function()
{//do stuff
};
}
var foo = new MessyConstructor(),
bar = new MessyConstructor();
console.log(foo === bar);//false, as before
console.log(foo.method1 === bar.method1);//false! for each instance, a new function object is constructed, too: bad performance!
从理论上讲,宣布第一构造函数是较慢比凌乱的方式:在创建单个实例之前创建的函数对象,由method1
引用。第二个示例不创建method1
,除了构造函数被调用时。但缺点是巨大的:在第一个例子中忘记了new
关键字,并且获得的结果是返回值undefined
。当您省略关键字new
时,第二个构造函数创建一个全局函数对象,并为每个调用创建新的函数对象。你有一个构造函数(和原型)是的,其实,怠速......这给我们带来例如3
例子3:
var foo = [];//create an array - empty
console.log(foo[123]);//logs undefined.
好了,所以在幕后发生了什么: foo
引用对象,Array
的实例,该实例继而继承Object对象原型(仅尝试Object.getPrototypeOf(Array.prototype)
)。按理说,因此,一个Array实例在几乎相同的方式与任何对象的工作,所以:
foo[123] ===> JS checks instance for property 123 (which is coerced to string BTW)
|| --> property not found @instance, check prototype (Array.prototype)
===========> Array.prototype.123 could not be found, check prototype
||
==========> Object.prototype.123: not found check prototype?
||
=======>prototype is null, return undefined
换句话说,像你描述的链条是不是太牵强或少见。这就是JS如何工作的原因,所以期望减慢速度就像期待着你的大脑因为你的想法而f然心动:是的,你可以通过思考太多而疲惫不堪,但只是知道什么时候休息一下。就像在原型链的情况下一样:它们很棒,只知道它们慢一点,是的...
导致my.class.js更快的一个方面是扩展函数不执行'hasOwnProperty'检查。 ---但是当你考虑到它每秒执行数百万次操作的事实时,jsperf测试中快速执行的库之间的差异是最小的。如果您创建了许多对象实例,那么您将会遇到与内存占用和垃圾收集相关的其他问题。 – Blaise