You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
作用域是什么呢?它指的是你的变量和函数运行到某个地方的代码处能否被访问到。为什么需要作用域呢?为什么要限制变量的访问性而非全部暴露到公共域下呢?这是计算机科学中最基本的概念和理念:隔离性(The Principle of Least Access)。为了职责明确,你只能刚好访问到你需要的所有东西,不多也不少。附带地,它带来了模块化、命名空间等好处,让你写出更易阅读、更易维护的代码。可以说,作用域是许多现代编程语言都从语言层面支持的一个特性。
块作用域:ES6 前,{ } 大括号对 及 for (i = 0; i < 10; i++) { ... } 循环结构 均不生成词法作用域。也就是说,块里面声明的变量全部都会跑到当前的全局作用域里去。这是前 JS 时代的一个坑。ES6 以后 let 和 const 关键字都修复了这个问题,它们会生成一个块作用域,或可称为 condition/loop lexical scope
因此 ES6 时代后,可以这样理解:除了函数作用域和块作用域,其他的全是全局作用域。
if(true){dragon='dragon'// used without declaration}// output: 'dragon'console.log(dragon)
'use strict'if(true){dragon='dragon'}// output: ReferenceError: dragon is not defined
if(true){varvarVariable='var'letletVariable='let'constconstVariable='const'}console.log(varVariable)// output: varconsole.log(letVariable)// output: ReferenceError: letVariable is not definedconsole.log(constVariable)// output: ReferenceError: constVariable is not defined
上下文与作用域是非常不同的概念。作用域是静态的分析概念,它在你代码写完后就唯一确定下来了,指示变量对所有代码的可见性;而上下文是动态的运行时概念,它指的是代码运行时同个作用域下 this 值的指向。
执行上下文,是 ECMA-262 规范定义的一个概念,用于描述一段可执行代码的类型和状态。但规范对其实现模型及数据结构均未做规定,由 ECMA 引擎各自实现。这里“执行上下文”中的上下文,偏指作用域,而非一般的“上下文”概念(也即 this 指向),它也是程序运行时的概念,可以通俗理解为它是代码运行起来后,作用域概念的内存描述。
Execution context (abbreviated form — EC) is the abstract concept used by ECMA-262 specification for typification and differentiation of an executable code.
The standard does not define accurate structure and kind of EC from the technical implementation viewpoint; it is a question of the ECMAScript-engines implementing the standard.
Logically, set of active execution contexts forms a stack. The bottom of this stack is always a global context, the top — a current (active) execution context. The stack is modified (pushed/popped) during the entering and exiting various kinds of EC.
本节需要读者具备基本的 JavaScript 预备知识 #130
Scope 作用域
this
和 原型 prototype;Lexical scope 词法作用域
作用域是什么呢?它指的是你的变量和函数运行到某个地方的代码处能否被访问到。为什么需要作用域呢?为什么要限制变量的访问性而非全部暴露到公共域下呢?这是计算机科学中最基本的概念和理念:隔离性(The Principle of Least Access)。为了职责明确,你只能刚好访问到你需要的所有东西,不多也不少。附带地,它带来了模块化、命名空间等好处,让你写出更易阅读、更易维护的代码。可以说,作用域是许多现代编程语言都从语言层面支持的一个特性。
而词法作用域,指的是一个变量,你可以通过变量名引用之,而不发生引用错误(Lexical Scoping defines how variable names are resolved in nested functions)。它本质上是 静态作用域 static scopes 。
有什么类型的作用域呢?
global
的上下文仅限于一个文件;比如浏览器中 不同 tab 之间的全局变量也是不能互相访问的,因为每个标签的全局对象window
的上下文也仅限于一个 tab 中{ }
大括号对 及for (i = 0; i < 10; i++) { ... }
循环结构中的变量统统都会跑到全局作用域下去。这就很违反直觉了。ES6 通过let
和const
关键字修复了这个问题,并且赋予了一般的块 block 以块作用域在函数或块作用域中声明的变量或函数若发生嵌套,则又有了嵌套下的词法作用域规则。相对地,位于一个函数/块内部的变量或函数称位于内层作用域 inner scope;前者相对地称其位于外层的作用域 outer scope。内外之间的变量访问性规则为:
作用域什么时候生成呢?
use strict
模式禁止function() { ... }
,自动生成一个词法作用域{ }
大括号对 及for (i = 0; i < 10; i++) { ... }
循环结构 均不生成词法作用域。也就是说,块里面声明的变量全部都会跑到当前的全局作用域里去。这是前 JS 时代的一个坑。ES6 以后let
和const
关键字都修复了这个问题,它们会生成一个块作用域,或可称为 condition/loop lexical scope因此 ES6 时代后,可以这样理解:除了函数作用域和块作用域,其他的全是全局作用域。
作用域对于编程模型的重要意义之一,即是其体现的模块概念。你只需要关注模块内与自己相关的变量和代码,而不需考虑代码库中其他任何代码。这样你可以专注在当前代码片段上,减少编写时大脑的思考负担,也减少了维护阅读时的理解负担。这样的编程模型很符合单一职责原则(SRP),大大提高了工作效率 。是人性的。
Execution context 执行上下文
跟讲 作用域&词法作用域 的套路一样,讲执行上下文前,我们先来了解了解上下文。
上下文与作用域是非常不同的概念。作用域是静态的分析概念,它在你代码写完后就唯一确定下来了,指示变量对所有代码的可见性;而上下文是动态的运行时概念,它指的是代码运行时同个作用域下
this
值的指向。执行上下文,是 ECMA-262 规范定义的一个概念,用于描述一段可执行代码的类型和状态。但规范对其实现模型及数据结构均未做规定,由 ECMA 引擎各自实现。这里“执行上下文”中的上下文,偏指作用域,而非一般的“上下文”概念(也即
this
指向),它也是程序运行时的概念,可以通俗理解为它是代码运行起来后,作用域概念的内存描述。可以看到反过来,词法作用域 是 执行上下文的静态描述。同一个函数的词法作用域,在运行时可能会生成零个、一个或多个执行上下文,取决于它被调用多少次。虽是同一函数,但每次调用的执行上下文是分割的,不可互相访问。执行上下文即是运行时用来描述词法作用域所表示的变量对不同函数、代码可见性的一段内存和数据结构,简称环境。话有点绕,但需要深刻体会。
实现上,普通的 key-value 键值对即可做到,这与一般的 object 很像。不过,一是执行上下文有其“业务需求”——即服务于实现词法作用域——决定了这个键值对可能不会使用普通 JS 对象上所具备的所有操作;二是,它们位于截然不同的世界中,词法作用域是静态的、文本的分析概念(因为它又称静态作用域),执行上下文是运行时的实现模型。
看一下 这一节 的视频,特别赞,使用了模型、颜色来模拟运行时的内存情况以解释执行上下文。
执行上下文栈 Execution Context Stack
由于 JavaScript 是个单线程模型的编程语言,因此任一时刻,正在运行的执行上下文只能有一个,称为 active EC;其他的 EC 则依它们被调用的先后次序,形成了一个后入先出的栈结构,简称 EC Stack。最地下的 EC 通常是 Global EC。
EC 的创建与闭包实现的关键:作用域链 Scope Chain
每个函数执行时,都会生成一个 EC。EC 的生成过程分为两个阶段:
执行阶段简单,不讲;环境准备阶段,引擎会准备并填充三个变量,保存到该创建的执行上下文中。里面的这些变量是代码执行过程所需要的所有变量的集合。话说回来,这三个变量分别为:
this
关键字的值。填充好该函数的this
的值简单来说,一个 EC 经过创建阶段后,概念模型上是长这样的:
拓展阅读
我视为最佳的材料:
其他材料:
The text was updated successfully, but these errors were encountered: