前言:「函數是對象,函數名是指針。」,函數名僅僅是指向函數的指針,與其餘包含函數指針的變量沒有什麼區別,話句話說,一個函數可能有多個名字。前端
-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; // };