Javascript 的九种作用域,你能说出几种?(2)

上一篇 / 下一篇  2022-12-12 10:45:29

  测试行业那些事儿,还有什么是你不知道?填问卷,了解详情。链接:http://vote.51testing.com/  (免费领测试技术资料,数量有限,先填先得~)

  7.With Block 作用域
  大家猜下这个 with 语句里的作用域是是啥:
  想必你猜到了,with 语句里的作用域就是这个对象:
  换成普通的对象更明显一些:
  8.Closure 作用域
  闭包是 JS 的常见概念,它是一个函数返回另一个函数的形式,返回的函数引用了外层函数的变量,就会以闭包的形式保存下来。
  比如这样:
  function fun() {
      const a = 1;
      const b = 2;
      return function () {
          const c = 2;
          console.log(a, c);
          debugger;
      };
  }
  const f = fun();
  f();
  那闭包的变量怎么保存的呢?
  通过 node 可以看到:
  通过 Closure 作用域保存了变量 a 的值,这个 Closure 作用域就是闭包的核心。
  那为啥只保存了 a 没保存 b、c 呢?
  c 是返回的函数的作用域里的,不是外部作用域,而 b 则是没用到,所以 Closure 作用域里只保存了 a。
  然后执行的时候就会恢复这个 Closure 作用域:
  这样函数需要的外部变量都在 Closure 作用域里,啥也没丢,可以正常执行。
  是不是很巧妙!
  这就是闭包的核心。
  当然,Closure 作用域也可以多层,比如这样:
  function fun() {
      const a = 1;
      const b = 2;
      return function () {
          const c = 2;
          const d = 4;
          return function () {
              const e = 5;
              console.log(a, c, e);
          };
      };
  }
  const f = fun()();
  f();
  用到的外部变量分别在两个作用域里,那就会生成两个 Closure 作用域:
  只留下用到的作用域的变量 a、c。
  执行的时候就会恢复这两层闭包作用域:
  这样函数需要的外部环境一点都不少。
  理解了 Closure 作用域,就真正理解了闭包。
  闭包里还有一种特殊情况,就是 eval:
  上面的代码如果我改动一下,把打印语句变成 eval,会发生什么呢?
  function fun() {
      const a = 1;
      const b = 2;
      return function () {
          const c = 2;
          const d = 4;
          return function () {
              const e = 5;
              eval("console.log(a, c, e);");
          };
      };
  }
  const f = fun()();
  f();
  有的同学会说,这不是一样么,都会形成闭包。
  没错,都会形成闭包,但是保存的变量不一样了:
  你会发现它把所有外部的作用域的变量都保存到了 Closure 作用域,包括模块作用域的变量。
  为什么呢?
  因为它根本不会去分析字符串呀,也没法分析,万一你这段 JS 是动态从服务端获取再 eval 的呢?
  没法分析!
  没法分析怎么保证代码执行不出错呢?
  全部保存不就行了?
  所以当返回的函数有 eval 的时候,JS 引擎就会形成特别大的 Closure,会把所有的变量都放到里面。
  这样再执行 eval 的时候就不会出错了:
  所有的变量都给你了,怎么可能出错呢?
  但是这样明显性能不好,会占用更多的内存,所以闭包里尽量不要用 eval。
  前面说模块作用域是特殊的函数作用域,为什么这么说呢?
  这就与 node 模块的执行机制有关系了。
  比如这样一段代码:
  function func() {
      require;
      debugger;
  }
  func();
  执行后发现形成了闭包:
  而如果不访问模块作用域的变量,就没有这一层了:
  我这明明没有闭包的代码呀!
  这就与 node 模块的执行机制有关系了:
  node 会把模块变为一个函数,它有 exports、require、module、__dirname、__filename 这五个参数,然后传入这五个参数来执行:
  所以模块作用域就是个函数作用域而已!
  模块里的函数引用模块作用域的变量,再执行,自然就形成了闭包。
  9.Eval 作用域
  最后一种特殊的作用域就是 eval 作用域了。
  比如这样一段代码:
  eval(`
      const a = 1;
      const b = 2;
      const c = 3;
      console.log(a,b,c);
      debugger;
  `);
  执行之后是这样的:
  可以看到有单独的 Eval 作用域,eval 的代码里声明的变量都在这个作用域里:
  总结
  JS 总共有 9 种作用域,我们通过调试的方式来分析了下:
  ·Global 作用域:全局作用域,在浏览器环境下就是 window,在 node 环境下是 global
  · Local 作用域:本地作用域,或者叫函数作用域
  · Block 作用域:块级作用域
  · Script 作用域:let、const 声明的全局变量会保存在 Script 作用域,这些变量可以直接访问,但却不能通过 window.xx 访问
  · 模块作用域:其实严格来说这也是函数作用域,因为 node 执行它的时候会包一层函数,算是比较特殊的函数作用域,有 module、exports、require 等变量
  · Catch Block 作用域:catch 语句的作用域可以访问错误对象
  · With Block 作用域:with 语句的作用域就是传入的对象的值
  · Closure 作用域:函数返回函数的时候,会把用到的外部变量保存在 Closure 作用域里,这样再执行的时候该有的变量都有,这就是闭包。eval 的闭包比较特殊,会把所有变量都保存到 Closure 作用域
  · Eval 作用域:eval 代码声明的变量会保存在 Eval 作用域
  上面这些都是调试得出的,是 JS 引擎执行代码时的真实作用域。
  JavaScript 的 9 种作用域,你能说出几种??

TAG: 软件开发 Java java

 

评分:0

我来说两句

Open Toolbar