JS执行机制

103 阅读3分钟

JS在编译代码的时候,会有一个预编译的阶段,变量提升之类的就发生在这个阶段。

预编译的时候,代码会被分成两部分:执行上下文、可执行代码。
可执行上下文有全局执行上下文、函数执行上下文、eval执行上下文。

执行上下文里面有四个对象(应该是对象),分别是变量环境、词法环境、outer、this可执行代码大部分是对函数的调用、变量的操作等等。

一个一个来说:
变量环境保存提升的变量(不是有个现象叫做变量提升),还有提升的函数。
词法环境由代码结构决定。它保存有通过let、const定义的变量。这也是块级作用域能够实现的原因。
outer指向作用域链中,上一级作用域。
this不用多说,我所理解的是指向当前对象的一个引用。

多说几句,变量提升,提升的是变量名,在定义变量之前获取变量,得到的是 `undefined`。
函数提升(这个词我瞎掰的),即使在函数定义前调用函数,也可以正常使用。 

关于为什么会有栈溢出这个问题。

我们上面说到,预编译的时候,代码分成执行上下文、可执行代码。
执行上下文创建完成之后,JS引擎会把它压入栈中,这个栈被称为执行上下文栈,又叫 调用栈
当JS引擎编译可执行代码的时候,遇到新的执行上下文就会把它压入栈中,如果执行完毕,就会把这个执行上下文弹出,是有这样一个压入弹出的过程。
但是,栈满则溢,它的容量有限,所以也不能无节制的压入,这也是一些没有结束条件的递归让栈满的原因。

怎么解决这个问题呢?

可以用Web APIs来解决,它是浏览器提供的一些接口,具体可以参考这个链接:Web API 接口参考
这些接口包括DOM APIsetTimeoutHTTP请求等等。这些API可以帮助我们创建一些异步的,非阻塞的任务
这就涉及到了JS的事件循环,可以查看我的这篇文章了解更多:由浅入深彻底理解JS事件循环Event Loop

代码中出现相同的变量怎么办?

这就涉及到了作用域链,执行上下文中的outer对象来控制执行上下文中的上级作用域,当这些作用域串联在一起就形成了作用域链。
作用域链由词法环境决定,词法环境由代码结构决定。
查找变量的时候,他会按照当前作用域【词法环境-->变量环境】-->上级作用域【词法环境-->变量环境】这种顺序来查找。

关于闭包。

内层函数调用了定义在外层函数的变量的时候,就形成了闭包。应该可以理解成调用的这些变量的集合,也可以理解成调用外层变量的内层函数是闭包。
这个定义各家不一,但是指的都是一个事情,重在会用~

关于this

this跟作用域链没有一点关系。
还有两点需要注意: 作为对象的方法调用的时候,this指向这个对象。
嵌套在函数里的函数,就是内层函数不会继承外层函数的this。