爱客仕-前端团队博客园

性能之作用域

‘编译’原理

js是属于动态类语言,更多的时候是解释执行。不过如果仔细研究,会发现js引擎在处理js前其实有个完整的短暂生命周期。

因为这个‘编译’不是提前,而是在执行前的几秒,所以属于动态类语言,寄生在引擎上。但是真正执行的代码,也是编译过的。

程序是给人读的,偶尔才给计算机读一下(出自《高性能javascript》)

例:

1
2
3
4
5
6
7
8
function show () {
console.log(2)
}
function end () {
console.log(a)
}
show()
end()

上面这个错误的end() 方法,只有在使用end方法的时候才去执行编译,所以如果不使用end方法,那js不会报错。

这就是动态语言的一些基本概念,如果有兴趣可以参考java做对比分析。

java是靠虚拟机来连接人和机器语言的沟通

编译一般分以下几个步骤

  • 分词/词法分析
  • 解析/语法分析
  • 代码生成

当然js的‘编译’,更有些语言所带的特性,比如优化性能。而这基础,就像上面代码一样,作用域在里面的作用非常重要。

作用域

全局作用域

所以根据以上,我们全局作用域下千万要保持干净,因为如果全局下有代码报错,那整个js就再也执行不下去了,因为一开始js引擎就执行全局的‘编译’,错了,就象上文一样,直接执行阻断。

全局作用域指的就是window对象(node指的是global)

一般前端工程师会被要求,不要过多的污染全局,大多的说法是说会引起命名冲突,这里很多人忽略了性能泄漏的问题。

1
2
3
4
5
6
7
8
9
10
// 全局作用域声明
var a = 2;
function start() {
// start作用域下声明
function show () {
console.log(a);
}
show()
}
start();

我借用下《你不知道的javascript》一个解释模式


作用域show:嗨,js引擎,你帮我问下谁那里有a这个声明,我要用。

js引擎:好的,我去问下作用域start

js引擎:嗨,作用域start,你有没有对a声明过,你的下属要用 作用域show

作用域show: 我没有,你问问我们的老大吧。

js引擎: window(global),你肯定有声明a吧,没有就糟糕了。

window(global): 我找找,嗯,还好我有声明了

js引擎: 好的,那我去回复了。

作用域show:谢谢引擎了,不过还得麻烦你,我这里被询问了这个值是多少,麻烦再去帮我问下

js引擎:好的,我问下 …… window 说是2

作用域show:嗯,我就去使用了


js的解释代码的逻辑就是这样,如果是传统编译性的语言,一般会在一开始就做好所有的编译工作。

那根据以上逻辑,如果全局作用域里去处理各种命名,那在js引擎在寻址到声明量的时候,js本身还不知道我所处的对象的具体位置,会多做很多次的问询。依次往上去执行,性能消耗非常明显。

按照向上问询的理论,变量的作用域理执行代码的作用域的距离越小越好。(可以理解为子节点拿父节点的东西,每次都要开口要,还不如一开始就全部拿过来)

性能测试:


每次执行都需要问全局变量的代码 bad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 全局作用域声明
var a = 2;
function start() {
// start作用域下声明
function show () {
var startTime = new Date().getTime()
for (var i = 0; i<1000000000; i++) {
a++
}
var endTime = new Date().getTime() - startTime
console.log(endTime);
}
show();
}
start();
// => endTime = 3046

把全局变量赋值到局部变量后再运行 good

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 全局作用域声明
var a = 2;
function start() {
// start作用域下声明
function show () {
var startTime = new Date().getTime()
// 变量指向全局
var b = a
for (var i = 0; i<1000000000; i++) {
b++
}
var endTime = new Date().getTime() - startTime
console.log(endTime);
}
show();
}
start();
// => endTime = 704

分析作用域

js发展到现在,es6规范中,有外部引入js的概念,特别是node开发,可以把这个全局理解为当前js作用域里面的顶级对象。这就需要我们开发者保持顶级对象的干净,也需要对变量的细化。

但是变量多了,也会引起书写的各种不便。这个前后天平就需要开发者自己把控。

而为什么会在向上问询(索引),会消耗如此多的性能,就涉及到了堆栈的概念

而在开头,为什么会出现下面的对话

作用域show:谢谢引擎了,不过还得麻烦你,我这里被询问了这个值是多少,麻烦再去帮我问下

js引擎:好的,我问下 …… window 说是2

就涉及到了RHS和LHS的概念,也同时涵盖到了堆栈,有兴趣的可以自己查阅文档。

有空,我会在下一个文章里说明这些东西。