JS高级教程知识整理——11闭包

00 引入闭包的概念

需求:点击某个按钮,提示弹出的是第n个按钮.

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button>测试一</button>
    <button>测试二</button>
    <button>测试三</button>
    <script>

        //第一种实现方法(不能实现,因为i循环后才执行onclick)
        // var btn = document.getElementsByTagName('button')
        // for(var i = 0, length = btn.length; i < length ; i++) { //i是全局变量,i只有一个
        //     var btns = btn[i]
        //     btns.onclick = function () {  //i循环后才执行onclick
        //         alert('第'+(i+1)+'个')    //i = 4
        //     }
        // }

        //第二种实现方法
        // var btn = document.getElementsByTagName('button')
        // for(var i = 0,length = btn.length; i < length ; i++){
        //     var btns = btn[i]
        //     btns.index = i  //给出每个按钮对应的下标
        //     btns.onclick = function(){
        //         alert('第'+(this.index+1)+'个')
        //     }
        // }

        //使用闭包的实现方法
        var btn = document.getElementsByTagName('button')
        for(var i = 0, length = btn.length; i < length ; i++) { 
            (function(i) {    //i是局部变量 全局i传给局部i,i就进入闭包
                var btns = btn[i] //i是局部变量
                btns.onclick = function () {  
                    alert('第'+(i+1)+'个')    
                }
            })(i)  //i是全局变量
        }
    </script>
</body>
</html>

01理解闭包

1.如何产生闭包
*当一个嵌套的内部函数(子)引用了嵌套的外部函数(父) 的 变量(函数)时,就产生了闭包
2.闭包到底是什么
* 使用chrome调试查看
* 理解一: 闭包是嵌套的内部函数(绝大部分人)
* 理解二: 包含被引用变量(函数)的对象(极少数人) 即a
* 注意: 闭包存在于嵌套的内部函数中
3.产生闭包的条件
*函数嵌套
*内部函数引用了外部函数的数据(变量/函数)
*函数调用

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function fn1(){
            var a = 2
            var b = 'abc'
            function fn2() {  //执行函数定义,调用发fn1就执行函数定义,而不需要调用函数fn2  //执行函数定义就会产生闭包,不需要调用内部函数
                console.log(a)  
            }
        }
        fn1()
    </script>
</body>
</html>

闭包的两种理解(附图解释):

* 理解一: 闭包是嵌套的内部函数(绝大部分人) 
* 理解二: 包含被引用变量(函数)的对象(极少数人) 即a

在这里插入图片描述

02常见的闭包

常见的闭包形式:
1.将函数作为另一个函数的返回值
2.将函数作为实参传递给另一个函数调用
产生多少个闭包:外部函数调用几次,产生多少个内部函数对象

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 1.将函数作为另一个函数的返回值
        function fn1() {
            var a = 2
            function fn2() {
                a++
                console.log(a)
            }
            return fn2
        }
        var f = fn1() //存返回值fn2函数
        f() //3 //调用内部函数fn2  //操作内部函数的变量
        f() //4 //再执行一遍 //调用内部函数fn2 //操作内部函数的变量

        //因为执行外部函数才会创建内部函数对象,跟内部函数执行几次没有关系
        fn1() //执行fn1()就把内部函数又创建了一遍 //看外部函数执行几次就创建几个闭包

        // 2.将函数作为实参传递给另一个函数调用 
        function showDelay(msg, time) {
            setTimeout(function () {
                alert(msg)  //用到了外部函数变量msg
            }, time)
        }
        showDelay('kate', 2000)
    </script>
</body>
</html>

03.闭包的作用

1.使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
2.让函数外部可以操作(读写)到函数内部的数据(变量/函数) 存在才可以操作
问题:
1.函数执行完后,函数内部声明的局部变量是否还存在? 一般是不存在,存在于闭包中的变量才可能存在
2.在函数外部能直接访问函数内部的局部变量吗 不能,但可以通过闭包让外部操作它

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>闭包的作用</title>
</head>
<body>
    <script>
        function fn1() {
            var a = 2
            function fn2() {  //fn2和fn3都是内部变量,执行完后会被释放
                a++
                console.log(a)
            }
            function fn3() {  //fn2和fn3都是内部变量,执行完后会被释放,fn3没有成为垃圾对象,因为f引用着
                a--  //a被fn3函数引用了,所以进入闭包的是a
                console.log(a)
            }
            return fn3 
        }
        var f = fn1()  //f指向函数对象,把返回值存起来了,函数对象关联着闭包,闭包里有a  //让内部函数中的变量存活在内存
        f() //1 //操作内部函数的变量
        f() //0 //操作内部函数的变量
    </script>
</body>
</html>

04.闭包的生命周期

1.产生: 在嵌套内部函数定义执行完时就产生了(不是在调用的时候产生)
2.死亡: 在嵌套的内部函数成为垃圾对象时

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        function fn1() {
            //调用fn1,闭包就已经产生了(因为函数提升,内部函数对象已经创建了)
            var a = 2
            // function fn2() {
            //     a++
            //     console.log(a)
            // }
            var fn2 = function() {
                a++
                console.log(a)
            }
            //产生闭包,函数定义执行,前面是函数执行
            return fn2
        }
        var f = fn1()
        f() // 3
        f() // 4
        f = null //闭包死亡(包含闭包的函数对象成为了垃圾对象)
    </script>
</body>
</html>

05.闭包的应用

闭包的应用: 定义js模块
* 具有特定功能的js文件
* 将所有的数据和功能都封装在一个函数内部(私有的)
* 只向外暴露一个包含n个方法的对象或函数
* 模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

实现代码:
myModule.js

function myModule() {
    //私有的数据
    var msg = 'bigface'
    //操作数据的函数
    function doSomething() {
        console.log('doSomething()'+ msg.toUpperCase())
    }
    function doOtherthing() {
        console.log('doOtherthing()'+ msg.toLowerCase())
    }
    //向外暴露对象(给外部使用的方法)
    return {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="myModule.js"></script>
    <script>
        // var fn = myModule()
        // fn()

        //要先执行myModule(),返回对象,对象再调用方法
        var module = myModule()
        module.doSomething()
        module.doOtherthing()
    </script>
</body>
</html>

实现代码2:
myModule2.js

(function () {
    //私有的数据
    var msg = 'bigface'
    //操作数据的函数
    function doSomething() {
        console.log('doSomething()'+ msg.toUpperCase())
    }
    function doOtherthing() {
        console.log('doOtherthing()'+ msg.toLowerCase())
    }

    //要暴露的东西添加为window的属性
    window.myModule2 = {
        doSomething: doSomething,
        doOtherthing: doOtherthing
    }
})()

// // 好处:代码压缩,如window可以压缩成w
// (function (window) {  //+window
//     //私有的数据
//     var msg = 'bigface'
//     //操作数据的函数
//     function doSomething() {
//         console.log('doSomething()'+ msg.toUpperCase())
//     }
//     function doOtherthing() {
//         console.log('doOtherthing()'+ msg.toLowerCase())
//     }

//     //要暴露的东西添加为window的属性
//     window.myModule2 = {
//         doSomething: doSomething,
//         doOtherthing: doOtherthing
//     }
// })(window)  //+window
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 第一种方式需要先执行myModule(),第二种直接使用 两种方式都有闭包-->
    <script src="myModule2.js"></script>
    <script>
        myModule2.doSomething()
        myModule2.doOtherthing()
    </script>
</body>
</html>

06.闭包的缺点及解决

1.缺点
* 函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
*容易造成内存泄露
2.解决:
*能不用闭包就不用
*及时释放

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>闭包的缺点及解决</title>
</head>
<body>
    <script>
        function fn1() {
            var arr = new Array[100000]
            function fn2() {
                console.log(arr.length)
            }
            return fn2
        }
        var f = fn1()  //必须让f指向内部函数对象,不然fn1执行完arr局部变量就被释放掉了
        f()

        f = null //让内部函数成为垃圾对象-->回收闭包
    </script>
</body>
</html>

07.面试题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>07_面试题1</title>
</head>
<body>
    <script>
        // 代码片段一
        //没有闭包
        var name = "the window"
        var object = {
            name: "my object",
            getNameFunc: function() {
                return function() {
                    return this.name
                }
            }
        }
        alert(object.getNameFunc()()) //the window    //object.getNameFunc() 是return 的fun,然后fun(),所以this是指window
    
        // 代码片段二
        //有闭包:函数嵌套 和 内部函数引用外部函数变量
        var name2 = "the window"
        var object2 = {
            name: "my object",
            getNameFunc: function() {
                var that = this  //object2
                return function() {
                    return that.name2
                }
            }
        }
        alert(object2.getNameFunc()()) //my object
    </script>
</body>
</html>

补充:内存溢出与内存泄露

1.内存溢出
* 一种程序运行出现的错误
* 当程序运行需要的内存 超过了 剩余的内存时,就出抛出内存溢出的错误
2.内存泄露
*占用的内存没有及时释放
*内存泄露积累多了就容易导致内存溢出
*常见的三种内存泄露:
**意外的全局变量
**没有及时清理的计时器或回调函数
**闭包

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>内存溢出与内存泄露</title>
</head>
<body>
    <script>
        // 1.内存溢出
        var obj = {}
        for (var i = 0;i < 10000; i++) {
            obj[i] = new Array(10000000)
            console.log('------')
        }

        // 2.内存泄露
            // 意外的全局变量
        function fn() {
            a = 3  //用var去定义变量
            console.log(a)
        }
        fn()

            // 没有及时清理的计时器或回调函数
        var intervalId = setInterval(function () {
            console.log('------')
        },1000)
        clearInterval(intervalId)

            // 闭包
        function fn1() {
            var a = 4
            function fn2() {
                console.log(++a)
            }
            return fn2
        }
        var f = fn1()
        f()

        f = null
    </script>
</body> 
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值