[英]What is the purpose of wrapping whole Javascript files in anonymous functions like “(function(){ … })()”?
我最近讀了很多 Javascript,我注意到整個文件在要導入的 .js 文件中如下所示。
(function() {
...
code
...
})();
這樣做的原因是什么而不是一組簡單的構造函數?
它通常用於命名空間(見下文)並控制成員函數和/或變量的可見性。 把它想象成一個對象定義。 它的技術名稱是立即調用函數表達式(IIFE)。 jQuery 插件通常是這樣寫的。
在 Javascript 中,您可以嵌套函數。 因此,以下內容是合法的:
function outerFunction() {
function innerFunction() {
// code
}
}
現在您可以調用outerFunction()
,但innerFunction()
的可見性僅限於outerFunction()
的范圍,這意味着它是outerFunction()
私有的。 它基本上遵循與 Javascript 中的變量相同的原則:
var globalVariable;
function someFunction() {
var localVariable;
}
相應地:
function globalFunction() {
var localFunction1 = function() {
//I'm anonymous! But localFunction1 is a reference to me!
};
function localFunction2() {
//I'm named!
}
}
在上述場景中,您可以從任何地方調用globalFunction()
,但不能調用localFunction1
或localFunction2
。
當您編寫(function() { ... })()
時,您正在做的事情是將第一組括號內的代碼制作為函數文字(意味着整個“對象”實際上是一個函數)。 之后,您將自行調用剛剛定義的函數(最后一個()
)。 因此,正如我之前提到的,它的主要優點是您可以擁有私有方法/函數和屬性:
(function() {
var private_var;
function private_function() {
//code
}
})();
在第一個示例中,您將通過名稱顯式調用globalFunction
來運行它。 也就是說,您只需執行globalFunction()
即可運行它。 但是在上面的例子中,你不只是定義一個函數; 您正在一次定義和調用它。 這意味着當你的 JavaScript 文件被加載時,它會立即被執行。 當然,你可以這樣做:
function globalFunction() {
// code
}
globalFunction();
除了一個顯着的區別外,行為基本上是相同的:當您使用 IIFE 時避免污染全局范圍(因此這也意味着您不能多次調用該函數,因為它沒有名稱,但是因為此功能僅在真正不成問題時才執行)。
IIFE 的巧妙之處在於,您還可以在內部定義事物,並且只向外界公開您想要的部分(命名空間的示例,因此您基本上可以創建自己的庫/插件):
var myPlugin = (function() {
var private_var;
function private_function() {
}
return {
public_function1: function() {
},
public_function2: function() {
}
}
})()
現在您可以調用myPlugin.public_function1()
,但不能訪問private_function()
! 非常類似於類定義。 為了更好地理解這一點,我推薦以下鏈接以供進一步閱讀:
編輯
我忘了提。 在那個 final ()
中,你可以在里面傳遞任何你想要的東西。 例如,當您創建 jQuery 插件時,您可以像這樣傳入jQuery
或$
:
(function(jQ) { ... code ... })(jQuery)
因此,您在這里所做的是定義一個接受一個參數的函數(稱為jQ
,一個局部變量,並且只有該函數知道)。 然后,您將自行調用該函數並傳入一個參數(也稱為jQuery
,但這個參數來自外部世界,是對實際 jQuery 本身的引用)。 沒有迫切需要這樣做,但有一些優點:
前面我描述了這些函數如何在啟動時自動運行,但如果它們自動運行,誰在傳遞參數? 該技術假定您需要的所有參數都已定義為全局變量。 因此,如果 jQuery 尚未定義為全局變量,則此示例將不起作用。 正如您可能猜到的,jquery.js 在其初始化期間所做的一件事是定義一個“jQuery”全局變量,以及它更著名的“$”全局變量,它允許此代碼在包含 jQuery 后工作。
以最簡單的形式,這種技術旨在將代碼包裝在函數范圍內。
它有助於減少以下機會:
它不會檢測文檔何時准備好 - 它不是某種document.onload
也不是window.onload
它通常被稱為Immediately Invoked Function Expression (IIFE)
或Self Executing Anonymous Function
。
var someFunction = function(){ console.log('wagwan!'); };
(function() { /* function scope starts here */
console.log('start of IIFE');
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})(); /* function scope ends */
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
在上面的示例中,函數中定義的任何變量(即使用var
聲明)都將是“私有的”並且只能在函數范圍內訪問(正如 Vivin Paliath 所說)。 換句話說,這些變量在函數之外是不可見/不可訪問的。 見現場演示。
Javascript具有函數作用域。 “在函數中定義的參數和變量在函數之外是不可見的,而在函數內任何地方定義的變量在函數內的任何地方都是可見的。” (來自“Javascript:好的部分”)。
最后,之前貼的代碼也可以這樣寫:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
};
myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
有一天,有人可能認為“必須有一種方法可以避免命名 'myMainFunction',因為我們只想立即執行它。”
如果你回到基礎,你會發現:
expression
:評估為值的東西。 即3+11/x
statement
:代碼行做某事但它沒有評估為一個值。 即if(){}
類似地,函數表達式計算為一個值。 一個后果(我假設?)是它們可以立即被調用:
var italianSayinSomething = function(){ console.log('mamamia!'); }();
所以我們更復雜的例子變成了:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
}();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
下一步是思考“為什么要有var myMainFunction =
如果我們甚至不使用它!?”。
答案很簡單:嘗試刪除它,如下所示:
function(){ console.log('mamamia!'); }();
它不起作用,因為“函數聲明不可調用” 。
訣竅是通過刪除var myMainFunction =
我們將函數表達式轉換為函數聲明。 有關這方面的更多詳細信息,請參閱“資源”中的鏈接。
下一個問題是“為什么我不能將它作為函數表達式保留為var myMainFunction =
以外的東西?
答案是“你可以”,實際上有很多方法可以做到這一點:添加一個+
,一個!
, a -
, 或者可能用一對括號括起來(就像現在按照慣例所做的那樣),我相信還有更多。 例如:
(function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.
或者
+function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console
或者
-function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console
因此,一旦將相關修改添加到我們曾經的“替代代碼”中,我們就會返回與“代碼解釋”示例中使用的完全相同的代碼
var someFunction = function(){ console.log('wagwan!'); };
(function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
閱讀有關Expressions vs Statements
的更多信息:
人們可能想知道的一件事是“當你沒有在函數內'正確'定義變量時會發生什么——即改為進行簡單的賦值?”
(function() {
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
myOtherFunction = function(){ /* oops, an assignment instead of a declaration */
console.log('haha. got ya!');
};
})();
myOtherFunction(); // reachable, hence works: see in the console
window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope
myFunction(); // unreachable, will throw an error, see in the console
基本上,如果未在其當前范圍內聲明的變量被分配一個值,則“查找范圍鏈直到它找到變量或到達全局范圍(此時它將創建它)”。
在瀏覽器環境中(與 nodejs 等服務器環境相比),全局范圍由window
對象定義。 因此我們可以做window.myOtherFunction()
。
我關於這個主題的“良好實踐”提示是在定義任何東西時始終使用var
:無論是數字、對象還是函數,甚至在全局范圍內也是如此。 這使得代碼更簡單。
筆記:
block scope
(更新:在ES6中添加了塊作用域局部變量。)function scope
和global scope
(瀏覽器環境中的window
作用域) 閱讀有關Javascript Scopes
的更多信息:
一旦你得到這個IIFE
概念,它就會導致module pattern
,這通常是通過利用這個 IIFE 模式來完成的。 玩得開心 :)
瀏覽器中的 Javascript 實際上只有幾個有效范圍:函數范圍和全局范圍。
如果變量不在函數范圍內,則它在全局范圍內。 全局變量通常很糟糕,所以這是一個將庫變量保留給自身的構造。
這叫做閉包。 它基本上將代碼密封在函數內部,以便其他庫不會干擾它。 這類似於在編譯語言中創建命名空間。
例子。 假設我寫:
(function() {
var x = 2;
// do stuff with x
})();
現在其他庫無法訪問我創建的要在我的庫中使用的變量x
。
您也可以在更大的表達式中使用函數閉包作為數據,就像在這種確定瀏覽器對某些 html5 對象的支持的方法中一樣。
navigator.html5={
canvas: (function(){
var dc= document.createElement('canvas');
if(!dc.getContext) return 0;
var c= dc.getContext('2d');
return typeof c.fillText== 'function'? 2: 1;
})(),
localStorage: (function(){
return !!window.localStorage;
})(),
webworkers: (function(){
return !!window.Worker;
})(),
offline: (function(){
return !!window.applicationCache;
})()
}
除了將變量保持在本地之外,一個非常方便的用途是在使用全局變量編寫庫時,您可以給它一個較短的變量名以在庫中使用。 它經常用於編寫 jQuery 插件,因為 jQuery 允許您使用 jQuery.noConflict() 禁用指向 jQuery 的 $ 變量。 如果它被禁用,您的代碼仍然可以使用 $ 而不會中斷,如果您這樣做:
(function($) { ...code...})(jQuery);
我們還應該在作用域函數中使用“使用嚴格”來確保代碼應該在“嚴格模式”下執行。 示例代碼如下所示
(function() {
'use strict';
//Your code from here
})();
為接受的答案提供一個示例,來自https://requirejs.org/docs/whyamd.html :
(function () {
var $ = this.jQuery;
this.myExample = function () {};
}());
代碼表明我們可以:
this
,這是瀏覽器的window
對象。它在 OOP 中稱為封裝。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.