‘编译’原理
js是属于动态类语言,更多的时候是解释执行。不过如果仔细研究,会发现js引擎在处理js前其实有个完整的短暂生命周期。
因为这个‘编译’不是提前,而是在执行前的几秒,所以属于动态类语言,寄生在引擎上。但是真正执行的代码,也是编译过的。
程序是给人读的,偶尔才给计算机读一下(出自《高性能javascript》)
例:
|
|
上面这个错误的end() 方法,只有在使用end方法的时候才去执行编译,所以如果不使用end方法,那js不会报错。
这就是动态语言的一些基本概念,如果有兴趣可以参考java做对比分析。
java是靠虚拟机来连接人和机器语言的沟通
编译一般分以下几个步骤
- 分词/词法分析
- 解析/语法分析
- 代码生成
当然js的‘编译’,更有些语言所带的特性,比如优化性能。而这基础,就像上面代码一样,作用域在里面的作用非常重要。
作用域
全局作用域
所以根据以上,我们全局作用域下千万要保持干净,因为如果全局下有代码报错,那整个js就再也执行不下去了,因为一开始js引擎就执行全局的‘编译’,错了,就象上文一样,直接执行阻断。
全局作用域指的就是window对象(node指的是global)
一般前端工程师会被要求,不要过多的污染全局,大多的说法是说会引起命名冲突,这里很多人忽略了性能泄漏的问题。
|
|
我借用下《你不知道的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
|
|
把全局变量赋值到局部变量后再运行 good
|
|
分析作用域
js发展到现在,es6规范中,有外部引入js的概念,特别是node开发,可以把这个全局理解为当前js作用域里面的顶级对象。这就需要我们开发者保持顶级对象的干净,也需要对变量的细化。
但是变量多了,也会引起书写的各种不便。这个前后天平就需要开发者自己把控。
而为什么会在向上问询(索引),会消耗如此多的性能,就涉及到了堆栈的概念
而在开头,为什么会出现下面的对话
作用域show:谢谢引擎了,不过还得麻烦你,我这里被询问了这个值是多少,麻烦再去帮我问下
js引擎:好的,我问下 …… window 说是2
就涉及到了RHS和LHS的概念,也同时涵盖到了堆栈,有兴趣的可以自己查阅文档。
有空,我会在下一个文章里说明这些东西。