闭包的实际应用

/*
 * 闭包应用之:循环中的闭包处理方案
 *   + 循环事件绑定
 *     + 自定义属性
 *     + 闭包的N中方案「含LET处理机制」
 *     + 事件委托
 *   + 循环中的定时器
 *     + 闭包的处理方案
 *     + 定时器本身处理方案
 */

// 无法实现? 每次点击按钮,执行对应的方法,方法中的i不是私有的,而是全局的,而此时全局的i已经是循环结束的5了...
/* var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
    btnList[i].onclick = function () {
        console.log(`当前点击按钮的索引:${i}`);
    };
} */

// 解决办法一:闭包解决方案,利用闭包的“保存”机制
// 每一轮循环的时候,都创建一个闭包(不释放的上下文),闭包中存储自己的私有变量i,并且值是每一轮循环的索引;当点击按钮,执行对应的函数,遇到一个变量i,不要再去全局找了,而是让其去所属的闭包中查找即可...

// @1
/* var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
    // 循环五次,产生五个不释放的闭包,每一个闭包中,都存在一个私有变量i,变量的值是对应的索引 0/1/2/3/4
    (function (i) {
        btnList[i].onclick = function () {
            console.log(`当前点击按钮的索引:${i}`);
        };
    })(i);
} */

// @2
/* var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
    btnList[i].onclick = (function (i) {
        return function () {
            console.log(`当前点击按钮的索引:${i}`);
        };
    })(i);
} */

// @3  NodeList集合本身具备forEach方法,和数组中的类似,都是用来迭代集合中每一项的
/* var btnList = document.querySelectorAll('.btn');
btnList.forEach(function (item, index) {
    // 迭代集合中每一项,都把这个回调函数执行,产生一个闭包「因为上下文中创建的小函数,被外层的按钮对象的onclick占用了;每个闭包中有一个私有变量index,存储的是当前这一项的索引」
    item.onclick = function () {
        console.log(`当前点击按钮的索引:${index}`);
    };
}); */

// @4 也是基于闭包的方案,只不过利用的是LET会产生块级上下文
/* let btnList = document.querySelectorAll('.btn');
for (let i = 0; i < btnList.length; i++) {
    // 每一轮循环都产生一个私有的块级上下文,里面的内容(函数)被外部占用,也会产生一个闭包;而且每个闭包中,都有一个私有变量i记录索引
    btnList[i].onclick = function () {
        console.log(`当前点击按钮的索引:${i}`);
    };
} */
// 闭包方案虽然可以解决,但是比较消耗内存

//======================
// 解决方案二:自定义属性
/* let btnList = document.querySelectorAll('.btn');
let i = 0;
for (; i < btnList.length; i++) {
    // 最开始每轮循环的时候,给每一个按钮对象都设定一个自定义属性myIndex,存储它的索引
    btnList[i].myIndex = i;
    btnList[i].onclick = function () {
        // 每一次点击的时候,基于THIS(当前操作元素)获取之前存放的自定义属性值
        console.log(`当前点击按钮的索引:${this.myIndex}`);
    };
}
// 性能比闭包要好一些,但是也有一些性能消耗{元素对象 & 节点集合 & 绑定的方法 都是开辟的堆内存} */

//======================
// 解决方案三:终极方案  事件委托
// 点击每一个按钮,除了触发按钮的点击事件行为,根据冒泡传播机制,也会把body的点击事件行为触发
/* document.body.onclick = function (ev) {
    let target = ev.target;
    if (target.tagName === 'BUTTON' && target.className === "btn") {
        // 点击的事件源是按钮
        let index = target.getAttribute('data-index');
        console.log(`当前点击按钮的索引:${index}`);
    }
}; */

//======================================
/* // 能否实现每间隔1秒输出 0 1 2?
for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i);
    }, i * 1000); //等待时间 0ms(5~7ms)  1000ms  2000ms
}
// 现在效果是,每间隔1000ms都输出的是3? 定时器执行,方法中的i不是私有的,向上级找就是全局的i「此时全局i已经是循环结束的3」 */

// @1
/* for (let i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i);
    }, i * 1000);
} */

// @2
/* let i = 0;
for (; i < 3; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        }, i * 1000);
    })(i);
} */

// @3
/* const fn = i => {
    return function () {
        console.log(i);
    };
};
let i = 0;
for (; i < 3; i++) {
    setTimeout(fn(i), i * 1000);
} */

// @4
/* let i = 0;
for (; i < 3; i++) {
    // 设置定时器:
    //   参数1:回调函数,到时间执行的方案
    //   参数2:等待时间
    //   参数3:给回调函数“预先传递”的实参值{底层本质也是闭包 柯理化函数思想}
    setTimeout(function (n) {
        console.log(n);
    }, i * 1000, i);
} */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值