JavaScript的作用域(scope)和执行上下文(execution context)总是纠缠不清,以至于网上出现了大量文章来区分这两个概念。

MDN中是这样描述scope的:
Scope
The current context of execution. The context in which values and expressions are “visible” or can be referenced. If a variable or other expression is not “in the current scope,” then it is unavailable for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.
Scope(作用域)
当前的执行上下文。值 (en-US)和表达式在其中 “可见” 或可被访问到的上下文。如果一个变量 (en-US)或者其他表达式不 “在当前的作用域中”,那么它就是不可用的。 作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用。

MDN文档认为Scope就是当前的执行上下文。

而在stackoverflow中,相关问题中赞同数最高的回答是这样的:

A context of a function is the value of this for that function
函数的执行上下文就是该函数this的值。

最后我还是接受了@Bernie维尼的观点和 @余光 的作用域、执行上下文相关观点,并结合了自己的理解,欢迎大家讨论。

1. 作用域

作用域,即某变量是可被获取的一个区域。一个作用域,包含了该区域中的变量,常量,函数等定义信息和赋值信息,以及这个区域内代码书写的结构信息。
JavaScript采用的作用域是词法作用域(lexical scoping),也就是静态作用域:

  • 函数的作用域在函数定义时就决定了。
    与之对应的还有一个动态作用域:
  • 即函数的作用域是在函数调用时才决定的。

观察以下代码:

  • global scope包含一个变量name1和一个函数sayName,所以在global scope中,name1和sayName是可用的;
  • sayName scope也包含了一个变量name2和一个函数say,所以在sayName scope中,name2和say是可用的;
  • say scope包含了一个变量name3,在say scope中,name3是可用的。

我们可以说name1的作用域为global scope

当使用到一个变量时,会首先在当前作用域中查找该变量,如果在当前作用域没有找到该变量,会向上级作用域递归查找,直到global scope,如果仍未找到,那么返回undefined

假如我们在say scope中需要使用name1,首先会在当前作用域say scope查找name1,没有找到后,在上级作用域sayName scope中查找,仍然没有找到,继续向上查找,最后在global scope中找到了name1。如果我们在say scope中需要使用name4,由于在say scope->sayName scope->global scope都没有找到,那么返回undefined

形如say scope->sayName scope->global scope这样的链状形式我们称之为作用域链,值得注意的是,作用域链是由下级到上级的单向结构,所以,如果我们在global scope中需要使用name2或者name3,将得到undefined

而在上文中的global scope我们称为全局作用域,全局作用域中的变量称之为全局变量。
注意: 所有未定义而直接复制的变量自变量也是全局变量

var name1 = 'Niall'
function sayNmae(){let name2 = 'Horan'function say(){let name3 = 'Adam'name4 = 'Jams'}
}
console.log(name4)

输出:

Jams

在上文中我们提到过,每个函数在定义之时,其作用域就以及确定了,随之确定还有该函数的作用域链
观察下面的函数:

var name = "Niall"
function sayName(){var name = 'Jams'function say(){console.log(name)}return say
}
var sayJams = sayName()
sayJams()

输出:

Jams

分析:

  • global scope含有一个变量name
  • 执行sayName()返回了say函数,并将say函数赋值给sayJams
  • 调用sayJams(),其内容为console.log(name),由于sayJams作用域内不存在name变量,所以会沿着作用域链递归查找name。
  • 而一个函数在定义时,其作用域链就已经被决定了,所以sayJamssay的作用域链一样(因为是将say赋值给了sayJams),而say的作用域链为say scope->sayName scope->globel scope,在sayName scope中找到了name为Jams。
  • 最后输出Jams

2. 执行上下文(execution context)

执行上下文中包含了一个重要的值,那就是this的指向。
什么是执行上下文?

  • 每一段代码块(被封装成函数的代码)都对应着一个执行上下文,而“全局”也被视作一段代码块。
  • 每当程序进入某个代码块时,一个新的执行上下文就会被创建,并被装入到执行上下文栈中,当程序退成该代码块,对应的执行上下文就被弹出。
  • 当程序在某段代码块中运行到某个点需要转到了另一个代码块时(调用了另一个函数),那么当前的可执行上下文的状态会被置为挂起,然后生成一个新的可执行上下文放入 stack 的顶部。

    观察下面代码:
console.log(1);function father() {console.log(2);(function child() {console.log(3);}());console.log(4);
}
father();console.log(5);//会依次输出1,2,3,4,5

分析它的执行栈尽力了什么:

JavaScript 作用域与执行上下文相关推荐

  1. JS基础篇之作用域、执行上下文、this、闭包

    前言:JS 的作用域.执行上下文.this.闭包是老生常谈的话题,也是新手比较懵懂的知识点.当然即便你作为老手,也未必真的能理解透彻这些概念. 一.作用域和执行上下文 作用域: js中的作用域是词法作 ...

  2. JS深入--词法作用域、执行上下文与闭包

    文章目录 词法作用域 执行上下文与词法环境 闭包 闭包练习 作用域链 REF   个人博客文章同步地址 词法作用域   JS 使用的是词法作用域(或称为静态作用域),函数的作用域在定义的时候就决定了, ...

  3. JavaScript深入之执行上下文栈

    顺序执行? 如果要问到 JavaScript 代码执行顺序的话,想必写过 JavaScript 的开发者都会有个直观的印象,那就是顺序执行,毕竟: var foo = function () {con ...

  4. javascript系列之执行上下文

    写在前面:一 直想系统的总结一下学过的javascript知识,喜欢这门语言也热爱这门语言.未来想从事前端方面的工作,提前把自己的知识梳理一下.前面写了些 DOM的知识,略觉水平有限.没几个月就要开赴 ...

  5. js笔记---作用域(执行上下文[execution context],活动对象) 闭包

    作用域: 首先,在javascript中的每个函数都是对象,是Funtion对象的一个实例,而Funtion中有一系列仅供javascript引擎存取的内部属性,其中一个便是[[scope]],它包含 ...

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

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

  7. JavaScript:执行上下文执行上下文栈

    在JavaScript概念中,有一个概念比较难以理解,它就是执行上下文和执行栈.最近在网上查阅了很多资料,现在把我的一些理解写出来,希望对各位有些帮助. 一.执行上下文 什么是执行上下文?是不是我们平 ...

  8. javascript执行上下文

    执行上下文(Excution Context) Js代码在引擎中是以"一段一段"的方式来执行的,而非一行一行来分析执行的.而这"一段一段"的可执行代码无非三种: ...

  9. js学习笔记(执行上下文、闭包、this部分)

    1.函数的准备工作 函数在执行会进行一些准备工作,如创建一个"执行上下文"环境:执行上下文可以理解为当前代码的执行环境,它会形成一个作用域: 每个碰到可执行代码的时候都会进行这些& ...

最新文章

  1. Go 学习笔记(36)— 基于Go方法的面向对象(封装、继承、多态)
  2. yii cgridview 默认的筛选如何做成选择框
  3. C#控制台程序生成文件分析
  4. Linux驱动修炼之道-内存映射
  5. Flutter Widget截图
  6. [Linux基础环境/软件]Linux下安装resin web服务器(涉及gcc、jdk环境部署)
  7. ListBox实现拖拽排序功能
  8. 通过Rancher安装K8s
  9. JanusGraph 安装
  10. 卸载 windows_Windows 10可能很快会自动卸载有问题的Windows更新
  11. [2013.9.27][cpp]一个简单的链接栈模型
  12. linux中查看某个进程打开的文件数
  13. 经典网页设计:20个与众不同的国外 HTML5 网站
  14. java 旅游管理系统
  15. 《如何写好科研论文》章节答案(清华)学堂在线(2020秋最新网课答案)
  16. win10系统下摄像头无法打开的解决方法
  17. 如何压缩图片呢?这两种方法很管用
  18. 《UnityAPI.Animator动画器》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+Animator+avatar+CrossFade+Key+立钻哥哥++OK++)
  19. 不用邀请照样申请Gmail免费邮箱
  20. Excel的Text函数详解

热门文章

  1. 奇迹暖暖一直显示服务器满怎么办,奇迹暖暖新版本常见问题汇总 玩家常见问题一览...
  2. php个性网址导航源码,仿13580个性网址导航源码PHP版
  3. 智能家居系列——灯光篇
  4. 计算机科学增刊封面,计算机科学 增刊
  5. 5G网络与4G相比,有什么区别?
  6. HDFS透明加密 从入门到放弃
  7. uml学习过程6-其他不能理解的(弱化,过于专业)
  8. Shopify 基础须知
  9. Spring Boot 排除自动配置
  10. 真正解决Could not transfer artifact org.springframework.bootspring-boot-starter-parentpom