前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS全局变量

JS全局变量

作者头像
前端柒八九
发布2022-08-25 14:14:18
12.8K0
发布2022-08-25 14:14:18
举报
文章被收录于专栏:柒八九技术收纳盒

❝你从互联网上学东西、掌握新的技能,或者互联网帮助你实现自我,那么互联网就是你的工具;如果你只是在互联网上玩乐,花了自己的时间和金钱,却只得到了精神的满足,那你是互联网的工具。在免费的江湖里,你就是产品 --《向上生长》 ❞

简明扼要

  1. 作用域是「静态」
  2. 作用域通过「词法环境」实现的
  3. 词法环境 = 环境记录 + OuterEnv
  4. 变量的作用域链由词法环境中OuterEnv串联
  5. 在浏览器环境下,globalThis不直接指向全局对象
  6. WindowProxy是一个将所有访问转发到当前窗口的对象
  7. 全局环境记录使用对象环境记录声明环境记录来管理变量
  8. 通过const,letclass创建的变量被绑定到声明环境记录中
  9. 通过var和函数声明的变量被绑定到对象环境记录中
  10. 声明环境变量中变量优先访问
  11. 使用ECMAScript和宿主环境的内置全局变量初始化全局对象

文章概要

  1. Scope(作用域)
  2. Lexical Environments (词法环境)
  3. 全局对象(global object)
  4. 浏览器环境下的globalThis
  5. 全局环境(global envrionment)
  • Script 作用域 和Module 作用域
  • 生成变量: 声明环境记录 vs 对象环境记录
  • 访问变量
  • 全局ECMAScript变量和全局宿主变量
  1. 一图胜前言

1. Scope(作用域)

我们平时常说的变量的作用域(scope),它全名应该叫「词法作用域」(lexical scope)。它是程序中可以访问变量的区域,即作用域控制着变量和函数的可见性和生命周期。

我们在前期的文章中,描述了,V8执行JS代码核心流程 1. 先编译 2. 后执行。在这个编译的过程就是「静态」的。所以我们可以这么说,作用域是「不随代码的运行而改变」的变量查找机制。

❝JS的作用域是静态的 ❞

同时,作用域还可以被嵌套。

代码语言:javascript
复制
function func() { // (A)
  const v1 = 1;
  if (true) { // (B)
    const v2 = 2;
  }
}

如上所示: 在B行的if语句内嵌在A行的func()函数作用域。

我们把内部作用域外面的作用域称为:outer 作用域。例如:func作用域就是if作用域的 outer 作用域。

2. Lexical Environments (词法环境)

在ecma262(自备🪜)语言规范中定义:

❝作用域通过「词法环境」实现的 ❞

而词法环境由两个重要部分组成:

  1. 「环境记录」(environment record): 将变量名映射到变量值(类似于Map)。这是作用域变量的实际存储空间。记录中的「名称-值」条目称为绑定(binding)。
  2. OuterEnv内部属性:指向「外部环境」(outer environment)的引用

代码中嵌套的作用域树由指向外部环境的OuterEnv连接起来。即:

❝变量的作用域链由词法环境中OuterEnv串联 ❞

3. 全局对象

全局对象是其属性成为全局变量的对象。可以通过如下方式访问全局对象

  1. globalThis: 所有平台/宿主环境都可以访问,它与全局变量this的值相等。
代码语言:javascript
复制
// 浏览器环境
`globalThis === this //true`
// node REPL
`globalThis === this //true`
  1. 其他指向全局对象的变量只有在指定的平台才可以被访问
  • window:它在浏览器「主线程」环境下生效,但是在Web Workers/Node环境下失效
  • self: 在浏览器环境下生效(主/Web Worker),在Node环境下失效
  • global: 只有在Node环境下生效

4. 浏览器环境下的globalThis

在浏览器环境下,globalThis不直接指向全局对象。

例如,现在有一个网页存在一个iframe:

  • 每当iframe中的src的值发生变更,它会获得一个新的全局对象
  • 无论iframesrc的值如何变化,globalThis的值一直不变

现在有两个html 1. parent 2. child

parent.html

代码语言:javascript
复制
<iframe src="child.html?first"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  const icw = iframe.contentWindow; // iframe的`globalThis`

  iframe.onload = () => {
    // 通过globalThis访问iframe的全局属性
    const firstGlobalThis = icw.globalThis;
    const firstArray = icw.Array;
    console.log(icw.iframeName); // 'first'

    iframe.onload = () => {
      const secondGlobalThis = icw.globalThis;
      const secondArray = icw.Array;

      // 全局对象发生变更
      console.log(icw.iframeName); // 'second'
      console.log(secondArray === firstArray); // false

      // globalThis 还是原来的那个值
      console.log(firstGlobalThis === secondGlobalThis); // true
    };
    iframe.src = 'iframe.html?second';
  };
</script>

child.html

代码语言:javascript
复制
<script>
  // 通过globalThis向全局对象新增一个binding(环境记录中的名称-值条目称为绑定)
  globalThis.iframeName = location.search.slice(1);
</script>

globalThis通过两个内部属性保持值的不变:

  1. Window指向全局对象。每次变更location(向window.location.href赋值/通过改变iframesrc)它的值也会随之改变。
  2. WindowProxy是一个将所有访问转发到当前窗口的对象。该对象永远不会改变

在浏览器环境下,globalThis指向WindowProxy;在其他环境下,globalThis直接指向全局对象。

5. 全局环境

全局作用域是「最外层」的作用域:不存在包含它的作用域了。与之匹配的环境变量(environment)为全局环境(global environment)。每一个内部环境变量通过outerEnv构建的作用域链最终与全局环境进行相连。全局环境的outerEnv是null。

全局环境记录(注意:和全局环境有区别) 使用两类环境记录来管理变量

  • 对象环境记录(object environment record ):将binding(环境记录中的名称-值条目称为绑定) 保存在对象中。在全局环境下,这个对象指向全局对象。
  • 声明环境记录(declarative environment record): 拥有属于自己的存储空间来存放binding

5.1 Script 作用域 和Module 作用域

在JS中,只有在script的顶层才属于全局作用域。相反的,每一个模块都有属于自己的作用域,而这个作用域作为全局作用域的子集。

通过一段伪代码来描述他们之间的关系:

代码语言:javascript
复制
{ // 全局作用域 

  // (全局变量)

  { // module 1 作用域
    ···
  }
  { // module 2 作用域
    ···
  }
  // (....)
}

5.2 生成变量: 声明环境记录 vs 对象环境记录

为了能够创建一个全局变量,我们需要在全局作用域下(script的顶层),进行变量的定义和赋值:

  • 通过const,letclass创建的变量被绑定到声明环境记录中
  • 通过var和函数声明的变量被绑定到对象环境记录中
代码语言:javascript
复制
<script>
  const one = 1;
  var two = 2;
</script>
<script>
  // 所有script共享全局作用域
  console.log(one); // 1
  console.log(two); // 2
  
  // 并非所有的变量声明都被存到全局对象中
  console.log(globalThis.one); // undefined
  console.log(globalThis.two); // 2
</script>

5.3 访问变量

当我们访问一个在声明环境记录和对象环境记录中都存在绑定的变量时

❝声明环境变量中变量优先访问。 ❞

代码语言:javascript
复制
<script>
  let gv = 1; // 存放到声明环境记录中
  globalThis.gv = 2; // 存放到对象环境记录中

  console.log(gv); // 1 声明环境变量中变量优先访问
  console.log(globalThis.gv); // 2
</script>

5.4 全局ECMAScript变量和全局宿主变量

除了通过var和函数声明创建的变量之外,全局对象还包含以下属性

  • 所有ECMAScript内置的全局变量
  • 宿主环境的所有内置全局变量(浏览器、Node.js等)

使用const/let定义的全局变量可以保证不受ECMAScript和宿主环境的内置全局变量影响。

例如,浏览器环境下,存在全局变量.location

代码语言:javascript
复制
// 改变当前页面的路径信息
var location = 'https://789.com';

// 将window.location截断了,并不会修改当前页面的页面信息
let location = 'https://789.com';

注意:这种情况只有在全局环境下,才会发生

一图胜前言

全局作用域的环境通过一个全局环境记录来管理它的绑定,这个全局环境记录又基于两个环境记录:

  1. 对象环境记录,它的绑定存储在全局对象中
  2. 声明性环境记录使用内部存储来存储它的绑定

可以通过向全局对象添加属性或通过各种声明来创建全局变量。使用ECMAScript和宿主环境的内置全局变量初始化全局对象。每个ECMAScript模块都有自己的环境,其外部环境是全局环境。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端柒八九 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简明扼要
  • 文章概要
  • 1. Scope(作用域)
  • 2. Lexical Environments (词法环境)
  • 3. 全局对象
  • 4. 浏览器环境下的globalThis
  • 5. 全局环境
    • 5.1 Script 作用域 和Module 作用域
      • 5.2 生成变量: 声明环境记录 vs 对象环境记录
        • 5.3 访问变量
          • 5.4 全局ECMAScript变量和全局宿主变量
          • 一图胜前言
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档