做用域是指程序源代碼中定義變量的區域。javascript
實際上描述的就是查找變量的範圍,做用域必須有的兩個功能就是存儲變量以及查找變量,做用域就是發揮這兩個做用以及更多做用的規則。java
做用域規定了如何查找變量,也就是肯定當前執行代碼對變量的訪問權限。閉包
JavaScript採用的是詞法做用域模塊化
在代碼中任何地方都能訪問到的對象擁有全局做用域函數
全局變量:設計
- 生命週期將存在於整個程序以內。
- 能被程序中任何函數或者方法訪問。
- 在 JavaScript 內默認是能夠被修改的。
JavaScript 採用詞法做用域(lexical scoping),也就是靜態做用域。code
帶有關鍵字 var 的聲明對象
var winValue = 10; console.log(window.winValue); // 10
不帶有聲明關鍵字的變量,JS 會默認幫你聲明一個全局變量繼承
function foo(value) { result = value + 1; // 沒有用 var 聲明 return result; }; foo(1); console.log(window.result); // 2 <= 掛在了 window全局對象上
函數做用域內,對外是封閉的,從外層的做用域沒法直接訪問函數內部的做用域。生命週期
在函數內部的變量權限稱爲函數做用域,有如下特色:
function bar() { var foo = 'test'; } console.log(foo); // Uncaught ReferenceError: foo is not defined
凡是由{}符號包裹起來的都是塊做用域
for(let i = 0; i < 5; i++) { // ... } console.log(i); // Uncaught ReferenceError: i is not defined
在 for 循環執行完畢以後 i 變量就被釋放了
做用域鏈:當訪問一個變量時,解釋器會首先在當前做用域查找,若是沒有找到,就去父做用域找,直到找到該變量或者不在父做用域中,這就是做用域鏈。
var a = 1 function foo () { var b = 2 console.log(a) } foo() // 1 console.log(b) // Uncaught ReferenceError: b is not defined
從上面代碼的執行結果能夠看出,foo 函數取到了它外部的變量 a, 而最外層的 console.log(b) 操做並沒能取得 foo 函數裏面的變量 b。
若是去查找一個普通對象的屬性,可是在當前對象和其原型中都找不到時,會返回undefined;但查找的屬性在做用域鏈中不存在的話就會拋出ReferenceError。
閉包是指有權訪問另一個函數做用域中的變量的函數
在javascript中,只有函數內部的子函數才能讀取局部變量,因此閉包能夠理解成"定義在一個函數內部的函數"。在本質上,閉包是將函數內部和函數外部鏈接起來的橋樑。
之因此出現閉包是由於JS的垃圾回收機制,JS自己爲了不解釋器過量消耗內存,形成系統崩潰,自帶有一套垃圾回收機制,垃圾回收機制可以檢測到一個對象是否是無用的。檢測到以後,就會把它佔用的內存釋放掉。可是實際工做中,咱們也會須要一些變量不那麼及時的被清理,因此就出現了閉包,用來達成這個效果。
function getOuter(){ var name = 'jacky'; function getName(str){ console.log(str + name); // 能夠訪問getName函數外部的name } return getName('名字是:'); } getOuter(); // 名字是:jacky
再來看下面一段代碼:
function bar() { var x = 1; return function () { var y = 2; return x + y; } } var foo = bar(); // 這一句執行完,變量x並無被回收,由於要內部函數還須要引用 console.log(foo()) // 3 執行內部函數,引用外部變量x
上面代碼中,在全局執行上下文中定義了一個函數bar和變量foo,函數bar內部返回一個匿名函數,因此此刻匿名函數的做用域鏈初始化爲包含了全局變量對象和bar中的變量對象。
當執行var foo = bar()時,把函數bar的執行上下文壓入棧,當bar執行完後,其執行上下文應該彈出棧,可是由於bar內部的匿名函數做用域鏈還引用這bar函數內的變量x,因此bar的執行上下文得不到釋放,這樣就造成了閉包。
1.設計私有的方法和變量(封裝,定義模塊)。
var counter = (function(){ var privateCounter = 0; //私有變量 function change(val){ privateCounter += val; } return { increment:function(){ change(1); }, decrement:function(){ change(-1); }, value:function(){ return privateCounter; } }; })();
2.匿名函數最大的用途是建立閉包。減小全局變量的使用。從而使用閉包模塊化代碼,減小全局變量的污染。
var objEvent = objEvent || {}; (function() { var addEvent = function() { // some code } function removeEvent() { // some code } objEvent.addEvent = addEvent objEvent.removeEvent = removeEvent })()
addEvent 和 removeEvent 都是局部變量,但咱們能夠經過全局變量 objEvent 使用它
javascript中,若是一個對象再也不被引用,那麼這個對象就會被垃圾回收機制回收。若是兩個對象互相引用,而再也不被第3者所引用,那麼這兩個互相引用的對象也會被回收。
即釋放對閉包的引用,使引用變量爲null。
優勢:
缺點:
造成閉包即要把一個函數當成值傳遞,並且該函數還引用這另外一個函數的做用域鏈使得被引用的函數不能被回收,使用不當容易形成內存泄漏;