前言:“函式是物件,函式名是指標。”,函式名僅僅是指向函式的指標,與其他包含函式指標的變數沒有什麼區別,話句話說,一個函式可能有多個名字。
-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; // };