JS从0开始(六)作用域、作用域链、闭包

193 阅读2分钟

之前说到的 function创立一个独立的作用域 其实不太准确

1.隐式属性

每个对象都有它的属性跟方法。
方法就是键名后面跟匿名函数
属性就是键名后面加字符串等

函数function就是一个对象,它也有它的属性
函数也是一种对象类型、引用类型、引用值

对象有些属性是我们无法访问的,这些属性就是JS引擎内部固有的隐式属性
(这些隐式属性是不供外部访问的,也可以理解为内部的私有属性)
我们研究这些隐式属性就是为了更好的理解其原理,更好的写代码

如下面这个 隐式属性:[[scope]] (scope就是域的意思):

  • 1.是函数创建时,生成的一个JS内部的隐式属性,只能 JS 引擎读取
  • 2.它是函数存储作用域链的容器
    作用域链里面存储的就是AO(函数的执行期作用域)、GO(全局的执行期作用域)

细节:当函数执行完成以后,AO是要销毁的,下一次执行要重新建立一个新的AO, 也就是说AO是即时的存储容器

2. [[scope]]步骤

  • 步骤1
  1. 生成GO(所以说每一个函数作用域链里面都有一个GO)
  2. 当函数被定义时 生成了scope
  • 步骤2
  1.   a()执行时生成AO;
    最顶端生成AO;GO往下移
    一个代码块中。每一个函数都有一个自己的AO,和共同(全局)的GO !
  • 步骤3
  • 步骤4
  • 步骤5

例子:

 function a(){
   function b(){
      function c(){
      } c(n);
   }  b(n);
 }  a();

a 定义: a.[[scope]] ->第0位 0 : GO
a 执行: a.[[scope]] -> 第0位 0: a -> AO;    1 : GO
b定义: b.[[scope]] -> 第0位 0 : a ->AO;    1 : GO
b 执行: b.[[scope]] ->第0位 0 : b ->AO;    1 : a ->AO     2: GO
c 定义: c.[[scope]] -> 第0位 0 : b ->AO;    1 : a-->AO;    2:GO
c 执行: c.[[scope]] ->第0位 0 : C-->AO     1:b ->AO;     2 : a-->AO;    3:GO
c 结束: c.[[scope]] 1:b ->AO;     2 : a-->AO;    3:GO
b 结束: b.[[scope]] -> 第0位 0 : a ->AO;    1 : GO    同时c.[[scope]] 没有了
a 结束: a.[[scope]] ->第0位 0 : GO     同时b.[[scope]] 没有了

3.闭包

<script>
function test1(){
   function test2(){
     var b = 2;
     consolie.log(a);
   }
   var a = 1;
   return test2();
}
var c = 3;
var test3 = test1();
test3();
<script>

步骤1: 步骤2:

步骤3: test2此时没有执行(默认最后执行里面的函数(b执行前一刻c定义,之后c再执行),即在test1执行时:执行完var a=1、return test2后再执行test2()函数,但是return这个东西,一旦执行就直接结束整个方法 所以此时test 2依旧不执行)

步骤4:

步骤5:

总结:

* 当内部函数被返回到外部并保存时,一定会产生闭包(一般出现return会出现闭包)

  • 闭包会产生原来的作用域链不释放, 所以过度的闭包可能会导致内存泄漏(别人的AO太多了,占用内存),或加载过慢。
    闭包实例中的作用:
  <script>
  /面包管理
   function breadMgr(num){
     var  breadNum = arguments[0] || 10;
      function supply(){
          breadNum += 10;
          console.log(breadNum);
      }
      function sale() {
          breadNum --;
          console.log(breadNum);
      }
       return [supply,sale];
   }   
   var breadMgr = breadMgr(50);
   breadMgr[0]();//60
   breadMgr[1]();//59
   breadMgr[1]();//58
   breadMgr[1]();//57
   breadMgr[0]();//67
   breadMgr[0]();//77
   breadMgr[1]();//76
   
   </script>

上述共用一个breadMgr(num)的AO,所以递归过来的两个方法一起改变这个AO;