1. 程式人生 > >詞法作用域,作用域鏈

詞法作用域,作用域鏈

在外 AR false 火狐 console 代碼 繪制 條件 foo

1.函數聲明和函數表達式的區別

function foo(){}

var foo=function(){}

1.上面的語法是聲明,可以提升,因此在函數定義的上方也能調用該函數

2.下面的語法是函數表達式,函數名是foo,它會提升,提升的不是函數體

3.函數表達式也支持名字語法

function foo(){
}
console.log(foo.name);//foo

**函數有一個屬性name,表示的是函數名。函數聲明的函數定義的屬性值name就是該函數名,如果是函數表達式定義的函數定義,如果後面沒有帶函數名的,默認是函數表達式變量的值,如果後面帶了函數名那麽name屬性值就是該函數名。函數聲明能夠直接使用name屬性值,但是函數表達式不能在函數表達式之外使用函數的name屬性,只能在函數表達式裏面使用函數的name屬性值。

//函數聲明
function foo(){
};
foo.name;//結果是foo
foo;//結果返回整個foo的函數體


//函數表達式
var foo1=function (){} //默認的是foo1.name就是foo1

var foo2=function bbb(){}
//foo2.name就是bbb,但是這種函數表達式的函數定義的name屬性值不能在外部直接使用。
//bbb  //返回的bbb is not defined

var foo3=function bbb(){
  console.log(bbb);   //這裏能使用函數的name屬性值     
}

foo3();//輸出bbb的函數體

註意:

在新的瀏覽器中,包含在邏輯判斷(if,while)裏面的函數聲明會被轉換成特殊的函數表達式(函數聲明的變量會提升,函數表達式的變量不會提升);

看一下例子

//如果是谷歌,火狐等主流瀏覽器

if(true){
   function fun1(){console.log(123)}  
}else{
  function fun1(){console.log(456)}  
}

fun1();//結果123;
if(true){
   function fun1(){console.log(456)}  
}else{
  function fun1(){console.log(123)}  
}

fun1();
//結果456; /*代碼解析,按照javascript的預編譯解析,一步一步走下來。因為函數聲明包含在if這種邏輯判斷裏面,所以是特殊的函數表達式,不會發生變量提升。 if(true)->聲明fun1,然後執行fun1,所以結果是輸出123;*/ //如果是在IE8以下這種瀏覽器 if(true){ function fun1(){console.log(123)} }else{ function fun1(){console.log(456)} } /* 代碼解析:包含在邏輯判斷裏面的函數聲明還是函數聲明,會發生變量提升,fun1聲明,fun1覆蓋前面這個聲明 ,所以不管是條件是true或者是false,最後調用fun1()輸出的結果都是456; */

2.詞法作用域

*****

作用域:在js中出現域表示的就是範圍,即作用範圍。就是一個名字可以在什麽地方使用,不可以在什麽地方使用。

在c,java等語言中,花括號表示的是塊級作用域

  偽代碼(僅僅用於描述意思即可)

  {  

    var name=123;

    {

      console.log(name);//輸出結果:123

    }

  }

console.log(name);//作用域之外訪問name,表示的是name is undefined

******

詞法作用域:所謂詞法(代碼)作用域,就是代碼在編寫過程中體現出來的作用範圍,代碼一旦寫好了,沒有運行之前(不用執行),作用範圍就已經確定好了,這個就是所謂的詞法作用域。

詞法作用域的規則:

   1.函數允許訪問函數外部的數據

2.整個代碼結構中只有函數才能限定作用域

3.作用規則首先使用變量提升規則分析

4.如果當前作用規則裏面有該名字,則不考慮外面的外面的名字

if(false){
  var num=123;  
}
console.log(num); //undefined

/*
代碼說明:num變量發生了函數提升,首先定義了var num,但是由於false沒有進入下面的var num=123的賦值操作,所以下面console.log(num)的時候顯示undefined
*/
var num=123;
function foo(){
    var num=456;
    function func(){
         console.log(num);
    }
    func();  
}

foo();//結果輸出:456;

/*
代碼說明:首先最外面的num和foo發生了變量提升,執行foo()的時候,進入foo的作用域裏面,num和func發生了變量提升,執行func()的時候,需要輸出num,func裏面沒有num,需要外跳一級尋找num,如果這裏面有num,則不查詢外面的num了。
*/

3.作用域鏈

繪制規則

(1)全局變量,函數申明都是屬於0級鏈,每個對象占一個位置

(2)凡是看到函數就延伸一個鏈出來,一級級展開

(3)訪問首先看到當前函數,如果當前作用域鏈沒有定義,往上級鏈中檢查

(4)如此往復,直到0級鏈,如果0級沒有,則彈出錯誤,這個變量沒有定義

var num=10;
var func1=function(){
     var num=20;
    var func2=function(){
        var num=30;
        alert(num);  
    };
    func2();  
}

func1();//結果彈出:30

繪制作用域鏈,代碼分析也標註在圖上

技術分享圖片

以下這段代碼值得好生體會一下:

function func1(){
   alert(num);  
}

function func2(){
   var num=456;
   function func3(){
       func1();
   }
   func3();  
}

func2();//結果顯示:num is not defined

繪制作用域鏈,代碼步驟分析標註在圖上

技術分享圖片

詞法作用域,作用域鏈