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


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

 

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

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

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

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

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

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

 

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

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

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

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

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

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; // };

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM