深入理解閉包系列第五篇——閉包的10種形式


前面的話

  根據閉包的定義,我們知道,無論通過何種手段,只要將內部函數傳遞到所在的詞法作用域以外,它都會持有對原始作用域的引用,無論在何處執行這個函數都會使用閉包。接下來,本文將詳細介紹閉包的10種形式

 

返回值

  最常用的一種形式是函數作為返回值被返回

var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    return N;
}
console.log(F()());

 

函數賦值

  一種變形的形式是將內部函數賦值給一個外部變量

var inner;
var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    };
    inner = N;
};
F();
console.log(inner());

 

函數參數

  閉包可以通過函數參數傳遞函數的形式來實現

var Inner = function(fn){
    console.log(fn());
}
var F = function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    Inner(N);
}
F();

 

IIFE

  由前面的示例代碼可知,函數F()都是在聲明后立即被調用,因此可以使用IIFE來替代。但是,要注意的是,這里的Inner()只能使用函數聲明語句的形式,而不能使用函數表達式。詳細原因移步至此

function Inner(fn){
    console.log(fn());
}

(function(){
    var b = 'local';
    var N = function(){
        return b;
    }
    Inner(N);
})();

 

循環賦值

  在閉包問題上,最常見的一個錯誤就是循環賦值的錯誤。關於其錯誤原因的詳細解釋移步至此

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//2    

  正確的寫法如下

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = (function fn(j){
            return function test(){
                return j;
            }
        })(i);
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//0    

 

g(s)etter

  我們通過提供getter()和setter()函數來將要操作的變量保存在函數內部,防止其暴露在外部

var getValue,setValue;
(function(){
    var secret = 0;
    getValue = function(){
        return secret;
    }
    setValue = function(v){
        if(typeof v === 'number'){
            secret = v;
        }
    }
})();
console.log(getValue());//0
setValue(1);
console.log(getValue());//1

 

迭代器

  我們經常使用閉包來實現一個累加器

var add = (function(){
    var counter = 0;
    return function(){
        return ++counter; 
    }
})();
console.log(add())//1
console.log(add())//2  

  類似地,使用閉包可以很方便的實現一個迭代器

function setup(x){
    var i = 0;
    return function(){
        return x[i++];
    }
}
var next = setup(['a','b','c']);
console.log(next());//'a'
console.log(next());//'b'
console.log(next());//'c'

 

區分首次

var firstLoad = (function(){
  var _list = [];
  return function(id){
    if(_list.indexOf(id) >= 0){
      return false;
    }else{
      _list.push(id);
      return true;
    }
  }
})();

firstLoad(10);//true
firstLoad(10);//false
firstLoad(20);//true
firstLoad(20);//false

 

緩存機制

  通過閉包加入緩存機制,使得相同的參數不用重復計算,來提高函數的性能

  未加入緩存機制前的代碼如下

var mult = function(){
  var a = 1;
  for(var i = 0,len = arguments.length; i<len; i++){
    a = a * arguments[i];
  }
  return a;
}

  加入緩存機制后,代碼如下

var mult = function(){
  var cache = {};
  var calculate = function(){
    var a = 1;
    for(var i = 0,len = arguments.length; i<len; i++){
      a = a * arguments[i];
    }
    return a;
  };
  return function(){
    var args = Array.prototype.join.call(arguments,',');
    if(args in cache){
      return cache[args];
    }
return cache[args] = calculate.apply(null,arguments); } }()

 

img對象

  img對象經常用於數據上報

var report = function(src){
  var img = new Image();
  img.src = src;
}
report('http://xx.com/getUserInfo');

  但是,在一些低版本瀏覽器中,使用report函數進行數據上報會丟失30%左右的數據,也就是說,report函數並不是每一次都成功地發起了HTTP請求

  原因是img是report函數中的局部變量,當report函數的調用結束后,img局部變量隨即被銷毀,而此時或許還沒來得及發出HTTP請求,所以此次請求就會丟失掉

  現在把img變量用閉包封閉起來,就能解決請求丟失的問題

var report = (function(){
  var imgs = [];
  return function(src){
    var img = new Image();
    imgs.push(img);
    img.src = src;
  }
})()
report('http://xx.com/getUserInfo');

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM