重学前端-总结:17 、JavaScript执行(二):闭包和执行上下文到底是怎么回事?

重学前端-总结:17 、JavaScript执行(二):闭包和执行上下文到底是怎么回事?

上节讲:由 JavaScript 引擎发起”还是“由宿主发起”,分成了宏观任务和微观任务,

本节讲:函数的执行

常见的名称概念有:

  • 闭包;
  • 作用域链;
  • 执行上下文;
  • this 值。

函数执行过程相关的知识图:

闭包概念

闭包翻译自英文单词 closure

  • 编译原理中,它是处理语法产生式的一个步骤;
  • 计算几何中,它表示包裹平面点集的凸多边形(翻译作凸包);
  • 编程语言领域,它表示一种函数。

这个古典的闭包定义中,闭包包含两个部分。

  • 环境部分

    • 环境
    • 标识符列表
  • 表达式部分
    在 JavaScript 中找到对应的闭包组成部分。

  • 环境部分

    • 环境:函数的词法环境(执行上下文的一部分)
    • 标识符列表:函数中用到的未声明的变量
  • 表达式部分:函数体

常见的概念误区,有些人会把 JavaScript 执行上下文,或者作用域(Scope,ES3 中规定的执行上下文的一部分)这个概念当作闭包。

实际上 JavaScript 中跟闭包对应的概念就是“函数”,早年间理解成的“作用域”,这个比较片面。

执行上下文:执行的基础设施

JavaScript 标准把一段代码(包括函数),执行所需的所有信息定义为:“执行上下文”。

执行上下文在 ES3 中,包含三个部分。

  • scope:作用域,也常常被叫做作用域链。
  • variable object:变量对象,用于存储变量的对象。
  • this value:this 值。

在 ES5 中三个部分改为下面。

  • lexical environment:词法环境,当获取变量时使用。
  • variable environment:变量环境,当声明变量时使用。
  • this value:this 值。

在 ES2018 中,执行上下文又变成了这个样子

  • lexical environment:词法环境,当获取变量或者 this 值时使用。
  • variable environment:变量环境,当声明变量时使用
  • code evaluation state:用于恢复代码执行位置。
  • Function:执行的任务是函数时使用,表示正在被执行的函数。
  • ScriptOrModule:执行的任务是脚本或者模块时使用,表示正在被执行的代码。
  • Realm:使用的基础库和内置对象实例。
  • Generator:仅生成器上下文有这个属性,表示当前生成器。

以下的这段 JavaScript 代码:

    var b = {}let c = 1this.a = 2;

要想正确执行它,我们需要知道以下信息:

  1. var 把 b 声明到哪里;
  2. b 表示哪个变量;
  3. b 的原型是哪个对象;
  4. let 把 c 声明到哪里;
  5. this 指向哪个对象。

var 声明与赋值,let,realm 三个特性来分析上下文提供的信息,分析执行上下文中提供的信息。

var 声明与赋值

var 声明作用域函数执行的作用域。也就是说,var 会穿透 for 、if 等语句。

值得特别注意的是,有时候 var 的特性会导致声明的变量和被赋值的变量是两个 b,JavaScript 中有特例,那就是使用 with 的时候:

var b;
void function(){
    var env = {b:1};
    b = 2;
    console.log("In function b:", b);
    with(env) {
        var b = 3;
        console.log("In with b:", b);
    }
}();
console.log("Global b:", b);

在 Global function with 三个环境中,b 的值都不一样,而在 function 环境中,并没有出现 var b,这说明 with 内的 var b 作用到了 function 这个环境当中。

var b = {} 这样一句对两个域产生了作用,从语言的角度是个非常糟糕的设计,这也是一些人坚定地反对在任何场景下使用 with 的原因之一。

let

let 是 ES6 开始引入的新的变量声明模式,比起 var 的诸多弊病,let 做了非常明确的梳理和规定。

为了实现 let,JavaScript 在运行时引入了块级作用域。也就是说,在 let 出现之前,JavaScript 的 if for 等语句皆不产生作用域。

我简单统计了下,以下语句会产生 let 使用的作用域:

  • for;
  • if;
  • switch;
  • try/catch/finally。

Realm

在最新的标准(9.0)中,JavaScript 引入了一个新概念 Realm,它的中文意思是“国度”“领域”“范围”。

但在实际的前端开发中,通过 iframe 等方式创建多 window 环境并非罕见的操作,所以,促成了新概念 Realm 的引入。

Realm 中包含一组完整的内置对象,而且是复制关系。

格外注意的问题,比如 instanceOf 几乎是失效的。

以下代码展示了在浏览器环境中获取来自两个 Realm 的对象,它们跟本土的 Object 做 instanceOf 时会产生差异:

var iframe = document.createElement('iframe')
document.documentElement.appendChild(iframe)
iframe.src="javascript:var b = {};"

var b1 = iframe.contentWindow.b;
var b2 = {};

console.log(typeof b1, typeof b2); //object object

console.log(b1 instanceof Object, b2 instanceof Object); //false true

可以看到,由于 b1、 b2 由同样的代码“ {} ”在不同的 Realm 中执行,所以表现出了不同的行为。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值