面试常见问题:你是怎么理解闭包的?闭包有哪些好处?

前言

相信无论是求职招聘还是实习招聘,面试官在第一轮面试的时候总是喜欢问到闭包并询问你对它的理解。对闭包的理解很大程度上可以区分出你是初级前端工程师还是更高层次。不同人对闭包有不同的理解,说出来的内容也会千差万别。
作为一个前端小白的我,在上个月的实习面试中也被问到这个问题,我回答的很不好。大概就只是知道有闭包,什么是闭包,闭包怎么用而已,对于闭包的了解也是通过阅读网上的文章,并没有自己太多的理解。所以,趁现在还有三天就要期末考了,5门考试的大好时光,我想分享一下我的答案。

你是怎么理解闭包的?

  1. 什么是闭包?

MDN给出的定义是:

闭包是函数和声明该函数的词法环境的组合。

还提供了一个官方示例:

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}

var myFunc = makeFunc();
myFunc();

JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。在我们的例子中,myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用,而 displayName 实例仍可访问其词法作用域中的变量,即可以访问到 name 。由此,当 myFunc 被调用时,name 仍可被访问,其值 Mozilla 就被传递到alert中。

总结:
可以粗糙地理解为,MDN中的函数就是指例子中的displayName。声明该函数的词法环境就是指makeFunc。所以makeFunc的开始的结尾这一段代码就是一个闭包。(笔试的时候有可能有一道叫你写出一个闭包实例的题目)。

  1. 闭包的结构?

从官方给出的例子可以看出闭包的格式是:

  1. 一个构造函数,里面有一些变量和一个函数。
  2. 构造函数里面的函数使用了构造函数的变量或自身的变量。
  3. 构造函数最后把它里面的那个函数用return抛出去。

这样一来,使用构造函数创建的函数对象都能像构造函数里面那个函数那样取到构造函数的变量了。

  1. 对闭包的看法

很大大佬写闭包的时候都会讲到作用域的问题。是的,闭包一开始不就是用来解决作用域访问的问题的吗?通过闭包可以让函数里面的作用域暴露到外面供我们使用。所以闭包是JavaScript的一个特色,由于小白的我没有项目经验,所以只是揣测,这个闭包可能对项目的开发提供了便利,所以闭包的写法和作用也是程序员们长期以来累积的经验的成果。
特意去百度了一下,得到的答案:

闭包开发的时候用的很少,但是用到的时候几乎都是不可被别的方式替代的,但凡遇到永久 ,保护等关键字就用闭包。

闭包有哪些好处?

引用廖雪峰老师关于闭包讲解的文章里面的一句话:

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

来源:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

来自MDN的介绍:

闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。
我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

来源:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

  1. 读取函数内部的变量

这是闭包很明显的作用,应该不用做过多的解释吧。
MDN例子:

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }   
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

这一点和MDN所说的模拟私有变量是相通的,通过闭包,你可以在构造函数里面那个函数里使用构造函数的变量又不要怕构造函数的变量被访问到,自己定义了改变变量的方法抛到外面供使用而已。

  1. 让这些变量的值始终保持在内存中

作用域不释放,这是闭包一个问题。也正如前面所说:

闭包开发的时候用的很少,但是用到的时候几乎都是不可被别的方式替代的,但凡遇到永久 ,保护等关键字就用闭包。

一般函数调用完不使用了,它的作用域链以及作用域就会被系统的回收机制自动销毁,但是闭包可以让作用域和作用域链不销毁,所以才能让这些变量的值始终保存在内存中。

后言

我只是抛砖引玉,请阅读后面这三篇文章,我觉得对你更有帮助:
廖雪峰老师:
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

MDN:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

知乎,答者:七律:
https://www.zhihu.com/question/34510484

猜你喜欢

转载自blog.csdn.net/canxuezhang/article/details/85337131