0、开场白

  在平时编写JavaScript代码时,我们并不会和执行上下文直接接触,但是想要彻底搞懂JavaScript函数的话,执行上下文是我们绕不过去的一个知识点。

1、执行上下文栈

  JavaScript在对一个函数的每次调用,都会创建一个执行上下文,然后基于这个执行上下文运行函数体内的代码。一个函数可能会创建无数的执行上下文,因为对函数的每次调用(即使在函数内部调用自己)都会创建一个具有新状态的上下文。

  当函数a执行的时候,会创建一个函数a的执行上下文,然后执行函数a中的代码,在函数a中调用另一个函数b的时候,当前a函数的执行会暂停下来,它的执行上下文也不会消失,然后创建一个函数b的执行上下文,执行函数b中的代码。从这个例子可以看出,JavaScript需要对函数的执行上下文进行有序的管理,有一种数据结构特别符合这种场景,它就是栈,所以JavaScript中管理执行上下文的就是执行上下文栈,有时也叫它调用栈。

2、执行上下文

  为什么调用函数的时候要创建一个执行上下文?因为JavaScript要保存一些函数调用时的信息,比如传入的参数、谁调用的。

  执行上下文是由JavaScript的一个内部实现,类似于一个简单的对象,这个对象拥有三个属性:变量对象,作用域链,this。下图展示了一个执行上下文的结构

  

3、变量对象

  变量对象是与执行上下文相关的数据作用域。它是一个与上下文相关的特殊对象,其中存储了在上下文中定义的变量和函数声明。变量对象是一个抽象概念。对于不同的上下文类型,使用不同的对象。比如,在全局上下文中变量对象就是全局对象本身;当函数被调用的时候,则会创建一个活动对象来作为变量对象使用。函数调用创建的活动对象,会比全局对象多一些属性,例如:形参、arguments。

4、作用域链  

  作用域链的作用就是用来查找自由变量的,如果一个变量在函数自身的作用域(活动对象)中没有找到,那么将会查找它外层函数的作用域(活动对象),以此类推。在函数体内没有定义,需要搜索作用域链的变量叫做自由变量。

  关于作用域链最关键的一条信息就是,在创建函数的时候会保存外层函数的作用域链,这个保存下来的作用域链会在将来函数调用时用来查找变量。在函数调用时,将当前函数的活动对象拼接在创建函数时保存的外层函数作用域链开头,然后保存在执行上下文的作用域链属性中,方便函数体内的自由变量进行变量查找。

5、闭包

  在计算机科学中,闭包,又称词法闭包或函数闭包,是引用了自由变量的函数。

  在JavaScript中,函数是第一级,就是说函数可以作为函数的参数、可以作为函数的返回值、可以赋值给变量。这是一个很强大的语言特性,不过它会引起两个问题:

  1. 当函数作为返回值时,这个函数内部的自由变量的解析问题。
  2. 当函数作为参数传递时,这个函数内部的自由变量的解析问题。

  接下来通过两段代码来说清楚这两个问题,先来看第一段代码,在全局环境中声明一个foo函数,foo函数里声明一个变量x等于10,然后返回一个函数,这个函数内部打印了x,执行foo函数,把返回值赋值给了back,在全局环境中声明一个变量x=20,此时执行back函数,会打印出来多少?

function foo() {
var x = 10;
return function() {
console.log(x);
};
}
var back = foo();
var x = 20;
back(); 

  要想知道打印出来的x等于多少,只需要知道变量x的查找顺序就行。back函数是定义在foo函数内部的,在定义的时候,会保存foo函数的作用域链,然后在back函数执行的时候,会把自己的活动对象拼接在foo函数作用域链的开头,所以变量x的查找顺序是back函数的活动对象,这是个空对象,没找到x,然后接着查找第二级,也就是foo函数的活动对象,里面有x,等于10,所以最后打印出来的结果是10。foo函数已经执行完了,它的执行上下文应该消失了,为什么还能找到foo函数执行时的活动对象呢?因为在JavaScript中,如果有外部引用使用了活动对象里的属性,这个活动对象并不会被回收掉。我画了张图帮助大家理解一下这个执行过程:

 

  第二段代码描述的是函数作为参数传递时的场景:

var x = 10;
function foo() {
console.log(x);
}
var b = function (foo) {
var x = 20;
foo();
}
b(foo);

  原理和上面一样,我也画了张图帮助大家理解一下:

  在JavaScript中,所有函数的执行都是基于这两种基础的模型的,无非就是变量多一些,嵌套深一些,查找的过程长一点,希望大家以后能看到代码时就能在脑海中呈现出代码的执行过程,变量的查找过程。

6、this

  执行上下文中还有最后一个this属性没讲,很多JavaScript初学者被this搞的晕头转向,这里我会帮大家理清楚this的所有情况,以后不用再怕this了。

  在JavaScript中,this是关键字,它不是变量,所以它不会参与到变量的解析过程,也就是说不会去查找作用域链。当JavaScript在执行代码时遇到this时,它会直接从执行上下文中拿到this的值,不用做任何查找。

  this的值只在创建执行上下文的时候进行确定,确定之后不会更改。可能有人会反驳,this指向可以修改的呀,那是在进入函数体执行代码之前修改的,进入函数体之后就不能修改了。

  在全局环境中,this的值就是全局对象。

  在函数中,this的值一共有四种情况:

  1. 作为构造函数调用时,this指向构造函数创建的对象
  2. 作为对象的方法调用时,指向该对象
  3. 作为函数调用时,指向全局对象,严格模式下为undefined
  4. call、apply、bind调用时,指向传入的第一个参数

  到此为止,和JavaScript函数执行上下文有关的知识点我都介绍完了,希望这篇文章能够帮你更深刻的认识JavaScript的函数。

  

  参考资料:

  https://www.ecma-international.org/publications/standards/Ecma-262.htm

  中文版ECMA规范

通俗易懂的来讲讲js的函数执行上下文的更多相关文章

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

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

  2. JS高阶---执行上下文

    1.代码分类 2.全局执行上下文 3.函数执行上下文 .

  3. javascript 函数执行上下文

    在js里,每个函数都有一个执行的上下文,我们可以通过this来访问. 如: 全局函数 function test(){ var local = this; } 我们发现local等于window(do ...

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

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

  5. JS高阶---执行上下文栈

    大纲: 主体: 注意:*******函数调用时才会产生上下文栈,声明时不会产生********** 顺序: 概念图: 执行上下文栈的顺序---→后进先出 其他概念图: 当前执行的上下文总是在顶部 全局 ...

  6. JS中函数执行顺序的问题?

    作者:知乎用户链接:https://www.zhihu.com/question/23564807/answer/82996422来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  7. js中的执行上下文,菜鸟入门基础。

    console.log(a); //Uncaught ReferenceError: a is not defined 因为没有定义a所以报错了. var a = 52; console.log(a) ...

  8. 一篇文章看懂JS执行上下文

     壹 ❀ 引 我们都知道,JS代码的执行顺序总是与代码先后顺序有所差异,当先抛开异步问题你会发现就算是同步代码,它的执行也与你的预期不一致,比如: function f1() { console.lo ...

  9. 深入学习JS执行--创建执行上下文(变量对象,作用域链,this)

    一.介绍 本篇继上一篇深入理解js执行--单线程的JS,这次我们来深入了解js执行过程中的执行上下文. 本篇涉及到的名词:预执行,执行上下文,变量对象,活动对象,作用域链,this等 二.预执行 在上 ...

随机推荐

  1. week01-绪论

    一.作业题目 仿照三元组或复数的抽象数据类型写出有理数抽象数据类型的描述 (有理数是其分子.分母均为整数且分母不为零的分数).           有理数基本运算: 构造有理数T,元素e1,e2分别被 ...

  2. 基于 TensorFlow 在手机端实现文档检测

    作者:冯牮 前言 本文不是神经网络或机器学习的入门教学,而是通过一个真实的产品案例,展示了在手机客户端上运行一个神经网络的关键技术点 在卷积神经网络适用的领域里,已经出现了一些很经典的图像分类网络,比 ...

  3. [.net 面向对象程序设计深入](18)实战设计模式——设计模式使用场景及原则

    [.net 面向对象程序设计深入](18)实战设计模式——设计模式使用场景及原则 1,什么是设计模式? 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计 ...

  4. harbor在谷歌云上搭建 日志

    参考:https://github.com/vmware/harbor/blob/master/docs/installation_guide.md 日志: [root@instance-1 harb ...

  5. springboot加ES实现全局检索

    ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...

  6. 平衡二叉树(AVL)介绍及其实现

    一.平衡二叉树 任何一个数据的查找过程都需要从根结点出发,沿某一个路径朝叶子结点前进.因此查找中数据比较次数与树的形态密切相关. 对于二叉树来说,当树中每个结点左右子树高度大致相同时,树高为logN. ...

  7. 再谈反向传播(Back Propagation)

    此前写过一篇<BP算法基本原理推导----<机器学习>笔记>,但是感觉满纸公式,而且没有讲到BP算法的精妙之处,所以找了一些资料,加上自己的理解,再来谈一下BP.如有什么疏漏或 ...

  8. [Swift]LeetCode404. 左叶子之和 | Sum of Left Leaves

    Find the sum of all left leaves in a given binary tree. Example: 3 / \ 9 20 / \ 15 7 There are two l ...

  9. 微信小程序与AspNetCore SignalR聊天实例

    微信小程序与aspnetcore signalr实例 本文不对小程序与signalr做任何介绍,默认读者已经掌握 aspnetcore Signalr文档 小程序文档 写在之前 SignalR没有提供 ...

  10. 精读《react-easy-state 源码》

    1. 引言 react-easy-state 是个比较有趣的库,利用 Proxy 创建了一个非常易用的全局数据流管理方式. import React from "react"; i ...