False Begginer - 6. 作用域、作用域链、预编译、闭包

128 阅读3分钟

6.1 作用域与作用域链

6.1.1 函数也是一种对象的类型,也是引用类型,引用值

var obj = {
	name:'实例',
  address:'地址',
  teach:function(){}
}

// 函数也是对象的一种类型
function fn(a, b) {}
console.log(fn.name);
console.log(fn.length);
console.log(fn.prototype);

6.1.2 函数的隐式属性(无法访问,JS引擎内部固有的隐式属性)

1. [[scope]](作用域)属性和[scope Chain](作用域链)属性

  • 每一个函数声明在定义的时候,系统自动生成 [[scope]]属性,作用域链[scope Chain] 中就已经保存了全局的执行期上下文GO;
  • [[scope]](作用域)属性是存放[scope Chain](作用域链)的容器;
  • [scope Chain](作用域链)是存放函数AO / GO的容器;
  • AO: 函数的执行期上下文 GO: 全局的执行期上下文;

2. 实例操作演示作用域和作用域链的产生与销毁

function a() {
	function b() {
  	var b = 2;
  }
	var a = 1;
  b();
}
var c = 3;
a();

// 预编译 AO,GO
GO = {
	c:undefined,
  -->3,
  a:function a(){};
}

// a函数的AO
AO = {
	a:undefined,
  --> 1
  b:function b() {}
}

// b函数的AO 
AO = {
	b:undefined;
  --> 2
}
  • a函数声明被定义

  • a函数执行和b函数声明被定义

  • b函数执行

  • b函数执行完成

  • a函数执行完成

3. 函数执行完成后,销毁AO函数的执行期上下文;再次调用函数,生成新的AO函数执行期上下文

4. 函数不调用就不会执行,如果不执行,那么内部的其他函数声明也不会被定义

5. 函数声明和函数的表达式产生作用域和作用域链的时间

// 函数声明被定义:
[[scope]] --> Scope Chain ---> GO

// 函数被执行:
AO

var fn1 = function() {};
function fn2() {};
Go = {
	fn1: undefined;
  	--> function() {};
  fn2: function fn2() {};
}

// 从预编译的情况来看:
// 函数的声明在预编译时就被定义,所以产生作用域和作用域链
// 函数的表达式是在全局环境执行的时候,匿名函数才被定义并且赋值给变量fn1,才产生作用域和作用域链

6.1.3 闭包的初始

1. 闭包定义

  • 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。
  • 闭包也是一种现象,当内部的函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的而作用域链不释放,过度的闭包可能导致内存泄漏,或加载过慢。

2. 闭包的作用域与作用域链的产生与销毁

function fn1() {
	function fn2() {
  	var b = 2;
    a = 2;
    console.log(a);
  }
  var a = 1;
  return fn2;
}
var fun = fn1(); // 如果不是闭包的作用,fn1()函数执行后,销毁fn1函数的AO执行期上下文,一并会将内部函数fn2的作用域、作用域链、fn2函数的AO执行上下文彻底销毁,fn2函数没有执行。当fn2被返回出来在全局环境下执行,理论fn2的作用域链第0位是自己的AO,第1位是GO,所以应该打印变量a应该是undefined,但是输出的是2,这是为什么呢?看下面过程
fun();

image.png