JavaScript开发和面试之闭包

什么是闭包

要理解闭包须要先理解变量做用域javascript

做用域

明确几点:html

  • JavaScript的变量做用域是基于其特有的做用域链的
  • JavaScript没有块级做用域
  • 函数中声明的变量在整个函数中都有定义

按照做用域区分,变量有全局变量和局部变量,因为做用域链,函数内部是能够直接读取全局变量的,而函数外部没法直接读取函数内的局部变量。java

闭包定义

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。chrome

本质上,闭包就是将函数内部和函数外部链接起来的一座桥梁浏览器

闭包的用途

  • 间接访问变量
  • 另外一个就是让这些变量的值始终保持在内存中

来看一个栗子bash

function fn(){
  // default
  var n=66;
  // get
  function getN(){
    return n;
  }
  // set
  function setN(num){
    n=num;
  }
  // add
  function addN(){
    n++;
  }
  return {
    getN:getN,
    setN:setN,
    addN:addN
  }
}

var test=fn();

console.log(test.getN()); //66
test.setN(666);
console.log(test.getN()); // 666
test.addN();
console.log(test.getN()); // 667
复制代码

咱们经过fn暴露出来的接口访问到了函数内部的n,同时咱们对n的值作了修改,能够看到n依然是存在于内存中的。闭包

具体应用方面:函数

  • 块级做用域,防止变量名污染
  • 封装私有变量

闭包的注意事项

1)因为闭包会使得函数中的变量都被保存在内存中,在IE中可能致使内存泄露。解决方法是,在退出函数以前,将不使用的局部变量所有删除。ui

2)闭包会在父函数外部,改变父函数内部变量的值。因此,若是你把父函数看成对象(object)使用,把闭包看成它的公用方法(Public Method),把内部变量看成它的私有属性(private value),这时必定要当心,不要随便改变父函数内部变量的值。this

闭包题目收集

让咱们理论结合实(mian)践(shi)来理解如下闭包 如下皆为chrome浏览器环境运行的结果

第一题

var name = "The Window";

var object = {
  name: "My Object",
  getNameFunc: function() {
    return function() {
      return this.name;
    };
  }
};
console.log(object.getNameFunc()()); // The Window
复制代码

拆成两步就很容易看懂

  • 第一步:object.getNameFunc()等价于function(){ return this.name; }
  • 第二步:(function(){ return this.name; })(),此时调用的是一个匿名行数,this指向的是window。

第二题

var name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function() {
    var that = this;
    return function() {
      return that.name;
    };
  }
};
console.log(object.getNameFunc()()); // My Object
复制代码

咱们一样分两步

  • 第一步:object.getNameFunc()===function(){ return that.name }。
  • 第二步:(function(){ return that.name })(),此时的that在匿名函数里面并无申明,因此它会向上级做用域寻找that,也就是getNameFunc里面的thatthat=this,此时的this指向调用getNameFunc的对象object

第三题

function foo(x) {
  var tmp = 3;
  function bar(y) {
    console.log(x + y + (++tmp));
  }
  bar(10);
}
foo(2); //16
foo(2); //16
foo(2); //16
foo(2); //16
复制代码

这里只是函数调用哦,不是闭包哦

function foo(x) {
  var tmp = 3;
  return function (y) {
    console.log(x + y + (++tmp));
  }
}
var bar = foo(2); 
bar(10); //16
bar(10); //17 
bar(10); //18
bar(10); //19
复制代码

当你return的是内部function时,就是一个闭包。

一个函数访问了它的外部变量,那么它就是一个闭包

第四题

function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}
var a = fun(0); // undefined
a.fun(1); // 0
a.fun(2); // 0
a.fun(3); // 0
复制代码

这题有点绕,咱们来分解一下:

  • a = fun(0)等价于执行后,console.log(o)没有在当前以及父级做用域中寻找到o,因此输出的是undefined,同时a被赋值为{fun:function(m){return fun(m,0)}}
  • a.fun(1)执行,简化后为(function(1){return fun(1,0)})()===fun(1,0),***当前做用域里面没有fun,最后找到了顶级的function fun(n,o)***,执行console.log(0)
function fun(n,o) {
  console.log(o)
  return {
    fun:function(m){
      return fun(m,n);
    }
  };
}

var b = fun(0).fun(1).fun(2).fun(3);
// undefined
// 0
// 1
// 2
复制代码

这链式看的我好慌,咱们继续分解一下

  • fun(0)会输出o,此时没有o因此为undefind,同时fun(0)表达式执行结果为{fun:function(m){return fun(m,0)}}(先忽略掉后面的fun(1)、fun(2)、fun(3))。
  • 再执行fun(1)等价于执行(function(1){return fun(1,0)})(),继续输出o,此时o为0,表达式简化为{fun:function(m){ return fun(m,1)}}
  • 同理继续执行,依次输出1,2

参考资料

  • http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
  • https://zhuanlan.zhihu.com/p/22486908?refer=study-fe
  • http://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html
相关文章
相关标签/搜索