闭包的一点浅见

148 阅读4分钟

一、什么是闭包?

“闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。

这是MDN上的定义,由此我们得到:闭包是函数和词法环境的组合

拜读了阮大神的关于闭包的文章,他对闭包的定义更加通俗易懂--闭包就是能够读取其他函数内部变量的函数(还是个函数)

我们知道在全局环境中一般我们是访问不到函数内部定义的局部变量的,那怎样能够访问得到呢?方法很明显---在函数内部定义一个函数就可以了嘛!,例如:

    function foo() {
    var dogName = '小米';
    function alertName() {
        console.log(dogName)
    }
    alertName()
}
foo()// 小米

但其实上面的函数alertName还不是闭包,因为我们无法在外部访问到foo内部定义的局部变量,而闭包是能够让我们在外部访问到函数内部定义的局部变量的,函数foo执行完毕后,根据浏览器的垃圾回收机制,其内部的变量将会被回收(因为外部没有引用)

在javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收; 如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。

那么怎样才能在外部访问得到函数内部的变量呢?

只需要将上述的代码改动一下:

    function foo2() {
    var dogName = '小米';
    function alertName() {
            console.log(dogName)
        }
        return alertName
    }
    var alertNameFunc = foo2();
    alertNameFunc()// 小米
    //解除引用,变量被回收
    alertNameFunc = null

foo2函数执行完毕后,得到的是一个函数,然后将该函数的指针指向新的全局变量alertNameFunc,由于alertNameFunc保持着对函数alertName的引用,所以根据浏览器垃圾回收机制,alertName并未被回收,而同理alertName依赖于foo2,所以,foo2也未被回收,直到alertName在外部不被引用为止。 如果我们想要在外部拿到dogName的值,可以这样做:

    function foo2() {
    var dogName = '小米';
    function alertName() {
            return dogName
        }
        return alertName
    }
    var alertNameFunc = foo2();
    alertNameFunc()// 小米

二、闭包的应用

闭包的应用主要有是设计私有的方法和变量

一般在函数中定义的变量都是私有变量(局部变量),我们把有权访问私有变量的方法称为特权方法, 例如:

function Animal(){
  // 私有变量
  var series = "哺乳动物";
  function run(){
    console.log("Run!!!");
  }
  
  // 特权方法
  this.getSeries = function(){
    return series;
  };
}
Animal()
console.log(getSeries())// 哺乳动物

上述的getSeries也是闭包,这是因为当Animal执行的时候是在全局环境中执行的,所以内部的this指向window,也就是说相当于在全局环境中定义了一个变量getSeries,而该变量一直保存着对匿名函数的引用,也就是说getSeries不会被浏览器的垃圾回收机制回收,而函数本身访问着父函数的局部变量,所以我们在外部能够访问series

三、闭包的缺陷

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题.

四、阮大神的思考题

题目一:

    var name  = `The Window`;
    var obj = {
        name:"My object",
        getNameFunc:function(){
            return function(){
                return this.name
            }
        }
    }
    obj.getNameFunc()()// The Window

上面的代码可以改写为:

    var name  = `The Window`;
    var obj = {
        name:"My object",
        getNameFunc:function(){
            return function(){
                return this.name
            }
        }
    }
    var getnamefunc = obj.getNameFunc()
     getnamefunc()// 由于是全局环境中中调用的所以是 The Window

题目二:

    var name  = `The Window`;
    var obj = {
        name:"My object",
        getNameFunc:function(){
            var that = this;
            return function(){
                return that.name
            }
        }
    }
 obj.getNameFunc()()// My object

这是为什么呢?如果将代码改成这样子或许就明白了一些:

    var name  = `The Window`;
    var obj = {
        name:"My object",
        getNameFunc:function(){
            var that = this;
            return function(){
                return that.name
            }
        }
    }
var getnamefunc = obj.getNameFunc()
getnamefunc()// My object

这是因为object.getNameFunc()和其所在的局部环境形成了闭包(变量getnamefunc保持着对闭包的引用),由于这里的that指向外层函数的this,所以相当于是访问了外层函数的局部变量this.name,在一个对象中this就指向对象本身(obj),所以是My Object

第一次写文章,可能会有些疏漏的地方,还望大家指正哈!😂

文中参考了以下大牛的文章:

  1. JavaScript闭包
  2. 学习JavaScript闭包