一、介绍

本篇继上一篇深入理解js执行--单线程的JS,这次我们来深入了解js执行过程中的执行上下文。

本篇涉及到的名词:预执行,执行上下文,变量对象,活动对象,作用域链,this等

二、预执行

在上一篇说到,在js代码被执行,执行上下文会被压进执行栈中,但是在此之前还有一步工作要做,就是创建好执行上下文,因为创建好才能被压进去啊。

创建执行上下文就是预执行过程: 接下来说说创建执行上下文的细节部分。

三、创建执行上下文

(1)执行上下文组成

执行上下文:也叫一个执行环境,有全局执行环境和函数执行环境两种。每个执行环境中包含这三部分:变量对象/活动对象作用域链this的值

代码模拟

//可以把执行上下文看作一个对象
exeContext = {
VO = [...], //VO代表变量对象,保存变量和函数声明
scopeChain = [...]; //作用域链
thisValue = {...}; //this的值
}

创建执行上下文就是创建变量对象,作用域链和this过程

接下来就分别细说创建变量对象/活动对象,作用域链,this值的过程。

(2)变量对象(variable object)

变量对象中存储了在上下文(环境)中定义的变量和函数声明

创建变量对象(VO)时就是将各种变量和函数声明进行提升的环节:

//用下面代码为例子
console.log(a);
console.log(b);
console.log(c);
console.log(d);
var a = 100;
b = 10;
function c(){};
var d = function(){};

上述代码的变量对象:

//这里用VO表示变量对象
VO = {
a = undefined; //有a,a使用var声明,值会被赋值为undefined
//没有b,因为b没用var声明
c = function c (){} //有c,c是函数声明,并且c指向该函数
d = undefined; //有d,d用var声明,值会被赋值为undefined
}

解说:执行上述代码的时候,会创建一个全局执行上下文,上下文中包含上面变量对象,创建完执行上下文后,这个执行上下文才会被压进执行栈中。开始执行后,因为js代码一步一步被执行,后面赋值的代码还没被执行到,所以使用console.log函数打印各个变量的值是变量对象中的值。

在运行到第二行时会报错(报错后就不再执行了),因为没有b(b is no defined)。把第二行注释掉后,再执行各个结果就是VO里面的对应的值。

讲到这里我想大家对变量对象理解了吧,以及对变量提升和函数提升有个深入了解。

(3)活动对象(activation object)

活动对象是在函数执行上下文里面的,其实也是变量对象,只是它需要在函数被调用时才被激活,而且初始化arguments,激活后就是看做变量对象执行上面一样的步骤。

//例子
function fn(name){
var age = 3;
console.log(name);
}
fn('ry');

当上面的函数fn被调用,就会创建一个执行上下文,同时活动对象被激活

//活动对象
AO = {
arguments : {0:'ry'}, //arguments的值初始化为传入的参数
name : ry, //形参初始化为传进来的值
age : undefined //var 声明的age,赋值为undefined
}

活动对象其实也是变量对象,做着同样的工作。其实不管变量还是活动对象,这里都表明了,全局执行和函数执行时都有一个变量对象来储存着该上下文(环境内)定义的变量和函数。

(4)作用域链(scope chain)

在创建执行上下文时还要创建一个重要的东西,就是作用域链。每个执行环境的作用域链由当前环境的变量对象及父级环境的作用域链构成。

创建作用域链过程:

//以本段代码为例
function fn(a,b){
var x = 'string',
}
fn(1,2);

1.函数被调用前,初始化function fn,fn有个私有属性[[scope]],它会被初始化为当前全局的作用域,fn.[[scope]="globalScope"。

2.调用函数fn(1,2),开始创建fn执行上下文,同时创建作用域链fn.scopeChain = [fn.[[scope]]],此时作用域链中有全局作用域。

3.fn活动对象AO被初始化后,把活动对象作为变量对象推到作用域链前端,此时fn.scopeChain = [fn.AO,fn.[[scope]]],构建完成,此时作用域链中有两个值,一个当前活动对象,一个全局作用域。

fn的作用域链构建完成,作用域链中有两个值,第一个是fn函数自身的活动对象,能访问自身的变量,还有一个是全局作用域,所以fn能访问外部的变量。这里就说明了为什么函数中能够访问函数外部的变量,因为有作用域链,在自身找不到就顺着作用域链往上找。

(5)this的值

上面说过执行上下文有两种,一个全局执行上下文,一个函数执行上下,下面分别说说这两种上下文的this。

a.全局执行上下文的this

指向window全局对象

b.函数执行上下文的this(主要讲函数的this)

在《JavaScript权威指南》中有这么几句话:

1.this是关键字,不是变量,不是属性名,js语法不允许给this赋值。

2.关键字this没有作用域限制,嵌套的函数不会从调用它的函数中继承this。

3.如果嵌套函数作为方法调用,其this指向调用它的对象。

4.如果嵌套函数作为函数调用,其this值是window(非严格模式),或undefined(严格模式下)。

解读一下: 上面说的概括了this两种值的情况:

1.函数直接作为某对象的方法被调用则函数的this指向该对象。

2.函数作为函数直接独立调用(不是某对象的方法),或是函数中的函数,其this指向window。

我们看几个栗子便可理解:

栗子1:(这个例子我相信都能理解)当函数被独立运行时,其this的值指向window对象。

function a(){
console.log(this);
}
//独立运行
a(); //window

栗子2:(函数中函数,这里嵌套了个外围函数)这里也是指向window对象,也相当于函数作为函数调用,就是独立运行。其实这个例子也说明闭包的this指向Window。

//外围函数
function a(){
//b函数在里面
function b(){
console.log(this);
}
//虽然在函数中,但b函数独立运行,不是那个对象的方法
b();
}
a(); //window

栗子3:(再写复杂点的话)x函数即使在对象里面,但它是函数中的函数,也是作为函数运行,不是Object的方法。getName才是objcet的方法,所以getName的this指向object(在下个栗子有)。

//一个对象
var object = {
//getName是Object的方法
getName : function(){
//x是getName里面的函数,它是作为函数调用的,this就是window啦
function x(){
console.log(this);
}
x();
}
}
object.getName(); //window

以上三个都是输出window,下面是this指向某个对象的情况。

栗子4:函数作为某个对象的方法被调用。

//一个对象
var object = {
name : "object",
//getName是Object的方法
getName : function(){
console.log(this === object);
}
}
object.getName(); //true , 说明this指向了object

这里的getName中的this是指向objct对象的,因为getName是object的一个方法,它作为对象方法被调用。

栗子5:再来个栗子。

var name = "window";
var obj = {
name : "obj"
};
function fn (){
console.log(this.name);
} //将fn通过call或bind或apply直接绑定给obj,从而成为obj的方法。
fn.call(obj); //obj

再总结一下this的值

全局执行上下文:this的值是window

函数执行上下文:this的值两种:

1.函数中this指向某对象,因为函数作为对象的方法:怎么看函数是对象的方法,一种是直接写在对象里面(不是嵌套在对象方法中的函数,不懂再看看栗子3),另一种是通过call等方法直接绑定在对象中。

2.函数中this指向window:函数独立运行,不是对象的方法,函数中的函数(闭包),其this指向window。

四、总结整个js代码执行过程

(1)JS执行过程

js代码执行分成了两部分:预执行和执行

  1. 预执行:创建好执行上下文,有两种,一种是开始执行js代码就创建全局的执行上下文,一种是当某个函数被调用时创建它自己的函数执行上下文。这里也就是本节主要讲的东西,创建执行上下文的三个重要成分。
  2. 执行:在执行栈中执行,栈顶的执行上下文获得执行权,并按顺序执行当前上下文中的代码,执行完后弹栈销毁上下文,执行权交给下一个栈顶执行上下文。

(2)放上图示

某个执行上下文生命周期:

五、后话

整个js的执行过程就这样了,一开始可能有点难理解,但看多几遍就慢慢领会了。希望大家能够理解。如果觉得写得好,记得点赞,关注哦。

本文出自博客园:http://www.cnblogs.com/Ry-yuan/

作者:Ry(渊源远愿)

欢迎访问我的个人首页:我的首页

欢迎访问我的github:https://github.com/Ry-yuan/demoFiles

欢迎转载,转载请标明出处,保留该字段。

深入学习JS执行--创建执行上下文(变量对象,作用域链,this)的更多相关文章

  1. JavaScript 执行环境(执行上下文) 变量对象 作用域链 上下文 块级作用域 私有变量和特权方法

    总结自<高程三>第四章  理解Javascript_12_执行模型浅析   JS的执行环境与作用域  javascript高级程序第三版学习笔记[执行环境.作用域] 在javascript ...

  2. [从jQuery看JavaScript]-变量与作用域链

    jQuery片段: var // Will speed up references to window, and allows munging its name. window = this, //  ...

  3. 进阶学习js中的执行上下文

    在js中的执行上下文,菜鸟入门基础 这篇文章中我们简单的讲解了js中的上下文,今天我们就更进一步的讲解js中的执行上下文. 1.当遇到变量名和函数名相同的问题. var a = 10; functio ...

  4. JavaScript学习日志(一):变量,作用域和内存问题

    一,变量分为两种类型:基本类型值和引用类型值,基本类型包括:Undefined, String, Boolean, Null, Number,我们无法给基本类型值添加属性: 二,复制变量值的时候,如果 ...

  5. js高程 第 4章 变量、作用域和内存问题 【笔记】

    4.4 小结 JavaScript变量可以用来保存两种类型的值:基本类型值和引用类型值.基本类型的值源自以下 5 种基本数据类型:Undefined.Null.Boolean.Number 和 Str ...

  6. 学习js函数--自执行函数

    我在写代码时候经常会在tpl的<script>里写类似的代码: $(function(){ alert("我好饿"); }); 刚开始的时候只知道写了它不需要调用,直接 ...

  7. 变量对象、作用域链和This

    变量对象 作用域链 This 整理自:https://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 系列文章中变量对象,作用域链和this ...

  8. JS进阶之---执行上下文,变量对象,变量提升

    一.结构顺序大体介绍 JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段. 编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定. 执行阶段由引擎完成, ...

  9. JavaScript学习系列之执行上下文与变量对象篇

    一个热爱技术的菜鸟...用点滴的积累铸就明日的达人 正文 在上一篇文章中讲解了JavaScript内存模型,其中有提到执行上下文与变量对象的概念.对于JavaScript开发者来说,理解执行上下文与变 ...

随机推荐

  1. 简述static关键字、void与void *(void指针)、函数指针

    static关键字1.修饰局部变量,延长局部变量的生命周期.使变量成为静态局部变量,在编译时就为变量分配内存,直到程序退出才释放存储单元.2.修饰全局变量,限制全局变量的使用范围为本文件中.全局变量默 ...

  2. CSS之 border 属性

    特性 border-width 不支持百分比  border-color 默认颜色是 color  border-color 透明值的作用:可利用增加可点击区域,利用内阴影做边框  border 应用 ...

  3. win10 UWP 申请微软开发者

    申请微软开发者可以到https://dev.windows.com/zh-cn/programs/join 如果是学生,先去http://www.dreamspark.com/ 如果是英文,点stud ...

  4. XamlReader动态使用xaml

    xamlload先在xaml做出一个grid,命名xgrid <Page x:Class="xamlload.MainPage" xmlns="http://sch ...

  5. git无法pull仓库refusing to merge unrelated histories

    本文讲的是把git在最新2.9.2,合并pull两个不同的项目,出现的问题如何去解决fatal: refusing to merge unrelated histories 我在Github新建一个仓 ...

  6. PyCharm 2017 免费 破解 注册 激活 教程(附 License Server 地址)(Python 编辑器 IDE 推荐)

    许多朋友都在问如何破解 PyCharm 2017 Professional 专业版,咪博士对此是坚决反对的! 不到万不得已,请不要这样做.破解之前,请拖到文章末尾,思考几个问题,想明白你确实需要这样做 ...

  7. 在Git上如何强推代码规范

    引言 最近参加了“前端规范制定topic”小组,小组成员一起制定了html.css.js.es6.vue和react等规范,但规范制定好了怎么进行推广去强制执行呢,已知我们的项目都是用git做管理的, ...

  8. Windows下安装RabbitMQ

    今天正好给自己机器安装rabbitmq,总结下安装经验. 现在国内访问erlang,和 RabbitMQ 官网好像都很难连上.我已下载好了资源,需要的朋友可以下载. 链接: https://pan.b ...

  9. 如何高效的编写Verlog HDL——菜鸟版

    工欲善其事.必先利其器!要想高效的编写verilog没有一个好的编辑器可不行,所以我这里推荐两款十分好用的编辑器Notepad++和Gvim,这两款编辑器由于其强大的添加插件的功能,所以深受代码工作者 ...

  10. C 返回函数与闭包的考虑

    #include <stdio.h> typedef int (*fun)(); fun closure(int i) { int squ() { return i*i; } return ...