JS执行 调用栈
v8解析js代码后,不会直接运行,会先编译,生成上下文后,再运行。
- 编译代码,生成全局上下文
- 进入函数后,创建运行上下文,函数运行结束后,运行上下文被销毁
- 进入eval后,创建运行上下文。
运行前,首先会将全局上下文压入调用栈。执行代码期间,如果遇到变量赋值等操作会改变一下全局上下文中的变量环境。如果遇到函数,会创建函数的运行上下文,并压入调用栈。函数内的过程和全局过程是一次递归。函数运行结束后,上下文销毁,出栈。直至全局代码运行完。
调用栈可能会爆栈。当递归次数比较多的时候,通常会上万,这时候调用栈会溢出。实验表明,即使是尾递归再当前的v8版本也会爆栈,再Safari中不会,应该是被优化成了迭代。
ES6中的箭头函数不会创建自己的执行上下文
作用域链
代码中出现相同的变量,JS引擎如何来决定用哪一个呢?
如下代码:
输出结果为: global
;
JS执行 调用栈中讲到,js的执行环境由调用栈来控制。不妨站在这个角度看下。
- 首先进入全局上下文,从头开始运行,函数定义、变量定义,变量赋值
bar()
调用,创建bar的执行上下文,变量定义、赋值foo()
调用,创建foo的执行上下文,这时候,需要打印出title
的值。- foo执行完毕,销毁上下文。bar执行完毕,销毁上下文。全局执行环境,销毁上下文。
可见,title并没有直接取调用栈最上买呢的bar的上下文中的变量环境,而是取了global的值。
这是因为调用栈中,变量环境中都有一个outer
的引用,用来指向外部执行环境,如果变量没有在当环境中找到,那么就会取外部执行环境来取。这个过程叫做作用域链,那为啥这个外部环境不是bar呢?
这是因为js的 作用域链是由词法作用域决定的,而这个词法作用域和运行时是没有任何关系的。看下面的代码:
输出 bar
之前一直知道js的作用域,但是在分析代码运行的时候,从来没有站在调用栈的角度去思考问题。