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. [python] PyMouse、PyKeyboard用python操作鼠标和键盘

      1.PyUserInput 简介 PyUserInput是一个使用python的跨平台的操作鼠标和键盘的模块,非常方便使用.支持的平台及依赖如下: Linux - Xlib Mac - Quart ...

  2. C# 控制台应用程序中输出彩色字体

    using System; class Example { public static void Main() { // Get a string array with the names of Co ...

  3. Vue 学习笔记 — filter

    简书 对将要插入html的对象进行处理 一个简单的Vue示例 基本过滤器用法 带参数的过滤器 全局过滤器 (这张图片有点问题,最后显示的应该是 hello world不是null) 过滤器的简单应用 ...

  4. [Swift]LeetCode153. 寻找旋转排序数组中的最小值 | Find Minimum in Rotated Sorted Array

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  5. 进程,线程,协程,异步IO知识点

    进程: qq 要以一个整体的形式暴露给操作系统管理,里面包含对各种资源的调用,内存的管理,网络接口的调用等...对各种资源管理的集合 就可以成为  进程 线程: 是操作系统最小的调度单位, 是一串指令 ...

  6. AspNetCore 基于流下载文件与示例代码

    昨天说了,AspNetCore如何进行上传文件,其中写了两种方式ajax与模型,其文章地址为:https://www.cnblogs.com/ZaraNet/p/9949167.html 那么既然有上 ...

  7. nginx替换响应头(重点:如何在替换时加上if判断)

    在实现微信小程序内嵌非业务域名时,通过nginx做镜像网站绕过小程序业务域名检测,但有一些表单页面提交后会返回一个302状态,由响应头Location的值决定提交成功后的跳转地址.那么问题来了,这个地 ...

  8. 使用ML.NET + ASP.NET Core + Docker + Azure Container Instances部署.NET机器学习模型

    本文将使用ML.NET创建机器学习分类模型,通过ASP.NET Core Web API公开它,将其打包到Docker容器中,并通过Azure Container Instances将其部署到云中. ...

  9. asp.net core 系列 18 web服务器实现

    一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...

  10. C#版 - Leetcode 201. 数字范围按位与(bitwise AND) - 题解

    C#版 - Leetcode 201. 数字范围按位与(bitwise AND) - 题解 在线提交: https://leetcode.com/problems/bitwise-and-of-num ...