js闭包的理解

242 阅读2分钟

闭包理解

闭包理解1:闭包是嵌套的内部函数(大众理解)

function fn1 () {
var num = 10;
function fn2 () {
  console.log(num)
}
}
fn1();

闭包理解2:闭包是包含被引用变量的对象(极少部分人的理解)

闭包本质:嵌套的内部函数引用了外部函数的变量

function fn1 () {
var num = 10;//外部函数定义的变量
function fn2 () {
  console.log(num) //内部函数引用外部函数变量
}
}
fn1();

闭包常用使用方式1:将函数作为另一个函数的返回值​

 function fn1() {
    var num = 10;
    function fn2() {
        num++;
        console.log(num);
    }
    return fn2;
}
var f = fn1();
f(); // 11
f(); // 12​

闭包常用使用方式2:将函数的形参作为实参传递给另一个函数调用​

  function logMsgDelay(msg, time) {
    setTimeout(function () {
        console.log(msg);
    }, time);
}

logMsgDelay('撩课学院', 3000);

闭包作用1:使函数内部声明的局部变量在函数执行完后仍然存在

闭包作用2:使函数外部能直接访问到函数内部的局部

例如:

  //闭包作用
 function fn1() {
    var num = 10;
    function fn2() {
        num++;  //引用外部函数的变量--->产生闭包
        console.log(num);
    }
    return fn2;
}
var f = fn1();  // 由于f引用着内部的函数-->内部函数以及闭包都没有成为垃圾对象
f(); // 间接操作了函数内部的局部变量
f();

理解闭包解决同步和异步

 //借助小闭包, 把每次循环的i值都封闭起来
for (var i = 0; i < btns.length; i++) {
    (function (i) {
        var btn = btns[i];
        btn.onclick = function () {
            alert('第' + (i + 1) + '个')
        }
    })(i);
}

闭包自定义模块

 //将所有的数据和功能都封装在一个函数内部(私有的)只向外暴露一个包含多个方法的对象或函数,模块的使用者, 只需要通过模块暴露的对象调用方法
 来实现对应的功能
 (function (window) {
    // 1. 变量
    var intro = ['我', '是', 'M', 'T'];
    var site = 'www.itlike.com';

    // 2. 操作变量的函数
    function one() {
        console.log(intro.join(''));
    }

    function two(){
        console.log(site.toUpperCase());
    }

    window.myTool = {
        fn1: one,
        fn2: two
    }
})(window);
console.log(window.myTool);
myTool.fn1();
myTool.fn2();

闭包使用:高级排他

1.默认的方式对li的hover事件

window.onload = function () {
    var allLis = document.getElementsByTagName('li');
    for(var i=0; i<allLis.length; i++){
        var li = allLis[i];
        li.onmouseover = function () {
            for(var j=0; j<allLis.length; j++){
                 allLis[j].className = '';
            }
            this.className = 'current';
        }
    }
}

2.闭包的方式对li的hover事件

window.onload = function () {
    var allLis = document.getElementsByTagName('li');
    // 记录移动前选中li对应的索引
    var preSelectLiIndex = 0;
    for(var i=0; i<allLis.length; i++){
        (function (i) {
            var li = allLis[i];
            li.onmouseover = function () {
                // 清除
                allLis[preSelectLiIndex].className = '';
                // 设置
                this.className = 'current';
                // 赋值
                preSelectLiIndex = i;
            }
        })(i);
    }
}

实现函数节流

1.普通方式(以window.onscroll事件为例)

window.onscroll = function (ev) {
    console.log(11111);
}

var timer = null;
window.onresize = function (ev) {
    clearTimeout(timer);
    timer = setTimeout(function () {
        console.log(11111);
    }, 200);
};

2.闭包的方式

window.onresize = throttle(function () {
   console.log(11111);
}, 200);

// 2. 将函数的形参作为实参传递给另一个函数调用
function throttle(fn, delay) {
    var timer = null;
    return function () {
        clearTimeout(timer);
        timer = setTimeout(fn, delay);
    }
}