js作用域链、预编译、闭包基础

77 阅读3分钟

作用域链、预编译、闭包基础

目录

作用域链

预编译

闭包基础

为啥要学AO和GO

  • 因为后续学习作用域和作用域链需要用到AO和GO的概念

AO

  • function 独立放东西的仓库
  • 在学习作用域和作用域链前需要讨论了解对象

函数的属性

  • 函数也是一种对象类型,(引用类型、引用值)
  • 函数是一种对象所以函数也有属性,例如.name.length.prototype
  • 对象有些属性是无法访问的,这些属性是JS引擎内部固有的隐式属性,也可以说是天生的

一、作用域链

隐式属性[[scope]]

  • [[scope]]是函数创建时,生成的一个js内部的隐式属性
  • 这个属性是函数存储作用域链的容器
  • 在函数执行完后,AO会被销毁
  • AO是即时存储容器

函数的作用域链

  • 只要函数被定义就会生成该函数的作用域和作用域链GO

eb7d68ec0e92e3d986ad7f849057dd4.jpg

  • 每一个函数在执行时作用域链上都会有GO(全局执行上下文)和AO

df02a226a455af4095363a1d67cc56a.jpg

  • 函数被提升时该函数的作用域链上就有GO,执行时生成自己的AO

265a1615f028f7046a8ce032deabd8f.jpg

ee07841e27aeb20a672dd61901752a4.jpg

二、预编译

scope属性

  • 函数的scope属性储存的是作用域链的地址
  • 先保存GO再保存自己的AO从上往下存储,找外部变量时从上往下查找
  • 所以作用域内可以访问作用域外的数据,而作用域外确不能访问作用域内的数据

函数作用域流程

  • 全局执行前,GO内函数定义提升,var变量提升(函数字面量不提升)
  • 全局执行时,解释一行,执行一行(赋值、执行函数)
  • 函数被定义时,就有[[scope]] -> 作用域链存的只有GO
  • 函数执行时,生成自己的GO并存进作用域链内
 function a() {
   function b() {
     function c() {}
     c();
   }
   b();
 }
 a();
 ​
 /*
   a定义:a.[[scope]] -> 0 : GO
   a执行时:a.[[scope]] -> 0 : a -> AO
                           1 : GO
   b定义: b.[[scope]] -> 0 : a -> AO
                         1 : GO
   b执行: b.[[scope]] -> 0 : b -> AO
                         1 : a -> AO
                         2 : GO
   c定义: c.[[scope]] -> 0 : b -> AO
                         1 : a -> AO
                         2 : GO
   c执行: c.[[scope]] -> 0 : c -> AO
                         1 : b -> AO
                         2 : a -> AO
                         3 : GO
   c结束: c.[[scope]] -> c的AO销毁
                         0 : b -> AO
                         1 : a -> AO
                         2 : GO
   b结束: b.[[scope]] -> b的AO被销毁
                         0 : a -> AO
                         1 : GO
   a结束: a.[[scope]] -> a的AO被销毁
                         0 : GO
   */

为什么作用域外访问不到作用域内的变量

  • 因为外部作用域链没有存作用域内的AO

三、闭包基础

  • 当内部函数被返回到外部并保存时,一定会产生闭包
  • 闭包会让原生的作用域链不释放
  • 过度是用闭包可能会造成内存泄露,或加载过慢
  • 闭包可以做数据缓存

代码示例

 // 闭包
 function test() {
   function test2() {
     var b = 2;
     console.log(a);
   }
   var a = 1;
   return test2();
 }
 var c = 3;
 var test3 = test();
 test3;
 // 当test1函数被执行结束时,因为test2倍返回到外部,且被全局变量test3接收。
 // 这时test1的AO并没有销毁,只是把线剪断了,test2的作用域链还连着的

闭包实例1

 // 闭包实例1
 function test() {
   var n = 100;
   function add() {
     n++;
     console.log(n);
   }
   function reduce() {
     n--;
     console.log(n);
   }
   return [add, reduce];
 }
 var arr = test();
 arr[0](); // 101
 arr[1](); // 100

闭包实例2

 // 闭包案例2
 function breadMgr(num) {
   var breadNum = num || 10;
   function supply() {
     breadNum += 10;
     console.log(breadNum);
   }
   function sale() {
     breadNum--;
     console.log(breadNum);
   }
   return [supply, sale];
 }
 var breadMgr = breadMgr();
 bareadMgr[0](50) // 60
 bareadMgr[1]() // 59

\