前端開發人員對閉包這個知識點,必定都不陌生,咱們都知道閉包的概念是指有權訪問另外一個函數做用域中的變量的函數。那麼,閉包在js中的實際應用都有哪些呢,今天就一塊兒來了解一下吧。javascript
要理解閉包,首先要對js的執行環境和做用域有了解。html
概念太長不想看系列:前端
執行環境定義了變量或函數有權訪問的其餘數據,決定了它們各自的行爲。每一個執行環境都有與之關聯的變量對象集合[[scope]],環境中定義的全部變量和函數都保存在這個對象中,這個集合被稱爲該環境的做用域鏈。vue
js中,每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。在函數執行以後,棧將其環境彈出,環境隨之銷燬,控制權返回給以前的執行環境。java
當代碼在一個函數的環境中執行時,會建立一個變量對象的做用域鏈。做用域鏈的前端,始終都是當前執行的代碼所在的變量對象。若是這個環境是函數,則將其活動對象(active object)做爲變量對象。活動對象在最開始時只包含一個變量,即arguments對象(這個對象在全局環境中是不存在的)。做用域鏈中的下一個對象來自包含(外部)環境,而再下一個變量則來自下一個變量環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。 當運行期上下文被銷燬,活動對象也隨之銷燬。react
標識符解析是沿着做用域鏈一級一級搜索標識符的過程。搜索過程始終從做用域鏈的前端開始,而後逐級的向後回溯,直至找到標識符爲止。web
總結:面試
閉包是指有權訪問另外一個函數做用域中的變量的函數。編程
建立閉包的常見方式,就是在一個函數內部建立另外一個函數。設計模式
在javascript語言中,閉包就是函數和該函數做用域的組合。從這個概念上來說,在js中,全部函數都是閉包(函數都是對象而且函數都有和他們相關聯的做用域鏈scope chain)。
大多數函數被調用時(invoked),使用的做用域和他們被定義時(defined)使用的做用域是同一個做用域,這種狀況下,閉包神馬的,可有可無。可是,當他們被invoked的時候,使用的做用域不一樣於他們定義時使用的做用域的時候,閉包就會變的很是有趣,而且開始有了不少的使用場景,這就是你之因此要掌握閉包的緣由。 用途:
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f();
}
checkScope(); //=> "local scope"
複製代碼
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return scope;
}
return f;
}
checkScope()(); //=> "local scope"
複製代碼
var scope = "window scope";
function checkScope() {
var scope = "local scope";
function f() {
return this.scope;
}
return f;
}
checkScope()(); //=> "window scope"
複製代碼
當前頁面有5個button,要求是點擊每一個button的時候彈出對應的編號
//html代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<button>Button0</button>
<button>Button1</button>
<button>Button2</button>
<button>Button3</button>
<button>Button4</button>
</body>
</html>
複製代碼
//js
for(var i = 0;i<btnList.length;i++){
//錯誤的代碼 onclick是異步觸發的,
btnList[i].onclick = function(){
console.log(i)
}
//正確的代碼
//採用「當即執行函數Immediately-Invoked Function Expression (IIFE)」的方式建立做用域
(function(i){
btnList[i].onclick = function(){
console.log(i)
}
})(i);
}
複製代碼
todo 當即執行函數不傳i
img對象常常用於數據上報,以下:
var report = function(src) {
var img = new Image();
img.src = src;
}
report('http://xxx.com/getUserInfo');
複製代碼
這段代碼在運行時,發如今一些低版本瀏覽器上存在bug,會丟失部分數據上報,緣由是img是report函數中的局部變量,當report函數調用結束後,img對象隨即被銷燬,而此時可能還沒來得及發出http請求,因此這次請求就會丟失。
所以,咱們使用閉包把img對象封閉起來,就能夠解決數據丟失的問題:
var report = (function() {
var imgs = [];
return function(src) {
var img = new Image();
imgs.push(img);
img.src = src;
}
})()
(function(){
//i在外部就不認識啦
for(var i=0;i<count;i++){}
})();
console.log(i);//報錯,沒法訪問
複製代碼
todo 傳參複雜數據 react vue 的實現 tgou https
var fn=function(){
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
console.log(fn(1,2));//3
//優化版本
var fn=(function(){
var cache={}//將結果緩存到該對象中
return function(){
var str=JSON.stringify(arguments);
if(cache[str]){//判斷緩存中是否存在傳遞過來的參數,存在直接返回結果,無需計算
return cache[str];
}else{//進行計算並返回結果
var sum=0;
for(var i=0;i<arguments.length;i++){
sum+=arguments[i];
}
return cache[str]=sum;
}
}
})()
複製代碼
在經典面向對象的編程語言中,Constructor是一種在內存已分配給該對象的狀況下,用於初始化新建立對象的方法。 在JavaScript中,幾乎全部的東西都是對象,咱們一般最感興趣的是object構造器。
Object構造器用於構建特定類型的對象--準備好對象以備使用,同時接收構造器可使用的參數,以在第一次建立對象時,設置成員屬性和方法的值。
// 構造器模式
function Car(model, year, miles){
this.model = model;
this.year = year;
this.miles = miles;
Car.prototype.toString = function(){
return this.model + 'has done ' + this.miles + ' miles';
}
}
var civic = new Car('honda civic', 2019, 2000);
console.log(civic.toString());
複製代碼
todo prototype位置
在JavaScript中,Module模式用於進一步模擬類的概念。經過這種方式,可以使一個單獨的對象擁有公有/私有方法和變量,從而屏蔽來自全局做用域的特殊部分。 產生的結果是:函數名與在頁面上其餘腳本定義的函數衝突的可能性下降。
var myNamespace = (function(){
var obj = {};
// 私有計數器變量
var myPrivateVar = 0;
// 記錄全部參數的私有函數
var myPrivateMethods = function(bar){
console.log('my privateVar in private = '+ bar);
}
//公有變量
var myPublicVar = 'foo';
// 調用私有變量和函數的公有函數
function myPublicMethods(bar){
// 增長私有計數器值
myPrivateVar = bar;
console.log('my privateVar in public = '+ myPrivateVar);
// 調用私有函數 並傳入參數
myPrivateMethods(bar);
}
obj.myPublicVar = myPublicVar;
obj.myPublicMethods = myPublicMethods;
return obj;
})();
// myNamespace.myPrivateVar;
// 用戶能夠調用公有函數,訪問和變動私有函數及私有變量
myNamespace.myPublicMethods('user input');
複製代碼
Module模式使用閉包封裝「私有」狀態和組織。它提供了一種包裝混合共有/私有方法和變量的方式,防止其泄漏至全局做用域,並與別的開發人員的接口發生衝突。經過該模式,只需返回一個共有API,而其餘的一切都維持在私有閉包裏。 在Module模式中,共有部分(閉包)能夠接觸私有部分,然而外界沒法接觸類的私有部分,模塊中的 myPrivateVar 和 myPrivateMethods 是私有的,所以應用程序的其餘部分沒法直接讀取它。它只與模塊的閉包一塊兒存在。