js深入理解之作用域鏈

語法分析,


分析3樣東西


第1步: 先分析參數
第2步: 再分析變量聲明
第3步: 分析函數聲明


一個函數能使用的局部變量,就從上面的3步分析而來




具體步驟:


0: 函數運行前的1瞬間, 生成 Active Object (活動對象),下稱AO
1: 
   1.1 函數聲明的參數,形成AO的屬性,值全是undefined,
   1.2 接收實參,形成AO相應的屬性的值


2: 分析變量聲明聲明聲明! 如 var age, 
   如果AO上還沒有age屬性,則添加AO屬性,值是undefined
   如果AO上已經有age屬性,則不做任何影響
   


3: 分析函數聲明,如 function foo() {}, 
則把函數賦給AO.foo屬性
注: 如果此前foo屬性已存在,則被無情的覆蓋了
*/






function t(age) {
    alert(age);
}


t(5); // 5
t();//   undefined
/*
詞法分析過程:
AO {age:undefined}


運行過程:
t(5)--> AO.age=5; alert(AO.age); //5


t() ---> AO.age沒得到賦值, 還是undefined
*/






function t2(age) {
    var age = 99;
    alert(age);
}


t2(5); 
/*
分析過程:


0: 形成AO = {}
1: 
   1.1 分析形參 AO = {age:undefined}
   1.2 接收形參 AO = {age:5}


2: 分析var age, 發現AO已有age屬性,不做任何影響




執行過程:
AO.age = 99;
alert(age);
*/






function t3(greet) {
    var greet = 'hello'; // 試着把這一句變成 var greet;再做分析
    alert(greet);


    function greet() {
    }


    alert(greet);
}


t3(null);  // hello hello


/*
詞法分析過程:
0: AO = {}
1: 
   1.1 分析參數 AO = {greet:undefined}
   1.2 分析參數 AO = {greet:null}
2: 分析greet變量聲明,AO已經有greet屬性,因此不做任何影響
3: 分析greet函數聲明, AO.greet = function() {} , 被覆蓋成函數




執行過程:
greet = 'hello';
alert(greet);
alert(greet);
*/




// 再看這道題


function a(b) {
   alert(b);
   function b(){
        alert (b);
    }
    b();
}


a(1);


/*


分析期:
0: AO = {}
1: 
   1.1分析參數 AO = {b:undefined}
   1.2接收參數  AO = {b:1}


2: 分析var 聲明,此函數沒有var


3: 分析函數聲明, AO = {b: function(){alert(b);}}


執行期:
alert(b);  // function
b();       // 由作用域尋找到a函數中的b,即 function,alert()出來   


*/



function a(b) {
   alert(b);
   b = function (){
        alert (b);
   }
   
   b();
}


a(1);


/*
學員常見答案:
1,1
1, function
1, undefined
function ,function 
*/




/*
詞法分析過程:
0: AO = {}
1: 分析參數 AO = {b:undefined} --> {b:1}
2: 分析var聲明,沒有.
3: 分析函數聲明??  沒有!


(注: b = function() {} ,是一個賦值過程,在執行期纔有用)




執行過程:
alert(b); // 1
b = function() {
    alert(b);
}
b();  // function


*/




// 函數聲明,與函數表達式
/*
JS被稱爲披着C外衣的Lisp語言, 
lisp是一種強大的函數式語言


函數可以賦值給變量,可以作爲參數來傳遞.
*/


function t1() {
}


t2 = function() {
}


// 這2種方式,效果不同的,
// t1是函數聲明, 雖然全局內也得到一個t1變量,值是function
// 而t2 只是一個賦值過程------值是誰? 值是右側的表達式的返回結果,即函數






// 就是說 function () {} 在js看來,就和 3*2, 6/3 一樣,是個表達式,返回一個結果


// 因此,t1 t2 兩種方式在詞法分析時,有着本質區別
// 前者 在詞法分析階段,就發揮作用
// 而後者,在運行階段,才發揮作用






// 知道了函數表達式的概念,再看看一個你以前看不懂的東西
(function(window,undefined) {
})(window);


這是jquery的最外層代碼


/*
(function(window,undefined){})  // 內層表達式,返回值是函數,包在小括號裏,當成表達式來執行\
(function(window,undefined){})(window) // 立即調用


// 而內層函數又沒有起名字,稱爲匿名函數,
// 這種手法,匿名函數,立即執行,不污染全局.   稱爲 立即執行匿名函數表達式


*/


// 思路: 爲什麼傳window, 而又不會傳undefined?
/*
答: 傳window是爲了速度


function() {
    function() {
        function () {
            function () {
                function () {
                    document.getElementById... // 這個document將會尚作用域層層上找,直到最外層
                }
            }
        }
    }
}




jquery就是爲了加快內部查找變局變量的速度, 而直接把window以參數形式傳進來
這樣 window 就在jquery內部的 AO上






不傳undefined是爲了安全
因爲在IE,FF低版本中 ,  undefined竟然可以重新賦值 ,如 undefined = 3;




聲明undefined局部變量(名字是undefined而已), 同時,又不傳參,值自然是undefined
防止了外界對undefined的污染


*/

function t1(){ var d function t2(){ d = 5; e=6; }t2();}t1();console.log(d); //d is not definedconsole.log(e); //6console.log(window.d);//undefined

//注意:變量以window.xxx引用,變量不存在,以某個屬性不存在報的錯  undefined
  // 變量以xxx引用的時候,尋找不到,則報的是 xxx is not undefined
//  var 僅僅是一個聲明變量的操作,  d = 5 只是一個賦值操作





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章