-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
深入理解JavaScript执行上下文和执行栈 #60
Comments
@ljianshu 我有个问题,希望大佬可以解答一下。全局执行上下文会一直存在于执行栈的栈底直到页面被关闭,这意味着执行栈一直非空吧?而 JavaScript 事件队列中的事件,是会等到执行栈空的时候才会进入到执行栈中(比如 |
一旦"执行栈"中的 所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 并不是你所说的等到执行栈空时候,而是执行栈中所有同步任务执行完毕 |
你好,非常感谢你的文章,但是关于本文和你写的深入理解JavaScript作用域和作用域链一文,有两点感到矛盾: |
作用域链:作用域链是当前执行上下文中的变量对象(VO)和其所有父级执行上下文的变量对象组成的。最顶端始终是当前执行上下文的变量对象(VO),最低端始终是全局执行上下文中的变量对象(VO)。(如果是函数执行上下文,AO = VO) 还拿上述代码为例:
当foo访问一个变量时,首先会从作用域链从顶端查找,如果未找到就沿着作用域链向后查找。直到找到全局执行上下文的VO。 2.作者这里所指的解析,跟解释阶段应该是同一个过程。但是我认为并不同。当开始执行一段JavaScript脚本时,会首先进入全局执行上下文(个人认为这里已经是解释阶段之后了)。然后进行执行上下文的两个阶段:创建阶段 -> 执行阶段。 |
执行上下文创建阶段的变量对象是指什么? |
前言
如果你是一名 JavaScript 开发者,或者想要成为一名 JavaScript 开发者,那么你必须知道 JavaScript 程序内部的执行机制。执行上下文和执行栈是JavaScript中关键概念之一,是JavaScript难点之一。 理解执行上下文和执行栈同样有助于理解其他的 JavaScript 概念如提升机制、作用域和闭包等。本文尽可能用通俗易懂的方式来介绍这些概念。
一、执行上下文(Execution Context)
1.什么是执行上下文
简而言之,执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行
2.执行上下文的类型
执行上下文总共有三种类型:
二、执行上下文的生命周期
执行上下文的生命周期包括三个阶段:创建阶段→执行阶段→回收阶段,本文重点介绍创建阶段。
1.创建阶段
当函数被调用,但未执行任何其内部代码之前,会做以下三件事:
在一段 JS 脚本执行之前,要先解析代码(所以说 JS 是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来。变量先暂时赋值为undefined,函数则先声明好可使用。这一步做完了,然后再开始正式执行程序。
另外,一个函数在执行之前,也会创建一个函数执行上下文环境,跟全局上下文差不多,不过 函数执行上下文中会多出this arguments和函数的参数。
2.执行阶段
执行变量赋值、代码执行
3.回收阶段
执行上下文出栈等待虚拟机回收执行上下文
三、变量提升和this指向的细节
1.变量声明提升
大部分编程语言都是先声明变量再使用,但在JS中,事情有些不一样:
上述代码正常输出
undefined
而不是报错Uncaught ReferenceError: a is not defined
,这是因为声明提升(hoisting),相当于如下代码:2.函数声明提升
我们都知道,创建一个函数的方法有两种,一种是通过函数声明
function foo(){}
另一种是通过函数表达式
var foo = function(){}
,那这两种在函数提升有什么区别呢?接下来我们通过一个例子来说明这个问题:
在上面的例子中,foo()调用的时候报错了,而bar能够正常调用。
我们前面说过变量和函数都会上升,遇到函数表达式
var foo = function(){}
时,首先会将var foo
上升到函数体顶部,然而此时的foo的值为undefined,所以执行foo()
报错。而对于函数
bar()
, 则是提升了整个函数,所以bar()
才能够顺利执行。有个细节必须注意:当遇到函数和变量同名且都会被提升的情况,函数声明优先级比较高,因此变量声明会被函数声明所覆盖,但是可以重新赋值。
function声明的优先级比var声明高,也就意味着当两个同名变量同时被function和var声明时,function声明会覆盖var声明
这代码等效于:
最后我们看个复杂点的例子:
这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后依次按照如下的步骤执行:
3.确定this的指向
先搞明白一个很重要的概念 —— this的值是在执行的时候才能确认,定义的时候不能确认! 为什么呢 —— 因为this是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。看如下例子:
接下来我们逐一解释上面几种情况
四、执行上下文栈(Execution Context Stack)
函数多了,就有多个函数执行上下文,每次调用函数创建一个新的执行上下文,那如何管理创建的那么多执行上下文呢?
JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。
从上面的流程图,我们需要记住几个关键点:
我们再来看个例子:
上述代码运行按照如下步骤:
参考文章
this
关键字The text was updated successfully, but these errors were encountered: