JS的作用域链、闭包及举例说明

作用域链是什么?

1、每个函数中都会有一个[[ scope]]属性,这个属性指向一个作用域链,这个作用域链里面存有自己的作用域地址、全局的作用域地址,是一个集合

2、当一个函数被定义之后就会存在作用域链,当一个函数开始执行时就会将自己的上下文执行环境放在作用域链的最顶端

为什么要引入闭包?

我们知道JS只有两种作用域,即全局作用域和局部作用域,所以在函数内部的变量只在它当前的作用域内有效,也就是说在外部是无法访问到函数内部的变量,那么如何解决这个问题?这时候就出现了闭包这个概念

什么是闭包?

1、 闭包是有权访问另一个函数作用域中变量的函数 ,注意闭包是一个函数。通俗一点来说,在一个作用域中可以访问到另一个函数中的变量,那么这个函数就是闭包。

闭包的优缺点

闭包延长了函数内部变量的作用域,但是闭包使用过多,这样也造了内存泄露,所以我们在不使用闭包之后就清除闭包,让它直接=null

举例说明1

在这里插入图片描述

如上图所示:
在函数test()有一个匿名函数,这个匿名函数访问了test()作用域中的x变量,那么这个匿名函数就是闭包,这个匿名函数是被return出去的,也就是说明test()是有返回值的,所以用一个变量newX来接受,即var newX = test(); 这条语句相当于

   var newX = function(){
		console.log(x++);
	}  

那我们就会发现这个是一个函数表达式,所以需要newX()来调用,一旦调用,函数就会按照自己的 [[scope]] 属性指向的作用域链地址去找自己的作用域,先在自己的作用域中找 x 这个变量,但是没有找到,所以会去自己的上一级作用域中找 x , 这时x = 10; 所以执行console.log(x++); 最后打印出10 (注意先++ 与 后++ 的区别)

举例说明2

在这里插入图片描述

在上图所示,我们想点击第 i 个 div 打印 i, 可最后就会发现全部打印出来都是3,这是因为打印操作是在点击事件之后的,属于异步操作,而for属于同步操作,所以等触发点击事件时,for 这个同步操作的循环早就完成了, 所以 i 已经变为了3,最后打印出来的时候就是3

那么怎么来实现点击第 i 个 div 打印 i?如下图所示

在这里插入图片描述

如上图所示,在这里我使用一个立即执行函数 (function(){})(),立即执行函数是不需要调用就可以执行的,所以我们利用这个特点在每次循环时将 i 当作实参,用形参 m 来接收,就相当于是将每次循环时 i 的值保存起来,总共有三次循环,所以每个立即执行函数中的 m 的值都不一样,也就是说在第一次循环中的 m 是0, 第二次循环中的 m 是1, 第三次循环中的m是2, 这样在每次点击div的时候,div之后的函数会去自己的作用域去找 m,但没有找到,就会顺着自己上一级的作用域链去找 m,即会在立即执行函数中找到 m,因为 m 的值就是每次循环接收的 i 值,所以这时就实现了第 i 个div打印 i

有人会疑问,立即执行函数执行完就会销毁,为什么还会拿到 m ? 这是因为函数作用域链在我们定义完函数的时候就会存在,这时还有一个闭包产生,在这个闭包函数中的作用域链指向立即执行函数的作用域,所以即使立即执行函数执行完就会销毁,但它的执行上下文环环境被另一个函数指向,所以还是可以 拿到 m 的值

举例说明3

在这里插入图片描述

如上图所示,定义了一个数组,让数组每 i 个数 输出 i, 结果会发现每个值输出3,
这是因为 当 fun 开始执行后,在执行到循环时,每个arr[i] 都会赋值一个function(){},但注意此时函数体内的内容并未执行,等到newfun [0] () 开始调用时才开始执行里面的输出语句,但这时里面的变量 i 已经变成了 3 ; 所以最后执行每个数组函数都 会返回3,若是想让每次都输出 i , 就用立即执行函数来将 i 的值封装起来,如下图,这个原理与举例说明2 利用立即执行函数来实现输出每个i 是一样的

在这里插入图片描述

发布了12 篇原创文章 · 获赞 27 · 访问量 780

猜你喜欢

转载自blog.csdn.net/qq_36091461/article/details/105466786