JavaScript核心概念之执行上下文和栈
桃翁桃翁,问个问题呢,据说 js 里面有个执行上下文,这个概念是个什么东东哦?据说挺重要的,给我科普科普呗。 Emm… 这个概念非常的抽象,简单来说呢,就是 JS 在执行某段代码的时候做的一些事情。 具体做的事情就是定义了变量或函数有权访问的其他数据决定了它们各自的行为(作用域链)。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中(变量包括 this、arguments)。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。 哇,还是好抽象啊,你能不能画个图举个栗子呢? 在之前说的执行上下文就是解释器在执行 JS 某段代码的时候做的一些事,那么首先我们把代码分个类。 Global 代码:代码第一次执行时默认的环境。 Function 代码:执行到一个函数中。 Eval 代码:文本在eval函数内部执行。 看到这个图相信现在分清楚各种类型的代码,每种类型代码会都会产生执行上下文,我们把 Global 代码产生的执行环境叫**「全局执行上下文」,把 Function 代码产生的执行环境叫「执行上下文」**吧,Eval 代码不考虑。 那我看这个图似乎有很多执行上下文(execution context),这个具体是怎么来的呢? 全局执行上下文只有一个,而执行环境的话是每次函数调用都会产生一个执行上下文。注意要调用才会产生哦,不调用是不会产生的。 那这个执行上下文基本知道是个什么东西了,那执行上下文栈又是啥呢? 见名知意,执行上下文栈就是执行上下文(包含全局执行上下文)形成的栈嘛。 那为什么要有这个执行上下文栈呢? 浏览器中 JavaScript 解释器是单线程的,这就是说同一时间代码只会做一件事,那么创建这么多执行上下文,又不能同一时间执行多个上下文,所以就必须要有个顺序,这个顺序就是就是先进后出,这很明显就是一个栈结构嘛。 那我就疑惑了,为啥要先进后出,不先进先出呢? 我们分析一下图一的代码,结合上图,首先我们看图 1,解释代码的时候首先创建的就是全局上下文,然后再创建 person 的执行上下文,然后再创建 firstName 的上下文,然后再执行完毕 firstName ,就把 firstName 的上下文弹出,再 创建 lastName 的上下文,然后执行完毕,再弹出 lastName 的上下文,然后执行完 person 的上下文,再弹出 person 的上下文,再执行全局上下文,然后全局上下文弹出。 如下是一张经典的执行上下文栈的图。 默认进入全局上下文。如果你的全局代码中调用了一个函数,那么程序将会进入这个被调用函数的上下文,创建一个新的执行上下文,并把当前上下文放到栈顶。浏览器总是会把当前执行上下文放到栈的顶部,一旦函数执行完成,这个执行上下文就会从栈中移除,返回到栈中的下一个上下文。 这些大概明白了,不过你说在创建执行上下文做的那些事儿,我还是有点迷糊,能再详细说说吗? 那我们首先看点代码: // 例1 console.log(a); // 报错,a is not defined // 例2 console....