你不知道的js—作用域闭包

830 阅读2分钟

闭包

闭包可以理解为:告诉浏览器这些变量我还要用,不可以进行垃圾回收。

为什么需要闭包

我们先不强硬背八股文,看下面的代码

function foo(){
    var a = 1;
    console.log(a); // 1
}
console.log(a);  // 报错

最后的打印会报错,我们不能使用到foo中的a。因为作用域不同,那如果我们想访问foo中的a,应该怎么办?

什么是闭包

function foo(){
    var a = 1;
    return a;
};
const a = foo();
console.log(a);  // 1

这样我们就可以访问到foo中的a了。

浏览器的内存空间是有限的,所以函数内的局部变量,会在函数执行时创建,执行完成时会销毁。如果不希望它销毁,就要让外部的变量保持对局部变量的引用。例:上述代码中全局中的a,就保持了对foo中a的引用,是的foo中的a不会被销毁。

强大的闭包

封装模块

在写项目时,如果所有的变量都定义在全局作用域,会导致内存消耗过大、变量名容易重复等问题。闭包的存在,让我们可以把项目分割成功能块,每个功能都有自己的作用域。这样代码规划更清晰,复用性更高。

看这个例子: 做了一个班级信息的模块,暴露出add,del,getInfo,可以增加学生、减少学生、获取学生信息。

  1. info存储所有信息;
  2. add向info中加入数据;
  3. del删除info中对应的数据;
  4. getInfo获取全班的信息;
 const classInfo = (function() {
    let info = [];
    function add(name, age) {
        info.push({
            name,
            age,
        })
    }
    function del(name) {
        info = info.filter(i => i.name !== name)
        return info;
    }
    function getInfo() {
        return info;
    }
    return {
        add,
        del,
        getInfo
    }
})()
console.log(classInfo.getInfo()) // []
classInfo.add('xiaohong', 18)
classInfo.add('xiaoming', 28)
classInfo.add('xiaowang', 38)
console.log(classInfo.getInfo()) // [{}, {}, {}]
classInfo.del('xiaowang')
console.log(classInfo.getInfo()) // [{}, {}]

模块化的实践

模块化是指多个模块之间相互协作。 我们先声明几个模块:

储存name模块

 const allName = function (){
    const names = [];
    function add(name) {
        names.push(name);
    }
    function getNames() {
        return names;
    }
    return {
        add,
        getNames
    }
}

储存age模块

 const allAge = function (){
    const ages = [];
    function add(age) {
        ages.push(age);
    }
    function getAges() {
        return ages;
    }
    return {
        add,
        getAges
    }
}

整合name和age信息的模块

 const getNameAge = function(names, ages) {
    function getNameAge() {
        const nameArr = names.getNames();
        const ageArr = ages.getAges();
        const data = [];
        for (let i = 0; i < nameArr.length; i++) {
            data.push({
                name: nameArr[i],
                age: ageArr[i],
            });
        }
        return data;
    }
    return {
        getNameAge
    }
};

模块组合

  1. module.define注册模块,注册时。如果使用到了其他模块,会作为参数传进去。
  2. module.get获取模块。
 const module = (function () {
    const modules = {};
    function define(key, dep, impo) {
        const args = [];
        for (let key of dep) {
            if (modules[key]) {
                args.push(modules[key])  
            }
        }
        modules[key] = impo.apply(null, args)
    }
    function get(key) {
        return modules[key];
    }
    return {
        define,
        get
    }
})()

注册和获取

module.define('names', [], allName);
module.define('ages', [], allAge);
module.define('getNameAge', ['names', 'ages'], getNameAge);

const impoName = module.get('names');
const impoAge = module.get('ages');
const impoGetNameAge = module.get('getNameAge');

验证结果

新增数据,然后打印,观察数据是否正确。这样就实现了多模块之间的联合使用。

impoName.add('xiaoma');
impoAge.add(18);
impoName.add('xiaowang');
impoAge.add(20);
impoName.add('xiaoli');
impoAge.add(22);

console.log(impoGetNameAge.getNameAge())

总结

模块化对于开发十分重要,闭包是实现模块化的关键技术。作为js初学者必须对其有深刻的理解。