JavaScript 闭包解析

简介

闭包是「函数」和「函数内部能访问到的变量(即作用域)」的总和,更通俗来说,闭包也可以理解为有权访问另一个函数作用域中的变量的函数。一般来说会是函数内嵌套函数,然后 return 函数的呀

js 之所以会有闭包,是因为 js 不同于其他规范的语言,js 允许一个函数中再嵌套子函数,正是因为这种允许函数嵌套,导致 js 出现了所谓闭包

function a(){
    function b(){

    };
    b();
}
a();

在 js 正常的函数嵌套中,父函数 a 调用时,嵌套的子函数 b 的结构在内存中产生,然后子函数又接着调用了,子函数 b 作用域中的数据就注销了,此时父函数 a 也就执行到尾,父函数 a 也会把自己函数体内调用时生成的数据从内存都注销

function a(){
    function b(){
        //
    }
    return b;
}
var fun=a();

而在这个例子中,父函数 a 调用时,函数体内创建了子函数 b,但是子函数 b 并没有立即调用,而是返回了函数指针,以备“日后再调用”,正是因为“准备日后调用”,此时父函数 a 执行完了,就不敢注销自己的作用域中的数据了,因为一旦注销了,子函数b日后再调用时,沿着函数作用域链往上访问数据时,就没有数据可以访问了,这就违背了 js 函数作用域链的机制。这就导致父函数要维持自己的作用域链,而不敢注销自己的作用域,那么这个子函数就是“闭包函数”

闭包并不是 js 为了创造而创造的,完全只是因为 js 允许函数嵌套,js函数嵌套还有个函数作用域链的机制,让父函数不敢注销自己作用域中的数据,才产生了所谓的闭包

闭包的用途

1、希望一个变量长期驻扎在内存中,实现缓存

一般函数执行完毕后,局部变量就会被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!

不带缓存的阶乘的计算函数:

function factorial(num){
    var result = 1;
    for(var i = num; i>0; i--){
        result *= i;
    }
    return result;
}
console.log(factorial(6))//控制台输出:720
console.log(factorial(6))//控制台输出:720

每次运行都要计算一次,可以使用缓存策略进行优化:

function factorial(num){
    var arr = [];
    return function(num){
        if(num in arr){
            console.log("这次数据来自缓存:")
            return arr[num];
        }else{
            var result = 1;
            for(var i = num; i>0; i--){
                result *= i;
            }
            arr[num] = result;//将结果存入缓存
            return result;
        }
    }
}
var func = factorial();
console.log(func(6));
console.log(func(6));

控制台输出:

720
这次数据来自缓存:
720
2、模块化代码,减少全局变量的污染

如果我们把一些只用到一两次的变量放在全局作用域中,最后肯定是容易出错且不可维护的,如下方例子:

var count = 0;
function getCount(){
    count++;
    console.log(count);
}
getCount();//控制台输出:1
getCount();//控制台输出:2

通过闭包,我们可以实现在函数外部访问一个函数内的局部变量,从而避免了对全局作用域的污染

function getCount(){
    var count = 0;
    return function(){
        count++;
        console.log(count);
    }
}
var a = getCount();
a();//控制台输出:1
a();//控制台输出:2
3、实现封装

js 没有 private 之类关键字,如果要实现 OOP 中的封装,可以利用闭包模拟出私有属性

var person = (function(){
    var name = 'default';//变量作用域为函数内部,外部无法访问到
    return{
        getName : function(){
            return name;
        },
        setName : function(newName){
            name = newName;
        }
  }
})()
console.log(person.name)
console.log(person.getName())
person.setName('Leo')
console.log(person.getName())

控制台输出:

undefined
default
Leo
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页