深入理解闭包

2020-12-29  本文已影响0人  每日log

01 什么是闭包

闭包指有权访问另一个函数作用域中变量的函数
其实,闭包就是一个内部函数

1.举例
function outer() {
    let a = 5 // outer中的变量
    function inner() {
        console.log(a) //5
    }
    inner()
}
outer()
//a是outer中的变量, inner函数的作用域访问了另一个函数即outer内部的变量 a
2.什么是闭包函数

函数inner就是一个闭包函数

function outer(){
    function inner(){
    }
}

02闭包的三个可访问作用域

1、在它自身声明之内声明的变量
function outer() {
    function inner() {
        let a = 5 // 自身
        console.log(a) // 5
    }
    inner()
}
outer()

当闭包函数inner被调用的时候,控制台会输出5
闭包函数可以访问所有在其声明内部声明的变量

2、对全部变量的访问
let b = 'global' // 全局变量
function outer() {
    function inner() {
        let a = 5
        console.log(b) // global
    }
    inner()
}
outer()

闭包能访问全局变量,此时调用闭包函数inner时,控制台会输出global

3、访问外部函数的变量
let b = 'global' 
function outer() {
    let c = 'out'  // 外部变量
    function inner() {
        let a = 5
        console.log(c) // out
    }
    inner()
}
outer()

inner函数能访问到外部函数(outer)中的变量c

03 闭包能记住它的上下文

let outerFn = (arg) => {
    let outer = "outer"
    let innerFn = () => {
        console.log(outer) // out
        console.log(arg)  // 5
    }
    return innerFn
}
outerFn(5)()

当outerFn(5)被调用时,返回了innerFn 函数
当innerFn 被返回,JavaScript引擎视innerFn为一个闭包,并相应的设置了它的作用域;arg和outer被设置到inner的作用域层级中,返回函数的引用存储到innerFn 中;
innerFn被调用时即(outerFn(5)())就记住了arg和outer

04 在浏览器中查看是否有闭包

图片

在浏览器中F12查看,closure就是闭包的意思

05 闭包的案例

1.点击每个li能打印出每个li的索引
<body>
    <ul class="nav">
        <li>语文</li>
        <li>数学</li>
        <li>英语</li>
        <li>化学</li>
    </ul>
</body>
<script>
    // 创建4个立即执行函数
    var lis = document.querySelector('.nav').querySelectorAll('li')
    for (var i = 0; i < lis.length; i++) {
        (
            function (i) {
                lis[i].onclick = function () {
                    console.log(i)
                }
            }
        )(i)
    }
    // 立即循环函数传入的是当前循环中的i值
</script>
2. 3秒后,打印所有li的索引
<body>
    <ul class="nav">
        <li>语文</li>
        <li>数学</li>
        <li>英语</li>
        <li>化学</li>
    </ul>
</body>
<script>
    // 闭包应用3秒后,打印所有li的索引
    var lis = document.querySelector('.nav').querySelectorAll('li');
    for (var i = 0; i < lis.length; i++) {
        (function (i) {
            setTimeout(function () {
                console.log(i);
            }, 3000)
        })(i);
    }
</script>

06 闭包的经典案例题

1、案例题1
<script>
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {
                return function () {
                    return this.name;
                };
            }
        };
        console.log(object.getNameFunc()()) // The Window
</script>

分析 拆解

object.getNameFunc() // 相当于
var f = object.getNameFunc()
f = function () {
  return this.name;
};
object.getNameFunc()() // 相当于
f()

this 在匿名函数里 就是f() 此时指向window

2、案例题2
<script>
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {
                var that = this;
                return function () {
                    return that.name;
                };
            }
        };
        console.log(object.getNameFunc()()) // My Object
</script>

分析 拆解

object.getNameFunc()  // 相当于
var f = function () {
    return that.name;
};
object.getNameFunc()() // 相当于
f()    

var that = this的this是getNameFunc函数的调用者
object.getNameFunc()调用了getNameFunc 即this指向 object
f()再调用的时候,that存储的是object中的name

07 闭包的作用

1.延伸了变量的作用范围(可以读取函数内部的变量)
function outer() {
    let a = 5
    function inner() {
        console.log(a) //5
    }
    return inner
}
let f = outer()
f()

全部作用域f()也能访问局部作用域的变量a

2.让变量的值始终保持在内存中
<script>
        function outer(){
            var i = 0;
            function inner(){
                i++
                console.log(i)
            }
            return inner
        }
        var f = outer()
        f() // 1
        f() // 2
</script>

理论上,局部变量在函数运行完是消失的,但是再次调用fn,i的值是2,说明变量保存在f这个对象上

下一篇:【this问题】 JS中改变函数内部this的指向问题

推荐阅读:
1、异步编程解决方案async/await
2、异步编程解决方案promise

上一篇下一篇

猜你喜欢

热点阅读