畢業了最近在找工做,面試一家公司遇到了許多javascript的題,由於只是之前稍微瞭解了一點,遇到了一些之前沒接觸的東西。許多的面試題裏面會有讀程序的題,當時不會作回來一搜發現這些題都是在考js的做用域。javascript
1、JavaScript做用域html
JavaScript變量實際上只有兩種做用域,全局變量和函數的內部變量。在函數內部任何一個地方定義的變量(var scope)其做用域都是整個函數體。
全局變量:指的是window對象下的對象屬性。
做用域劃分:基於上下文,以函數進行劃分的,而不是由塊劃分的。
強調兩點:
1. 在同一做用域中,JavaScript是容許變量的重複定義,而且後一個定義將覆蓋前一個定義。
2. 函數內部若是不加關鍵字var而定義的變量,默認爲全局變量。java
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); //「local」 scope="local" console.log(scope); //「local」 } 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運算符刪除。
2、做用域實例閉包
<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>
依次彈出button1,button2,button3,當註冊事件結束後,i的值爲4,當點擊按鈕時,事件函數即function(){ alert("Button"+i);}這個匿名函數中沒有i,根據做用域鏈,因此到buttonInit函數中找,此時i的值爲4,因此彈出」button4「。函數
3、利用js閉包實現循環綁定事件this
<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>
這裏的for循環中一開始就會循環完畢,若是不是經過tt()傳入的nob記錄的話,就不能正常獲取alert出來的數。code