一、JavaScript作用域
JavaScript變數其實只有兩種作用域,全域變數和函數的內部變數。在函數內部任何一個地方定義的變數(var scope)其作用域都是整個函數體。
全域變數:指的是window物件下的物件屬性。
作用域劃分:基於上下文,以函數進行劃分的,而不是由區塊劃分的。
強調兩點:
1. 在同一作用域中,JavaScript是允許變數的重複定義,後一個定義會覆寫前一個定義。
2. 函數內部如果不加關鍵字var而定義的變量,則預設為全域變數。
var scope="global"; function t(){ console.log(scope); //"global" scope="local" console.log(scope); //"local" } t(); console.log(scope); //"local" var scope="global"; function t(){ console.log(scope); //"undefined" var scope="local" console.log(scope); //"local" } t(); console.log(scope); //"global"
在變數解析過程中先找局部的作用域,然後找上層作用域。在第一段程式碼的函數當中沒有定義變數scope,於是尋找上層作用域(全域作用域),進而進行輸出其值。但在第二段程式碼的函數內定義了變數scope(無論是在console之後還是之前定義變量,都認為在此作用域擁有變量scope),於是不再向上層的作用域進行查找,直接輸出scope。但不幸的是此時的局部變數i並沒有賦值,所以輸出的是undefined。
//所以根据函数作用域的意思,可以将上述第二段代码重写如下: var scope="global"; function t(){ var scope; console.log(scope); scope="local" console.log(scope); } t();
由於函數作用域的特性,局部變數在整個函數體始終是由定義的,我們可以將變數宣告」提前「到函數體頂部。
var b; //第1步 function fun(){ b = "change"; } alert(b);//输出undefined,由于第1步只定义未赋值 var b; //第1步 function fun(){ b = "change"; } fun(); //调用上述函数 alert(b); //输出change
當使用var宣告一個變數時,所建立的這個屬性是不可配置的,也就是說無法透過delete運算子刪除。
二、作用域實例
<html> <head> <script type="text/javascript"> function buttonInit(){ for(var i=1;i<4;i++){ var b=document.getElementById("button"+i); b.addEventListener("click",function(){ alert("Button"+i);},false); } } window.onload=buttonInit; </script> </head> <body> <button id="button1">Button1</button> <button id="button2">Button2</button> <button id="button3">Button3</button> </body> </html>
當註冊事件結束後,i的值為4,當點擊按鈕時,事件函數即function(){ alert("Button"+i);}這個匿名函數中沒有i,根據作用域鏈,所以到buttonInit函數中找,此時i的值為4,所以彈出」button4「。
三、javaScript閉包
在js中,閉包主要涉及到js的幾個其他的特性:作用域鏈,垃圾(內存)回收機制,函數嵌套,等等。
1. 作用域鏈:簡單來說,作用域鏈就是函數在定義的時候創建的,用於尋找使用到的變數的值的一個索引,而他內部的規則是,把函數本身的本地變數放在最前面,把自身的父級函數中的變數放在其次,把再高一級函數中的變數放在更後面,以此類推直至全局物件為止。當函數中需要查詢一個變數的值的時候,js解釋器會去作用域鏈去查找,從最前面的本地變數先找,如果沒有找到對應的變量,則到下一層的鏈上找,一旦找到了變量,就不再繼續。如果找到最後也沒找到需要的變量,則解釋器會傳回undefined。
2. Javascript的垃圾回收機制:在Javascript中,如果一個物件不再被引用,那麼這個物件就會被GC回收。如果兩個物件互相引用,而不再被第3者所引用,那麼這兩個互相引用的物件也會被回收。因為函數a被b引用,b又被a外的c引用,這就是為什麼函數a執行後不會被回收的原因。建構一個閉包,這些變數將不會被記憶體回收器回收,只有當內部的函數不被呼叫以後,才會銷毀這個閉包,而沒有任何一個閉包引用的變數才會被下一次記憶體回收啟動時所回收。
3. 有了閉包,巢狀的函數結構才可以運作
四、利用js閉包實現循環綁定事件
<html> <head> <title>闭包</title> </head> <body> <ul id="list"> <li>第1条记录</li> <li>第2条记录</li> <li>第3条记录</li> <li>第4条记录</li> <li>第5条记录</li> <li>第6条记录</li> </ul> <script type="text/javascript"> function tt(nob) { this.clickFunc = function() { alert("这是第" + (nob + 1) + "记录"); } } var list_obj = document.getElementById("list").getElementsByTagName("li"); //获取list下面的所有li的对象数组 for (var i = 0; i<= list_obj.length; i++){ console.log(list_obj[i]) list_obj[i].onmousemove = function(){ this.style.backgroundColor = "#cdcdcd"; } list_obj[i].onmouseout = function() { this.style.backgroundColor = "#FFFFFF"; } //list_obj[i].onclick = function() { // alert("这是第" + i + "记录"); //不能正常获取 alert出来的都是:“这是第6记录” //} var col = new tt(i); //调用tt函数 list_obj[i].onclick = col.clickFunc; //执行clickFunc函数 } </script> </body> </html>
以上就是本文的全部內容,希望對大家學習javascript程式設計有所幫助。