JavaScript 之有趣的函數(函數聲明、調用、預解析、做用域)

前言:「函數是對象,函數名是指針。」,函數名僅僅是指向函數的指針,與其餘包含函數指針的變量沒有什麼區別,話句話說,一個函數可能有多個名字。前端

 

-1.函數聲明,function+函數名稱。調用方法:函數名(參數);函數

function f1(x,y){
    return x+y;     //函數體
}
console.log(f1(2,3));

這是最多見的指定函數名聲明函數,在函數體內返回參數值,函數調用時纔會輸出結果。既然說到函數,那就免不了提一提它的預解析以及做用域。性能

此類方法定義的函數,在代碼開始執行以前會經過解釋器進行一個函數聲明提早的過程,並將其添加到執行環境中, JavaScript 引擎會將其提高到代碼樹的頂端,率先執行,因此即便聲明函數的代碼在函數調用代碼的後面,也能正確訪問,上述過程就被稱爲函數的預解析。例如:spa

console.log(sum);          //控制檯輸出函數源代碼,證實函數能夠被調用
console.log(sum(3,2));     // 5
function sum(x,y){
    return x+y;
}

執行環境定義了變量或者函數有權訪問其餘數據,每一個環境中都有一個與之關聯的變量對象,環境中定義的變量和函數都保存在這個對象中,解析器在處理數據時就會使用這個對象。指針

 

- 2.匿名函數,即沒有命名的函數,經過給將函數賦值給變量的形式聲明函數,也稱之爲函數表達式。調用方法:函數名(參數);code

var f2 = function(x,y){
                return x+y;
            }
f2(4,5);                

此類方法定義的函數不能在函數聲明前調用函數,由於在預解析機制中:對象

1.把變量的聲明提高到當前做用域的最前面,只會提高聲明,不會提高賦值。 blog

2.把函數的聲明提高到當前做用域的最前面,只會提高聲明,不會提高調用。ip

3.先提高var,在提高function 內存

因此上述代碼若是提早調用函數,在預解析的執行環境中是:

var f2;       //變量聲明提高,但賦值沒有
f2(4,5);     //報錯
console.log(f2(4,5));
f2 = function(x,y){
   return x+y;
}

匿名函數是用一個變量去接收函數,所以預解析只將變量的聲明提早了,此時的函數位於一個初始化語句中,在執行到函數體代碼以前變量不會保存對函數的引用,因此直接報錯。

 

-3.自調用函數,顧名思義就是本身調用本身的函數,在聲明的同時進行調用,雖然方便但只能執行一次。

(function(){
      console.log("這是一個自調用函數");
})();
/* * * 而它的演變過程也很簡單,是由函數表達式演變而來。 * var f1 = function(){ * console.log("這是一個自調用函數"); * } * f1(); * 這裏把調用時的 f1 替換成 function(){} * 因此調用時就是function(){}(); * 爲了保持代碼的總體性,因此在最外層加了一個括號。 * * */

//這裏還有一個點須要注意,自調用函數前的一個函數若是沒有輸出調用,須要在函數表達式的結尾加上分號,以和自調用函數區分開來,防止將上一個函數也解析成自調用函數。

 

 

-4.Function 構造函數,Function 構造函數能夠接受任意數量的參數,但最後一個參數始終被看作是函數體,而前面則看做是函數體的參數。調用:函數名();

var sum = new Function("sum1","sum2","return sum1+sum2");

 但這種定義函數的方法並不推薦使用,由於在此類函數在執行時會先解析一次常規ECMAscript 代碼,再解析傳入函數中的字符串,反覆調用時將會影響性能。

 函數也是有數據類型的,全部函數的類型都是 function 。 

 

關於做用域鏈

做用域鏈的用途是,保證執行環境中,有權訪問的全部函數和變量的有序訪問。簡單來講就是變量的使用範圍。

 

全局變量:在函數之外,用 var 聲明的變量都是全局變量,在全局執行環境中均可以使用。 !!! 但須要注意,全局變量只有在整個程序退出或者銷燬後纔會被釋放,不然就一直佔內存。

局部變量:在函數內部定義的變量就是局部變量,只能在函數內部使用。

隱式全局變量:沒有var 聲明的,也是做用於全局,可是可使用delete刪除。

全局做用域:全局變量的使用範圍,始終是做用域鏈中的最後一個對象。

局部做用域:局部變量的使用範圍。

塊級做用域:指的是在一對大括號內聲明的變量,就只能在這對大括號中使用,可是js中所有均可以使用,因此js沒有塊級做用域,函數除外。

 

使用過程:1.做用域鏈的前端始終是當前執行代碼所在環境的變量對象,解析過程是按照做用域鏈一級一級搜索的過程。

     2.始終是從做用域的前端開始的,而後逐級向後回溯。

     3.內部變量能夠經過做用域鏈訪問外部變量,但外部變量不能訪問任何內部的變量或者函數。(自內向外訪問)

 

最後給你們舉個栗子,綜合解說函數調用、預解析以及做用域:

f1();
   console.log(c);
   console.log(b);
   console.log(a);  
   function f1(){
      var a = b = c = 9;
        console.log(a);
        console.log(b);
        console.log(c);
   }

輸出的結果爲:9  9  9  9  9  報錯,緣由以下

function f1(){
       // var a = b = c = 9;   變量的聲明也提高
       var a;
       a = 9;  // 此時的 a 是被 var 聲明的,做爲函數內的局部變量,只能在函數內被調用,因此最後的a會報錯
       b = 9;  
       c = 9;  // b,c 則是隱式全局變量
       console.log(a);
       console.log(b);
       console.log(c);
}
f1();  //調用f1函數,預解析後f1函數的聲明提高到做用域的最前面,此時的代碼爲
console.log(c);
console.log(b);
console.log(a); 

第二個小栗子

f1();
var f1 = function (){
            console.log(a);
            var a = 10;
        };
//結果是報錯 //由於預解析的存在,因此函數和變量提早 //因爲是函數表達式的形式,因此預解析後的代碼爲:

// var f1; // f1(); // function (){ // console.log(a); // var a = 10; // };
相關文章
相關標籤/搜索