JavaScript變數作用域(Variable Scope)和閉包(closure)的基礎知識
在這篇文章中,我會試圖講解JavaScript變數的作用域和宣告提升,以及許多隱隱藏的陷阱。為了確保我們不會碰到不可預見的問題,我們必須真正理解這些概念。
基本定義
作用範圍是個“木桶”,裡面裝著變數。變數可以是區域性或者全域性性的,但在子範圍中定義的變數是可以訪問父範圍的,這一點可能會造成一些困擾。
在JavaScript中使用"var"關鍵字宣告變數。一旦在父範圍宣宣告,就會作為各自子範圍的一部分。即在本地範圍內有效,但本地定義的變數不可在全域性範圍內訪問。
讓我們來看一個例子。執行下面的程式碼,你會發現,你能列印出全域性範圍定義的變數,而全域性範圍無法訪問區域性範圍定義的變數。
var agloballydefinedvariable = 'Global'; function someFunction() { var alocallydefinedvariable = 'Local'; console.log(agloballydefinedvariable); // Global } console.log(alocallydefinedvariable); // Uncaught ReferenceError: alocallydefinedvariable is not defined
作用域鏈(Scope Chain)
如果你忘記使用“var”的關鍵字來定義區域性變數,事情可能會變得非常糟糕。為什麼會這樣呢?因為JavaScript會首先在父作用域內搜尋一個未定義的變數,然後再到全域性範圍進行搜尋。在下面的例子中,JavaScript知道變數“a”是someFunction()的一個區域性變數,在anotherFunction()中它會尋找它父作用域內的變數。
var a = 1; function someFunction() { var a = 2; function anotherFunction() { console.log(a); // 2 } }
更復雜的情況是,在下面的例子中,一個變數沒有在函式中進行作用域的限定。
在someFunction()中呼叫了一個沒有在函式範圍內定義的變數 a=2; 這個分配將覆蓋全域性變數的值。
後續引用將指向全域性變數的值。
var a = 1; function someFunction() { a = 2; function anotherFunction() { console.log(a); // 2 } anotherFunction(); } someFunction(); console.log(a); //2
宣告提升(Hoisting)
Hoisting會將在函式或全域性範圍內的變數“提升”到頂部宣告的過程。請記住,只有量宣告被提升了,初始化或值分配等等沒有變化,在下面的程式碼的情況下,第一個輸出將不確定...但它不會丟擲任何錯誤。
console.log(a); //undefined var a = 1; console.log(a); //1
Window範圍
在基於瀏覽器的JavaScript中,定義為全域性範圍內的一部分變數實際上是所謂的“Window”物件的屬性。這裡的Window是指“容器”。換句話說,當你想從一個區域性範圍修改全域性定義的變數,你也可以通過修改Window物件的相應的屬性來做到這一點。
var myVariable = 'Global Scope'; function myFunction() { window.myVariable = 'Something Else'; } myFunction(); console.log(myVariable); // Something Else
可能的陷阱
如果在函式內部分配一個以前沒有被定義的變數的值,它會自動成為全域性範圍的一部分。
function myFunction() { myVariable = 'JavaScript'; } myFunction(); console.log(myVariable); //JavaScript
如果你不小心忘記定義了一個區域性變數,你的整個指令碼可能會執行混亂。
var city="LA"; var team="Lakers"; function showTeam () { console.log (city + " " + team); } function showCity () { city = "Moscow"; console.log (city); } showTeam(); // LA Lakers showCity(); // Moscow /* 因為上面的 showCity 中定義的變數 "city" 沒有使用 "var" 宣告,全域性範圍內的變數被覆蓋了。因此會導致下面的問題 :) */ showTeam(); // Moscow Lakers
內部函式依然會儲存區域性變數即使它的外部函式已經執行完畢
這聽起來可能有點怪異,看一個例子,就會更容易理解。解釋這一點的最好辦法是使用一個簡單的“Hello World”的例子。
function greet(who) { var iterations = 0; return function () { console.log(++iterations); return 'Hello ' + who + '!'; }; } var greeting = greet('World'); console.log(typeof greeting); //function console.log(typeof greeting()); //string & iterations=1 console.log(greeting()); //Hello World! & iterations=2 console.log(greeting("Universe")); //Hello World! & iterations=3 //輸出不是 Hello Universe. world 被閉包封閉儲存了起來
注* 在電腦科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函式和與其相關的引用環境組合而成的實體。閉包在執行時可以有多個例項,不同的引用環境和相同的函式組合可以產生不同的例項。
閉包的概念出現於60年代,最早實現閉包的程式語言是Scheme。之後,閉包被廣泛使用於函數語言程式設計語言如ML語言和LISP。很多命令式程式語言也開始支援閉包。 引自:Wiki
正如你上面看到的那樣,greet() 返回一個被稱為“閉包”的內部函式。閉包除了會儲存他們自己本地作用域內部的封閉起來的函式和變數外,還會儲存外部引用的引數。參看我們的具體例子,引數 who 和 iterations 就是被閉包封閉起來的區域性變數。
這意味著,greeting已成為一個包含who和iterations在內的函式(直接返回的匿名函式)。- 它不會再次執行greet,它只會執行閉包而且返回結果永遠是 "Hello World!"。
原文地址: codepunker.com
相關文章
- 面試-JS基礎知識-作用域和閉包、this面試JS
- javascript 基礎(作用域和閉包)JavaScript
- 【JS基礎】作用域和閉包JS
- Go基礎知識-02 作用域 常量 變數 作用域(持續更新)Go變數
- 變數的作用域--js閉包變數JS
- JavaScript之作用域和閉包JavaScript
- Javascript閉包(Closure)JavaScript
- JS基礎總結(3)——作用域和閉包JS
- 【譯】終極指南:變數提升、作用域和閉包變數
- Javascript-this/作用域/閉包JavaScript
- JavaScript物件導向~ 作用域和閉包JavaScript物件
- 深入理解javascript原型和閉包(14)——從【自由變數】到【作用域鏈】JavaScript原型變數
- 迴圈輸出——閉包、變數作用域變數
- JavaScript中變數和作用域JavaScript變數
- 夯實基礎中篇-圖解作用域鏈和閉包圖解
- 深入理解閉包之前置知識→作用域與詞法作用域
- 學習Javascript閉包(Closure)JavaScript
- JavaScript從作用域到閉包JavaScript
- javascript變數物件函式呼叫棧作用域閉包等細解!JavaScript變數物件函式
- 《JavaScript 闖關記》之作用域和閉包JavaScript
- javascript基礎(函式與方法的區別,變數作用域,變數和函式的宣告提前,函式作用域)(十五)JavaScript函式變數
- [JavaScript基礎] 函式,初識作用域JavaScript函式
- [JavaScript閉包]Javascript閉包的判別,作用和示例JavaScript
- 原型模式故事鏈(5)--JS變數作用域、作用域鏈、閉包原型模式JS變數
- javascript中的閉包closure詳解JavaScript
- Javascript深入之作用域與閉包JavaScript
- javascript中的作用域(全域性變數和區域性變數)JavaScript變數
- Java基礎06:變數、常量、作用域Java變數
- JavaScript 變數的作用域鏈JavaScript變數
- 深入淺出JavaScript之閉包(Closure)JavaScript
- java基礎06-變數、常量、作用域Java變數
- JAVA基礎6-變數、常量、作用域Java變數
- 遊戲基礎知識——“禁令”的作用和形式遊戲
- 現代 JavaScript 的變數作用域JavaScript變數
- 理解 Javascript 中變數的作用域JavaScript變數
- javascript忍者祕籍-第五章 閉包和作用域JavaScript
- 深入理解javascript原型和閉包(12)——簡介【作用域】JavaScript原型
- Js基礎知識(一) – 變數JS變數