闭包小练习

188 阅读4分钟
/*
 * 下面代码是否可以,每隔1000MS依次输出 0 1 2 3 4 5 ?
 * 如果不可以,说明为啥?
 * 以及如何解决?
 */

/* 
// 不行:基于var在循环中声明的变量“i”是全局变量
//   第一轮循环 全局i=0 设置第一个定时器{1000} 给定时器设置的回调函数并没有执行,等待1秒后执行,循环继续
//   第二轮循环 全局i=1 设置第二个定时器{2000}
//   ...
//   第五轮循环 全局i=4 设置第五个定时器{5000} i++,让全局的i=5,循环结束
// ->每一个定时器设置的回调函数,只有等到循环结束后,到了等待的时间,才会触发执行
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        /!*
         * EC(AN)
         *   
         * 作用域链:<EC(AN),EC(G)>
         * 形参赋值:--
         * 变量提升:--   
         *!/
        console.log(i); //i不是自己私有的,是全局的,但是此时全局的i已经是循环结束后的5了
    }, (i + 1) * 1000); 
}
*/

//=======解决方案:闭包
/* 
第一轮 全局i=0 
   (function (i) {
        // EC(AN1) 私有i=0 闭包
        setTimeout(function () { //0x001 [[scope]]:EC(AN1)
            console.log(i);
        }, 1000);
   })(0);
第二轮 全局i=1
   (function (i) {
        // EC(AN2) 私有i=1 闭包
        setTimeout(function () { //0x002 [[scope]]:EC(AN2)
            console.log(i);
        }, 2000);
   })(1);
....
循环结束:设置了五个闭包,每一个闭包中都有一个自己的私有变量“i”,存储的值分别是0~4;全局i是5;
*/
// for (var i = 0; i < 5; i++) {
//     (function (i) {
//         setTimeout(function () {
//             console.log(i);
//         }, (i + 1) * 1000);
//     })(i);
// }

// var fn = function fn(i) {
//     return function () {
//         console.log(i);
//     };
// };
// for (var i = 0; i < 5; i++) {
//     // 第一轮 全局i=0 设置定时器{1000}  
//     //   + fn(0)  EC(FN1) 私有i=0 返回一个小函数0x001
//     //   + setTimeout(0x001,1000)
//     setTimeout(fn(i), (i + 1) * 1000);
// }

// // new Array(5).fill(null) 创建一个长度为5的数组,每一项填充null,把其变为密集数组,这样可以forEach
// //   “_”就是形参占位,原本应该有个形参变量,但是我不想用,所以我占个位即可
// new Array(5).fill(null).forEach(function (_, index) {
//     // 第一轮  EC(AN1) index=0  设置定时器{1000}  闭包
//     // 第二轮  EC(AN2) index=1  设置定时器{2000}  闭包
//     // ...
//     setTimeout(function () {
//         console.log(index);
//     }, (index + 1) * 1000);
// });

// 真实项目中都这样搞
// for (let i = 0; i < 5; i++) {
//     setTimeout(function () {
//         console.log(i);
//     }, (i + 1) * 1000);
// }

//====终极方案:基于定时器传参「核心:闭包」
// for (var i = 0; i < 5; i++) {
//     setTimeout(function (i) {
//         console.log(i);
//     }, 1000, i);
// }

//=========
/* setTimeout(function (x) {
    // 等待1秒后,浏览器帮助我们把这个函数执行
    //   + 当前写法是没有给其传实参值的,所以x的值是undefined
}, 1000); */

/* // 设置定时器的时候:第一个是回调函数CB、第二个是等待时间
// 第三个及以后都是到时间后,执行CB,给CB预先传递的实参值
setTimeout(function (x, y) {
    console.log(x, y); //1秒后执行回调函数,x='zhufeng' y='peixun'
}, 1000, 'zhufeng', 'peixun'); */

//=====================================================
// 问题:全局变量污染
/* var name = '曹雅倩';
var age = 18;
var BF = false;

var name = "李利颖";
var age = 62;
var BF = true; */

// 解决1:闭包
/* (function () {
    var name = '曹雅倩';
    var age = 18;
    var BF = false;
})();
(function () {
    var name = "李利颖";
    var age = 62;
    var BF = true;
})(); */

// 解决2:对象
// 把描述同一事物的属性和方法放在一起,实现了分组的效果,避免了全局变量的污染;每一个对象都是一个单独的实例(个体/堆内存空间),所以我们把这种方案称之为“单例设计模式”!!
//   person1:命名空间 namespace
/* var person1 = {
    name: '曹雅倩',
    age: 18,
    BF: false
};
var person2 = {
    name: '李利颖',
    age: 62,
    BF: true
}; */

//----
// 资讯板块
var newsModule = (function () {
    var box = null;
    var query = function query() {};

    // 需求:把这个私有的方法setVal,在其它板块中使用
    var setVal = function setVal() {};

    // 解决1:直接挂载到GO中「弊端:挂载的东西多了,很可能有引发全局变量污染」
    window.setVal = setVal;

    // 解决2:基于返回值,返回一个对象(命名空间),里面包含需要供外部调用的属性和方法即可
    return {
        setVal: setVal,
        query: query
    };
})();

// 换肤板块
var skinModule = (function () {
    var box = null;
    var query = function query() {};
    setVal();

    return {};
})();

// 搜索板块
var searchModule = (function () {
    var query = function query() {};
    newsModule.setVal();

    return {};
})();