博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端提高篇(十九)JS进阶14闭包
阅读量:97 次
发布时间:2019-02-26

本文共 2755 字,大约阅读时间需要 9 分钟。

前言:

关于闭包的入门篇在,初步引入了闭包的概念,在这篇文章中有一个计数器的案例,如果要兼顾计数变量的安全性和计数功能的实现,使用闭包是一个很好的办法
关于计数器的实现方法,的最后提供了另一种思路:使用函数的对象特性,巴拉巴拉

正文

闭包三要素
1.嵌套结构的函数
2.内部函数访问了外部函数的变量
3.在外部函数之外,调用内部函数

作用

1.让局部变量变成私有化的长存变量
2.给事件调用的函数传递参数

或者内部函数赋值给全局变量,扩大作用域,让内部函数可以在大环境下被调用,运行结果同上

也可以有多个内部函数,操作外部函数的同一个变量

运行结果:

在这里插入图片描述
闭包究竟是什么?
概念:闭包是由函数以及创建该函数的语法环境组成的,这个环境包含了这个闭包创建时所能访问的所有局部变量
就像是有一个简易的类特性:
闭包操作可以看成闭包工厂和闭包对象构成;
由闭包工厂生成闭包对象,每个闭包对象互不干扰;
闭包对象中都存储着工厂给的局部变量,和一个函数,有一个指针指向闭包对象中的函数,使用这个函数;
闭包对象在内存中保存着,直到很长一段时间内都没有使用,系统自动回收;(所以那个x,没有在f函数执行结束之后就失效)
在这里插入图片描述

//闭包工厂(函数)        function f(){
var x = 1; function e(){
x++; console.log(x); } return e; } var f1 = f();//闭包对象1 var f2 = f();//闭包对象2

运行结果:执行了3次f1后,f1中x的值已经到了4,此时执行一次f2,打印出x的值为2,可以看出,两个闭包对象不干扰

在这里插入图片描述
常见错误:
场景:循环内使用同一个闭包对象,造成变量累加

  • li1
  • li2
  • li3

运行结果:不论点击哪一个li,都是输出4

在这里插入图片描述
解析:
setClick函数满足了闭包的要素,里面的匿名函数使用了外部函数创建的变量i,上面代码的for循环,执行之后的结果是:
在这里插入图片描述
可以看到,setClick函数执行后,i已经变成3,并且长存在内存中,所以当我们点击其中一个li,showId出来的,都是4

我们可以通过两种办法解决这个问题

方法一:使用闭包工厂创建多个闭包对象

function showId(id){
console.log(id); } function clickFunc(id){
function e(){
showId(id); } return e; } function setClick(){
var ary = document.getElementsByTagName('li'); for (var i = 0; i < ary.length; i++){
ary[i].onclick = clickFunc(i + 1); } } setClick();

运行效果:

在这里插入图片描述
在这里插入图片描述
方法二:使用立即执行函数,创建多个闭包对象

function showId(id){
console.log(id); } function setClick(){
var ary = document.getElementsByTagName('li'); for (var i = 0; i < ary.length; i++){
(function (){
var id = i; ary[id].onclick = function(){
showId(id + 1); } })(); } }

效果同上:

在这里插入图片描述
调试如下:
在这里插入图片描述

或者不使用闭包

function setClick(){
var ary = document.getElementsByTagName('li'); for (var i = 0; i < ary.length; i++){
let id = i; ary[id].onclick = function(){
showId(id + 1); } } }

效果同上

调试出的各个li的onclick函数也一样

整理一下,应该是,

因为setClick函数里,循环时定义的变量i,在内部函数被直接使用,所以setClick相当于是一个闭包工厂,它的局部变量i是长存的状态,ary[i].onclick在同一个闭包对象中;
当我们点击其中一个li时,showId得到的参数是i+1=3+1=4,而i到达3之后,在闭包中是不再存储累加结果的,所以showId得到的i一直是3;
如果我们将i与id区分开,就能避免i的累加对内部函数参数的影响,即在setClick的内部函数的内部创建了多个闭包对象(记为ID1,ID2,ID3),i依然长存,但不影响闭包对象ID们的参数
循环时,随着i的累加,而创建出闭包对象,i+1已经传到变量id上了
在这里插入图片描述

所以,在使用for循环给内部函数传参时,如果不想要i的累加影响内部函数,简单的办法就是,使用立即执行,或直接用let定义一个变量,隔开内部函数与外部变量的直接联系

如果有更能说服我的理解,我再来更新

转载地址:http://ziuk.baihongyu.com/

你可能感兴趣的文章