JavaScript系列-作用域&闭包

206 阅读4分钟

作用域(主要是函数预编译和执行过程阶段中)

先理解3个概念

执行期上下文

当函数执行时,会创建一个称为执行期上下文的内部对象.一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,当函数执行完毕,它所产生的执行上下文被销毁

[[scope]]

每个JavaScript函数都是一个对象(Object) 对象中有些属性我们可以访问,但有些不可以,这些属性仅供JavaScript引擎存取,[[scope]]就是其中一个,[[scope]]值的就是我们所说的作用域,其中存储了运行时期上下文集合

作用域链

[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫作用域链

查找变量

从作用域的顶端(从scope chain的第0位开始)依次向下查找

过程分析

** 例子分析1 **:

function a() {
	function b() {
		var b = 234;
	}
	var a = 123;
}
var glob = 100;
a();
  1. 当a函数被定义的时候 define

    1
    a函数刚被定义的时候: a define a.[[scope]] --> 0(第0位) GO {} //GO:全局global环境 在GO中存储了如图:this,windoe,document,global,a函数

  2. 当a函数执行时 doing

    2

a doing a.[[scope]] --> 0(第0位) AO {} a doing a.[[scope]] --> 1(第1位) GO {} scope chain 第0位:AO(Activation Object) 激活执行状态 scope chain 第1位:对应global环境

** 例子分析2:**

function a() {
	function b() {
		function c() {
			
		}
		c();
	}
	b();
}
a();
// 作用域分析
a defined a.[[scope]] -- > 0 : GO
a dong a.[[scope]] -- > 0 : aAO
                        1 : GO

b defined b.[[scope]] -- > 0 : aAO
                           1 : GO

b doing b[[scope]] -- > 0 : bAO
                        1 : aAO
                        2 : GO

c defined c.[[scope]] -- > 0 : bAO
                           2 : aAO
                           3 : GO

c doing c[[scope]] -- > 0 : cAO
                        1 : bAO
                        2 : aA0
                        3 : GO

结论1

根据函数和内部外部的作用域分析:在查找变量的时候从scope chain的最顶端开始依次往下查找变量(函数变量都一样)(如有同名的变量,作用域层级最高,则是该值)

作用域与闭包

根据上面作用域分析 分析下面例子

** 闭包例子分析1:**

function a() {
	function b() {
		var bbb = 234;
		console.log(aaa)
	}
	var aaa = 123;
	var cccc = 345;
	return b;
}
var glob = 100;
var dome = a();
dome()

分析:当a执行过后,ccc变量被回收,不会释放aaa变量 因为a执行时返回b b中对aaa有使用 如下图作用域分析:

3
当a执行完,a的aAO 被保存下来,成为b函数上 scope chain的作用域 然后就产生了闭包

闭包

内部函数被保存到外部(b函数被保存在了外部)

dome:需求:循环一个数,依次打印数值 ** 闭包例子分析2-bug:**

function test() {
	var arr = [];
	for(var i=0;i<10;i++){
		arr[i] = function() {
			console.log(i)
		}
	}
	return arr;
}
var mytest = test();
for(var j=0;j<10;j++){
	mytest[j]();
}
// 结果是10个10

** 分析: ** 当执行test()后 mytest数组中保存了10个函数(功能是打印这每个函数的下标值) 循环mytest,然后执行每一个函数 结果打印10个10

** 原因: ** 通过作用域: test 中有一个全局变量i(i是不断变化的) 内部的arr[i] 函数中i是使用的是test的AO中的i变量 arr一直在添加函数,却只是定义的阶段define 所以10循环结束后,test 中全局变量i=10 当执行mytest中的函数时 此时内部使用的变量i则是引用test i = 10 所以打印出来10个10

** 闭包例子分析3-good:**

function test() {
	var arr = [];
	for(var i=1;i<=10;i++){
	    // 添加立即执行函数,动态传入i的值,内部的arr[i]函数中的i变量 = 立即函数作用域提供的动态i变量,这时 arr[i]内部的i变量则 !== test提供的i变量
	    (function(i){
			arr[i] = function() {
				console.log(i)
		    }
	    })(i)	
	}
	return arr;
}
var mytest = test();
for(var j=1;j<=10;j++){
	mytest[j]();
}
// 1 2 3.....10