对闭包的理解

1.什么是闭包?

闭包就是有权访问另一个函数作用域中的变量的函数,一般创建闭包的方式是:在一个函数内创建另一个函数。

在A函数内创建匿名函数,匿名函数可获取到A函数的变量,则匿名函数就是闭包:

function A(){
    var n = 0;
    return function(){
       n++; 
       console.log(n);
    } 
}
var C = A();
C();//1
C();//2

1.1.闭包为什么可访问另一个函数作用域内的变量?(闭包可访问另一个函数作用域内的变量的原理是什么呢?)

一般来说,创建闭包的方式是在一个函数内创建另一个函数,那么,被创建的那个函数就叫闭包。此时闭包在函数内部,即可获取到包含函数的作用域,以及全局的作用域。所以此时闭包包含了自己的作用域、包含函数的作用域、全局作用域。由于作用域链的原因,所以此时闭包可访问另一个函数作用域内的变量。(闭包为什么包含了三个作用域:自己的作用域,包含函数的作用域,全局作用域?

那么闭包可访问另一个函数作用域内的变量,如果不做处理,还是和普通函数一样,会在外部函数执行完以后就被销毁。所以此时闭包需要将自己暴露出来,让其他的对象引用他

1.2.闭包怎么暴露自己呢?

闭包暴露自己的方法有两个:

1.作为返回值返回;

2.作为参数传递;

闭包被返回后,A函数执行环境的作用域链会被销毁,而活动对象不会被销毁,会被保留在内存里,直到闭包(就那个匿名函数)不存在为止。(如何让匿名函数被销毁不存在?)

2.闭包的作用是什么?

闭包的作用有两点:

1).模仿块级作用域;

2).在对象中创建私有变量。

2.1.模仿块级作用域是怎么模仿的?

使用立即执行函数模仿块级作用域。

没有使用立即执行函数的情况:

function outputNumbers(count){
    for(var i=0;i<count;i++){
        console.log(i);//计数--输出1-9
    }
    var i;//重新声明变量
    console.log(i);//计数--10
}

outputNumbers(10);

使用立即执行函数的情况:

function outputNumbers(count){
    (function(){
    for(var i=0;i<count;i++){
        console.log(i);
    }

})();
console.log(i);//报错:i未定义(其实我觉得是很正常的,本来就不在人家函数里,怎么会有定义呢)
}

outputNumbers(10);

此函数不会在内存中留下对该函数的引用,所有变量都会被立即销毁,除非将某些变量赋给包含作用域中的变量。所以他减少了闭包占用的内存问题,因为没有指向匿名函数的引用。函数执行完毕就会销毁其作用域链。

2.2.如何在对象中创建私有变量(私有变量包括:函数的参数、局部变量、函数内部定义的其他函数)?

闭包实现公有方法,通过公有方法访问在包含作用域中定义的变量。其中公有方法又叫特权方法

2.2.1.如何实现特权方法呢?

实现特权方法有四种方式,实现两种类型的特权方法

1).实现自定义类型创建私有变量和特权方法

i.构造函数模式(私有变量)

function person(name){
    this.getName=function(){
        return name;
    };
    this.setName=function(value){
        name=value;
    };
}


var per=new person("Nicholas");
console.log(per.getName());//Nicholas
per.setName("Greg");
console.log(per.getName());//Greg

缺点:构造函数模式针对每个实例都会创建同样一组新方法 

ii.原形模式(静态私有变量)

(function(){
    var name="";
    person=function(value){
        name=value;
    };
    person.prototype.getName=function(){
        return name;
    };
    person.prototype.setName=function(value){
        name=value;
    };
})();

var person1=new person("Nicholas");
console.log(person1.getName());//Nicholas
person1.setName("Greg");
console.log(person1.getName());//Greg
var person2=new person("Michael");
console.log(person2.getName());//Michael
person2.setName("gggg");
console.log(person2.getName());//gggg
console.log(person1.getName());//gggg

 此时变量变成了静态的,由所有实例共享的属性。每个实例都没有自己的私有变量。

PS:多查找作用域链中的一个层次,就会在一定程度上影响查找速度。这也是闭包和私有变量的一个明显不足之处。

2).实现单例创建私有变量和特权方法

i.模块模式

var application=function(){
    var components=new Array();
    components.push(new BaseComponent());
    return {
        getComponentCount:function(){
            return components.length;
        },
        registerComponent:function(component){
            if(typeof component=="object"){
                components.push(component);
            }
        }
    };
}();
var singleton=function(){
    var privateVariable=10;
    function privateFunction(){
        return false;
    }
    return {
        publicProperty:true,
        publicMethod:function(){
            privateVariable++;
            return privateFunction();
        }
    };
}();

ii.增强的模块模式

var application=function(){
    var components=new Array();
    components.push(new BaseComponent());
    var app=new BaseComponent();
    app.getComponentCount=function(){
        return components.length;
    }
    app.registerComponent=function(component){
        if(typeof component=="object"){
            components.push(component);
            }
        };
        return app;
}();
var singleton=function(){
    var privateVariable=10;
    function privateFunction(){
        return false;
    }
    var object=new CustomType();
    object.publicMethod=function(){
        privateVariable++;
        return privateFunction();
    };
    object.publicProperty=true;
    return object;
}();

3.闭包的优缺点?

优点:

1.在开发大项目的时候,限制向全局作用域中添加过多的变量和函数,从而避免了全局变量和函数命名冲突的问题。

2.可用于创建块级作用域。

3.可用于创建私有变量。

……

缺点:

1.会导致内存泄漏。

2.在闭包中,返回函数不要引用任何循环变量或者后续会发生变化的变量,因为他会取到最后一个值。

3.在使用匿名函数时,匿名函数的执行环境具有全局性,所以其中的this会指向全局(window),此时要注意this的指向问题。

4.闭包包含了自己本地的作用域,包含函数的作用域,全局作用域,需要内存,所以使用闭包会导致内存占用过大,出现性能方面的问题。

---------------------------------------------------------------------------------------------------------------------------------------------------------

1.解释为什么闭包包含了三个作用域:自己的作用域,包含函数的作用域,全局作用域?

每个执行环境都有一个表示变量的对象——变量对象。全局环境中的变量对象始终存在。而函数的变量对象只在函数执行过程中存在。在A函数被创建时,会创建一个预先包含全局变量对象的作用域链,放在[[scope]]属性中,在调用A函数时,会创建一个执行环境,复制[[scope]]属性中的作用域链放到对象中构建成A函数的作用域链。又把A函数的活动对象(arguments和变量等)放在A函数执行环境的作用域链前端。所以此时A执行环境就包含了全局变量对象和本地活动对象。同理,闭包则包含了本地活动对象、A的活动对象、全局变量对象。

2.如何让匿名函数被销毁不存在?

将其设置为null,解除对其的引用,则会通知垃圾回收机制例程将其清除 。

谨以此文献给要问我闭包的HR们,如果我写错的地方,或者有问题的地方,请各位小哥哥小姐姐们一定要告诉我,我好改了重新记忆。阿里嘎多! 我三个露露一起谢谢你们

                 

猜你喜欢

转载自blog.csdn.net/sinat_41432025/article/details/82823371