<JavaScript深入> 执行上下文栈
顺序执行?
如果要问到 JavaScript 代码执行顺序的话,想必写过 JavaScript 的开发者都会有个直观的印象,那就是顺序执行,毕竟:
var foo = function () { console.log('foo1');}
foo(); // foo1
var foo = function () { console.log('foo2') }
foo(); // foo2
实际上执行结果是这样的
var foo = function () { console.log('foo1');}
foo(); // foo2
var foo = function () { console.log('foo2') }
foo(); // foo2
刷过面试题的都知道这是因为 JavaScript 引擎并非一行一行地分析和执行程序,而是一段一段地分析执行。当执行一段代码的时候,会进行一个“准备工作”,比如第一个例子中的变量提升,和第二个例子中的函数提升。
可执行代码
这就要说到 JavaScript 的可执行代码(executable code)的类型有哪些了?
其实很简单,就三种,全局代码、函数代码、eval代码[1]。
举个例子,当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做"执行上下文(execution contexts)"。
执行上下文栈
function fun3() {
console.log('fun3')
}
function fun2() {
fun3();
}
function fun1() {
fun2();
}
fun1();
当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。
压入堆栈
[fun1] ->
[fun2,fun1] ->
[fun3,fun2,fun1]
然后依次执行
fun3 -> fun2 -> fun1
执行完后,清空堆栈
和其他解释性语言一样,javascript 同样可以解释运行由javascript源代码组成的字符串,javascript 通过eval()来完成。eval() 其实是一个函数,是javascript很早版本中就已经存在,但是javascript设计者和解释器对其进行了许多的限制,看起来eval更像一个运算符。但是作为用于动态执行代码,eval存在这样的问题,解释器无法对动态代码做进一步的优化。Eval 只有一个参数,如果传入的不是字符串的话,它直接返回这个参数;如果是字符串,那么首先会进行编译,如编译失败则爆出语法错误;编译成功则执行代码。更主要的是eval可以使用调用它的变量作用域环境,也就是说它查找的变量或者新增的变量作用域跟代码的完全一样。eval 可以动态执行代码,并申明或者改变变量。 ↩︎