在技术采访中的另一天,问题之一是“你如何优化Javascript代码”?
令我惊讶的是,他告诉我,虽然循环通常比循环更快。
这是真的吗?如果是,那为什么呢?
答案 0 :(得分:32)
你应该反驳一个负面的while循环会更快!请参阅:JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing。
在while和for之间,这两个来源通过在不同浏览器中运行各种循环并以毫秒为单位比较结果来很好地记录速度现象: https://blogs.oracle.com/greimer/entry/best_way_to_code_a和: http://www.stoimen.com/blog/2012/01/24/javascript-performance-for-vs-while/
从概念上讲,for循环基本上是一个封装的while循环,专门用于递增或递减(根据某种顺序或某个长度在逻辑上前进)。例如,
for(var k=0; ++k; k< 20){ ... }
可以通过使其成为负循环来加速:
var k = 20;
while(--k){ ... };
正如您从上面链接中的测量结果中看到的那样,节省的时间确实会增加非常大的数字。
答案 1 :(得分:15)
虽然这是对速度和效率的微小检测的一个很好的答案,但我不得不离开@Pointy原始声明。
正确的答案是,它通常毫无意义 担心这样的细节,因为你付出了任何努力 下次登记时,优化可能会完全浪费 到V8或SpiderMonkey
由于Javascript是客户端确定的,并且最初必须针对每个浏览器进行编码以实现完全跨浏览器兼容性(在ECMA甚至涉及之前更糟糕),速度差异在此时可能甚至不是合乎逻辑的答案,因为Javascript在浏览器及其编译器引擎上的重要优化和采用。
我们甚至没有谈论非严格的脚本只能编写诸如GAS中的应用程序,因此虽然答案和问题很有趣,但它们很可能在实际应用程序中比实用更简单。
要阐述这个主题,首先需要了解这个主题最初的来源和编译与解释。让我们简要介绍语言的演变历程,然后再回到编译与解释。虽然不需要阅读,但您可以阅读Compiling vs Interpeting以获得快速答案,但为了深入理解,我建议您阅读编译与解释和编程的演变(显示它们如何应用于今天)。
编译语言编码是一种编程方法,您可以用编译器理解的可编译方式编写代码,今天一些比较公认的语言是Java,C ++和C#。编写这些语言的目的是编译器程序然后将代码转换为目标机器使用的机器代码或字节码。
解释代码
是执行时没有编译的 J ust I n T ime(JIT)的代码,它会跳过此步骤,允许更快地编写,调试,添加/更改等。它也将永远不会存储脚本的解释以供将来使用,它将在每次调用方法时重新解释脚本。解释的代码在定义的和预期的程序运行时环境(对于javascript通常是浏览器)中运行,然后由环境解释然后输出到期望的结果。解释过的脚本永远不会成为独立的软件,并且总是希望插入一个有效的运行时环境来进行解释。这就是脚本不可执行的原因。他们永远不会直接与操作系统通信。如果您查看发生的系统进程,您将永远不会看到正在处理您的脚本,而是看到正在处理的程序正在其运行时环境中处理您的脚本。
因此,在Javascript中编写一个hello脚本意味着浏览器会解释代码,定义hello是什么,当发生这种情况时,浏览器会将此代码转换回机器级代码,说我有这个脚本,而我的环境想要显示单词hello,然后机器将其处理成脚本的可视化表示。这是一个持续的过程,这就是为什么在计算机中有处理器以及在系统上进行处理的持续动作的原因。没有什么是静态的,无论情况如何,都会不断地执行流程。
<强>编译器强>
通常将代码编译为定义的字节码系统或机器代码语言,现在是代码的静态版本。除非重新编译源代码,否则机器不会重新解释它。这就是为什么在编译后会看到运行时错误的原因,然后程序员必须在源代码中调试并重新编译。解释器预期的脚本(如Javascript或PHP)只是在运行之前未编译的指令,因此可以轻松编辑和修复源代码,而无需额外的编译步骤,因为编译是实时完成的。
并非所有已编译的代码都是平等的
说明这一点的简单方法是视频游戏系统。 Playstation与Xbox。构建Xbox系统以支持.net框架以优化编码和开发。 C#将此框架与公共语言运行时结合使用,以便将代码编译为字节码。字节码不是对编译代码的严格定义,它是一个中间步骤,允许在程序中更快地编写代码,然后在运行时使用代码执行时解释代码。猜对了, J 我我 n T ime(JIT)。不同之处在于此代码仅被解释一次,一旦编译,程序将不再重新解释该代码,除非重新启动。
解释的脚本语言永远不会编译代码,因此解释脚本中的函数不断被重新处理,而编译的字节码函数被解释一次并且指令被存储直到程序的运行时停了好处是,只要您拥有必要的资源,字节码就可以移植到另一台机器的架构中。这就是为什么你必须安装.net以及可能的更新和框架到你的系统,以使程序正常工作。
Playstation不为其机器使用.net框架。您需要使用C ++编写代码,C ++应针对特定的系统体系结构进行编译和汇编。代码将永远不会被解释,并且需要完全正确才能运行。您永远不能像使用中间语言那样轻松移动此类型语言。它是专门为该机器的架构而制作的,绝不会以其他方式解释。
所以你看到即使编译语言也不是编译语言的固有版本。编译语言的严格定义是完全编译使用。解释语言应该由程序解释,但也是编程中最便携的语言,因为只需要安装了理解脚本的程序,但由于不断被解释,它们也使用最多的资源。中间语言(例如Java和C#)是这两种语言的混合体,部分编译但也需要外部资源才能保持功能。一旦运行,然后再次编译,这是在运行时的一次解释。
机器代码
编码的最低形式,这个代码在其表示中是严格二进制的(我不会进入三元计算,因为它是基于理论和实际应用的讨论)。计算机了解自然值,开/关真/假。这是机器级数字代码,它与下一级汇编代码不同。
汇编代码
直接的下一级代码是汇编语言。这是语言被解释为由机器使用的第一个点。此代码用于解释随后以机器级代码发送到计算机的助记符,符号和操作数。这一点很重要,因为当你第一次开始编程时,大多数人会做出假设, this 或 意味着我编译或解释。 除了低级别机器代码之外,没有编码语言可以只编译指令或仅解释说明!!!
我们在&#34; Not Not Compiled Code is Created Equal&#34;中进行了检查。汇编语言是第一个这样的例子。机器代码是机器读取的内容,但汇编语言是人类可以阅读的内容。随着计算机处理速度的加快,通过更好的技术进步,我们的低级语言开始变得更加精简,不需要手动实现。汇编语言曾经是高级编码语言,因为它是编码机器的更快捷的方法。它本质上是一种语法语言,一旦组装(最低版本的编译)直接转换为机器语言。 汇编程序是编译器,但并非所有编译器都是汇编程序。
高级编码
高级编码语言是比汇编高一级的语言,但甚至可能包含更高级别的语言(这将是字节码/中间语言)。这些语言从定义的语法结构编译成所需的机器代码,要解释的字节码或前一种方法的混合,以及允许内联编写汇编的特殊编译器。像它的前身Assembly那样的高级编码意味着减少开发人员的工作量并消除冗余任务中的关键错误的任何机会,例如构建可执行程序。在今天的世界中,你很少会看到一个开发人员在汇编中工作,只是为了大小的好处来处理数据。比开发人员更常出现这种情况,例如在视频游戏控制台开发中,他们需要在此过程中提高速度。因为高级编码编译器是寻求简化开发过程的工具,所以它们可能不会100%以最有效的方式为该系统架构编译代码。在这种情况下,将编写汇编代码以最大化系统的资源。但你永远不会看到一个人用机器码编写,除非你遇到一个怪人。
如果你做到这一点,恭喜你!你只是一次性地听了一遍,而不是我的妻子,关于这些东西,一辈子。 OP的问题是关于while和for循环的性能。这是今天标准中没有实际意义的原因是双重的。
原因一
解释Javascript的日子已经一去不复返了。所有主流浏览器(是的,甚至是Opera和Netscape)都使用Javascript引擎来编译脚本,然后再实现它。 JS开发人员在非调用方法方面讨论的性能调整是在查看语言中的本机函数时过时的研究方法。在成为DOM的一部分之前,代码已经针对该代码进行了编译和优化。当该页面启动时,它不再被解释,因为该页面是运行时环境。 Javascript实际上已成为一种中间语言,而不是解释脚本。它永远不会被称为中间脚本语言的原因是因为Javascript永远不会被编译。这是唯一的原因。除此之外,它在浏览器环境中的作用是Bytecode所发生的缩小版本。
原因二 您编写脚本或脚本库的机会几乎是不可能的,这些脚本或脚本库需要与网站上的桌面应用程序一样多的处理能力。为什么?因为Javascript从未创建过,意图成为一种包罗万象的语言。它的创建只是提供一种中等水平的语言编程方法,允许完成HTML和CSS无法提供的流程,同时减轻了需要专用高级编码语言(特别是Java)的开发难度。 。
大多数早期的Web开发都不支持CSS和JS。直到1997年,CSS并不是一个安全的集成,而且JS的战斗时间更长。除HTML之外的所有内容都是网络世界中的补充语言。
HTML特定于作为网站的构建块。你永远不会写javascript来完全构建一个网站。最多你做DOM操作但是建立一个网站。
您永远不会在JS中设置您的网站样式,因为它不实用。 CSS处理该过程。
除了暂时使用Javascript之外,您永远不会存储。您使用的是数据库。
那么我们还剩下什么呢?越来越多的功能和流程。 CSS3及其未来的迭代将采用Javascript的所有样式方法。你看到已经有动画和伪状态(悬停,活动等)。
此时Javascript中代码优化的唯一有效论据是,可以通过优化用户的公式/代码模式来帮助编写错误的函数,方法和操作。只要您学习正确有效的编码模式,Javascript在今天的时代就不会失去其本机功能。
答案 2 :(得分:2)
for(var k=0; ++k; k< 20){ ... }
可以通过使其成为负面来加速 while loop:
var k = 20; while(--k){ ... };
更准确的测试是将用于,与同时使用。唯一的区别是使用for循环提供了更多描述。如果我们想要超级疯狂,我们可以放弃整个街区;
var k = 0;
for(;;){doStuff till break}
//or we could do everything
for (var i=1, d=i*2, f=Math.pow(d, i); f < 1E9; i++, d=i*2, f=Math.pow(d,i)){console.log(f)}
无论哪种方式......在NodeJS v0.10.38中我在一秒钟内处理一个10 9 的JavaScript循环,其中 的平均速度提高了约13% 。但是这对我将来决定使用哪个循环或我选择在循环中描述的数量没有影响。
> t=Date.now();i=1E9;
> while(i){--i;b=i+1}console.log(Date.now()-t);
292
> t=Date.now();i=1E9;
> while(--i){b=i+1}console.log(Date.now()-t);
285
> t=Date.now();i=1E9;
> for(;i>0;--i){b=i+1}console.log(Date.now()-t);
265
> t=Date.now();i=1E9;
> for(;i>0;){--i;b=i+1}console.log(Date.now()-t);
246
答案 3 :(得分:2)
在JavaScript中,反向循环是最快的。 for循环比<循环更快 。更专注于可读性。
以下测试循环:
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
//here goes library projects dependencies, which you must include
//in yours build.gradle(modile app) too
...
}
答案 4 :(得分:1)
2017答案
jsperf for vs foreach on Chrome 59
在这里你可以看到自编写日期(17/17/17)以来,最新版Chrome(59)上的Array.forEach变得最快。您可以在此处找到其他浏览器版本的平均时间:https://jsperf.com/for-vs-foreach/66。
这表明ES引擎优化可以随时改变效率。
我的建议是你使用对你的用例更具表现力的那个。
由于计算机在摩尔定律的指数下变得更快,因此在相同幅度内的性能差异将在未来变得无关紧要。