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
相關文章
相關標籤/搜索