js: 闭包

闭包的作用:变量的私有化,不让它污染全局作用域。使局部变量拥有全局生命周期

        var counter = 0;
// 全局变量: 谁都可以访问、修改
function add() {
    counter++;
    console.log(counter);
}
function add2() {
    counter = 0;
}

add();
add2(); // 其他函数来修改变量
add();  // 变量被污染

      

v2-f61068810837a28fe98e30b3aea59153_b.png

改进:

在add函数中再定义一个plus函数,如果只看plus函数,counter++,因为没有声明,所有它是一个全局变量。

        function add() {
    var counter = 0;
    // 局部变量
    function plus() {
        counter++;
        // 如果把plus函数单独看作一个函数,counter没有声明,所有它是全局变量
        console.log(counter)
     }
}

      

问题:

如果我们能够访问到plus函数,那么就可以执行函数了,所以我们把plus函数写成匿名函数,并给全局变量plus。

        function add() {
    var counter = 0;
    // 局部变量
    plus = function() {
    // 使plus也成为全局函数
        counter++;
        console.log(counter)
    }
}
add()
plus()
plus()

      

先执行add()初始化,即可访问plus函数

v2-426a0b98ac4114436bc417d1134581bf_b.png

改进: 优化

使用函数的立即执行

        var plus = (function() {
 var counter = 0;
 return function() {
     counter++;
     console.log(counter)
 }
})()
plus()
plus()
plus()

      

v2-d3b68e7fb83c37d2d4114f37c9070b02_b.jpg



闭包进阶:

三要素:

  1. 嵌套结构的函数
  2. 内部函数访问外部函数的变量
  3. 在外部函数的外面,调用内部函数

简单的闭包:

        // 闭包
function f() {
    var a = 1;
    function e() {
        a++;
        console.log(this)
        console.log(a)
    }
    return e;
}
var e = f();    // 在外部函数之外通过外部函数获取的内部this指向
e();

      

v2-184419d632acd56abc8bee8328a13dbd_b.jpg

如果缺少了其中一样,比如不在外部函数的外面调用内部函数,而是直接使用。

普通函数:

        // 普通函数
function f1() {
    var a1 = 1;
    function e1() {
       a1++;
       console.log(this)
       console.log(a1)
    }
    e1(); // 通过调用f1函数让内部函数自己执行
}

      

v2-04b8191e89d52a2ab7e2f411043e7d60_b.jpg

问题: 外部函数一定要return吗

答: 不一定,只要在外部函数外面可以访问到内部函数(声明需要声明在外面)也行。比如个内部函数一个名字,在执行外部函数后,再执行内部函数也可以。如

        var fee;
function f1() {
    var a1 = 1;
    fee = function e1() { // 通过赋值,在外面可以访问到
        a1++;
        console.log(a1)
    }
}

      

v2-a885a1ea7a8a7125957e4fd809a2e21d_b.jpg

概念:

闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了这个闭包创建时所能访问的所有局部变量。

类似简易类特性: 闭包工厂(函数) -> 闭包对象

new出来的闭包对象,一个与一个之间没有关系。

闭包对象中的值能够调用是因为长存在内存之中,js会自动处理不使用的闭包对象(回收机制)

         // 闭包工厂
 function f() {
     var a = 1;
     function e() {
         a++;
         console.log(a)
     }
     return e;
}

 // 闭包对象1
 var f1 = f();
 // 闭包对象2
 var f2 = f();

      

v2-2a25c38d5f282076719108d2d7fecaf3_b.jpg

作用:

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


常见错误: 循环内使用闭包

假设我们有一个li数组,要给每隔dom上添加一个点击事件,事件内容为打印被点击的索引。

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

// 给每个li绑定这个点击事件函数
function showId(id) {
    console.log(`li索引是: ${id}`)
}

 setClick(); // 执行一下

      

v2-57d669e551bfcb255836a0f777a23b76_b.jpg

但执行结果发现,不管点击哪里li,它打印的索引都是5

这是因为li的事件函数虽然绑定了,但是事件函数并未执行,而索引从i=0, 被for循环到了5,这个i都是在同一个内存中的,直接就是被修改修改,当点击其中一个li的时候,自然也就打印了5。

解决办法有两种:

  1. 使用立即执行函数(定义在函数内部的匿名闭包)
        (function setClick() {
    var arr = document.getElementsByTagName('li');
    for(var i = 0; i < arr.length; i++) {
        (function() {
            var id = i; // 这一步不能省略 闭包的条件
            arr[id].onclick = function () {
                showId(id)
            }
        })()
    }
})();

 // 事件函数
function showId(id) {
    console.log(`li索引是: ${id}`)
}

      

v2-14c5357351630b074ce84ef796f3c0dd_b.jpg
  1. 使用多个闭包:每一次循环在闭包工厂中调用创建闭包对象,使其拥有独立的i
        function setClick() {
    var arr = document.getElementsByTagName('li');
    for(var i = 0; i < arr.length; i++) {
        arr[i].onclick = clickMake(i);
        // 每一次循环在闭包工厂中调用创建闭包对象,使其拥有独立的i
    }
}
// 事件函数
function showId(id) {
    console.log(`li索引是: ${id}`)
}

// 闭包工厂
function clickMake(id) {
    var id = id; // 可省略
    function e() {
        showId(id)
    }
    return e;
}

setClick(); // 执行一下

      

v2-3e0de61d014beb95190bdfe50dc7933e_b.jpg

简写:

        (function setClick() {
    var arr = document.getElementsByTagName('li');
    for(var i = 0; i < arr.length; i++) {
        arr[i].onclick = clickMake(i);
        // 每一次循环在闭包工厂中调用创建闭包对象,使其拥有独立的i
    }
})();

 // 事件函数
function showId(id) {
    console.log(`li索引是: ${id}`)
}

 // 闭包工厂
function clickMake(id) {
    return function() {
       showId(id)
   }
}

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值