理解Javascript之执行上下文(Execution Context)
1>什么是执行上下文
Javascript中代码的运行环境分为以下三种:
全局级别的代码 - 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
函数级别的代码 - 当执行一个函数时,运行函数体中的代码。
Eval的代码 - 在Eval函数内运行的代码。
- javascript是一个单线程语言,这意味着在浏览器中同时只能做一件事情。当javascript解释器初始执行代码,它首先默认进入全局上下文。每次调用一个函数将会创建一个新的执行上下文。
每次新创建的一个执行上下文会被添加到作用域链的顶部,有时也称为执行或调用栈。浏览器总是运行位于作用域链顶部的当前执行上下文。一旦完成,当前执行上下文将从栈顶被移除并且将控制权归还给之前的执行上下文。
不同执行上下文之间的变量命名冲突通过攀爬作用域链解决,从局部直到全局。这意味着具有相同名称的局部变量在作用域链中有更高的优先级。
简单的说,每次你试图访问函数执行上下文中的变量时,查找进程总是从自己的变量对象开始。如果在自己的变量对象中没发现要查找的变量,继续搜索作用域链。它将攀爬作用域链检查每一个执行上下文的变量对象,寻找和变量名称匹配的值。
2>执行上下文的建立过程
我们现在已经知道,每当调用一个函数时,一个新的执行上下文就会被创建出来。然而,在javascript引擎内部,这个上下文的创建过程具体分为两个阶段:
建立阶段(发生在当调用一个函数时,但是在执行函数体内的具体代码以前)
建立变量,函数,arguments对象,参数
建立作用域链
确定this的值
代码执行阶段:
变量赋值,函数引用,执行其它代码
实际上,可以把执行上下文看做一个对象,其下包含了以上3个属性:
1
2 3 4 5 |
executionContextObj = {
variableObject: { /* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */ }, scopeChain: { /* variableObject 以及所有父执行上下文中的variableObject */ }, this: {} } |
3>建立阶段以及代码执行阶段的详细分析
确
切地说,执行上下文对象(上述的executionContextObj)是在函数被调用时,但是在函数体被真正执行以前所创建的。函数被调用时,就是我
上述所描述的两个阶段中的第一个阶段 -
建立阶段。这个时刻,引擎会检查函数中的参数,声明的变量以及内部函数,然后基于这些信息建立执行上下文对象
(executionContextObj)。在这个阶段,variableObject对象,作用域链,以及this所指向的对象都会被确定。
上述第一个阶段的具体过程如下:
找到当前上下文中的调用函数的代码
在执行被调用的函数体中的代码以前,开始创建执行上下文
进入第一个阶段-建立阶段:
建立variableObject对象:
建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性值
检查当前上下文中的函数声明:
每找到一个函数声明,就在variableObject下面用函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用
如果上述函数名已经存在于variableObject下,那么对应的属性值会被新的引用所覆盖。
检查当前上下文中的变量声明:
每找到一个变量的声明,就在variableObject下,用变量名建立一个属性,属性值为undefined。
如果该变量名已经存在于variableObject属性中,直接跳过(防止指向函数的属性的值被变量属性覆盖为undefined),原属性值不会被修改。
初始化作用域链
确定上下文中this的指向对象
代码执行阶段:
执行函数体中的代码,一行一行地运行代码,给variableObject中的变量属性赋值。
下面来看个具体的代码示例:
1
2 3 4 5 6 7 8 9 10 11 |
function foo(i) {
var a = 'hello'; var b = function privateB() { }; } foo(22); |
在调用foo(22)的时候,建立阶段如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
fooExecutionContext = {
variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: undefined, b: undefined }, scopeChain: { ... }, this: { ... } } |
由此可见,在建立阶段,除了arguments,函数的声明,以及参数被赋予了具体的属性值,其它的变量属性默认的都是undefined。一旦上述建立阶段结束,引擎就会进入代码执行阶段,这个阶段完成后,上述执行上下文对象如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
fooExecutionContext = {
variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c() a: 'hello', b: pointer to function privateB() }, scopeChain: { ... }, this: { ... } } |
我们看到,只有在代码执行阶段,变量属性才会被赋予具体的值。
4>局部变量作用域提升的缘由
在网上一直看到这样的总结: 在函数中声明的变量以及函数,其作用域提升到函数顶部,换句话说,就是一进入函数体,就可以访问到其中声明的变量以及函数。这是对的,但是知道其中的缘由吗?相信你通过上述的解释应该也有所明白了。不过在这边再分析一下。看下面一段代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
(function() {
console.log(typeof foo); // function pointer var foo = 'hello', function foo() { }()); |
上述代码定义了一个匿名函数,并且通过()运算符强制理解执行。那么我们知道这个时候就会有个执行上下文被创建,我们看到例子中马上可以访问foo以及bar变量,并且通过typeof输出foo为一个函数引用,bar为undefined。
为什么我们可以在声明foo变量以前就可以访问到foo呢?
因
为在上下文的建立阶段,先是处理arguments,
参数,接着是函数的声明,最后是变量的声明。那么,发现foo函数的声明后,就会在variableObject下面建立一个foo属性,其值是一个指向
函数的引用。当处理变量声明的时候,发现有var
foo的声明,但是variableObject已经具有了foo属性,所以直接跳过。当进入代码执行阶段的时候,就可以通过访问到foo属性了,因为它
已经就存在,并且是一个函数引用。为什么bar是undefined呢?
因为bar是变量的声明,在建立阶段的时候,被赋予的默认的值为undefined。由于它只要在代码执行阶段才会被赋予具体的值,所以,当调用typeof(bar)的时候输出的值为undefined。
理解Javascript之执行上下文(Execution Context)的更多相关文章
- 深入理解Javascript之执行上下文(Execution Context)
在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念.相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数 ...
- 深入理解JavaScript系列+ 深入理解javascript之执行上下文
http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html http://blog.csdn.net/hi_kevin/article/d ...
- 前端知识体系:JavaScript基础-原型和原型链-理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题
理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题(原文文档) 1.什么是执行上下文: 简而言之,执行上下文就是当前JavaScript代码被解析和执行时所在环境的抽象概念,Java ...
- Javascript 的执行环境(execution context)和作用域(scope)及垃圾回收
执行环境有全局执行环境和函数执行环境之分,每次进入一个新执行环境,都会创建一个搜索变量和函数的作用域链.函数的局部环境不仅有权访问函数作用于中的变量,而且可以访问其外部环境,直到全局环境.全局执行环境 ...
- 深入理解javascript执行上下文(Execution Context)
本文转自:http://blogread.cn/it/article/6178 在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念.相信读完这篇文章后,你 ...
- 【深入理解javascript】执行上下文
参考原文:执行上下文 1.每一个执行上下文,工作分为三个阶段: 准备阶段–>执行阶段–>调用阶段 准备阶段:代码执行之前,设置数据,相当于初始化. 执行阶段:开始执行每一行代码. 调用阶段 ...
- JavaScript的执行上下文,真没你想的那么难
作者:小土豆 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/2436173500265335 前言 在正文开始前,先来看 ...
- JavaScript的执行上下文
在JavaScript的运行过程中,经常会遇到一些"奇怪"的行为,不理解为什么JavaScript会这么工作. 这时候可能就需要了解一下JavaScript执行过程中的相关内容了. ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
随机推荐
- 《C++ API设计》作者Martin Reddy访谈问题征集
Martin Reddy博士是软件行业的一名老兵,有着15年以上的从业经验,共撰写过40多篇论文,拥有3项软件专利,并与他人合著了Level of Detail for 3D Graphics.另外, ...
- c++ 注册类到 lua
test.h: #ifndef __TEST_H__ #define __TEST_H__ class CTest { public: CTest(); ~CTest(); int getA(); v ...
- spring boot servlet 注入
spring boot 注入servlet的方法是借助ServletRegistrationBean这个类 例子如下: 先建一个servlet import java.io.IOException; ...
- PLSQL Developer导入Excel数据
LSQL Developer导入Excel数据 最近处理将Excel数据导入Oracle的工作比较多.之前都是采用Sqlldr命令行导入的方式处理.每次导入不同格式的Excel表数据,都需要先把Exc ...
- 简单使用Apache POI
Apache POI是一个纯Java编写用来操作Microsoft Office的框架,最常见的应用是让服务器后台按照特定的数据生成Excel表格提供给用户实用.前段时间因为项目的需要被大量使用,使用 ...
- knockoutjs入门
Knockoutjs入门 (概念相关)对于asp.net 后端开发人员来说我们几乎都接触过mvc框架.knockoutjs是MVVM模式对于我们来说有点陌生.Knockout是一个轻量级的UI类库,通 ...
- Python作业 1
用户有三次机会登录,登录成功,显示欢迎界面. 三次登录失败,账户被锁定,下次登录失败. 思路: 1:流程控制:for或者while循环,达到三次检测目的. 2:文件操作. 检测文档,一旦发现输入账户名 ...
- python之禅
>>> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is ...
- 显示Class 'Think\Controller\FuController' not found和Call to a member function assign() on a non-object 的错误问题
Class 'Think\Controller\FuController' not found 错误位置 FILE: D:\wamp\www\tinkphp\Application\Come\Cont ...
- Excel-漏斗图分析(差异分析)
漏斗图适用于业务流程比较规范.周期长.环节多的流程分析,通过漏斗各环节业务数据的比较,能够直观地发现和说明问题所在.在网站分析中,通常用于转化率比较,它不仅能展示用户从进入网站到实现购买的最终转化率, ...