函數 基本概念 在JS中,函數實際上是對象,是Function類型的實例。 函數名是指向函數對象的指針。 function sum(num1, num2){ return num1 + num2; } // 檢查sum是否是Function類型的實例 console.log(sum instance ...
函數
基本概念
-
在JS中,函數實際上是對象,是
Function
類型的實例。 -
函數名是指向函數對象的指針。
function sum(num1, num2){
return num1 + num2;
}
// 檢查sum是否是Function類型的實例
console.log(sum instanceof Function); // true
let anotherSum = sum; // 指向同一個函數
console.log(anotherSum(1,2)); // 3
- 函數將若幹條語句封裝起來,可以提高代碼的可復用性。
創建函數
函數聲明
語法:
function funcName(params){
// do something...
return something;
}
函數表達式
語法:
let funcName = function(params){
// do something...
return something;
}
因為函數本質上是對象,所以可以直接賦值給一個變數。這個變數將指向這個函數,調用函數時直接使用變數名即可。
箭頭函數
語法:
let funcName = (params)=>{
// do something...
return something;
}
註:箭頭函數是ES6新加入的語法。
構造函數
語法:
let funcName = new Function("param1","param2",...,"body");
構造函數接收任意多個字元串參數,最後一個是函數體,前面的是函數參數。
註:這種寫法不推薦,因為作為JS語句,它首先會被當做常規代碼解析,然後再通過Function
構造函數解析形成函數對象,性能較差。
箭頭函數
箭頭函數是ES6新增的語法。任何可以使用函數表達式的地方,都可以使用箭頭函數。
語法格式
- 如果沒有參數,要寫括弧:
let getRandom = () => {return Math.random();};
- 如果參數只有一個,可以不寫括弧:
let double = x => {return x*2; };
- 如果有多個參數,要寫括弧:
let sum = (x, y) => {return x + y; };
- 如果函數體有多條語句,則需要大括弧:
let division = (a, b)=>{
if(b === 0){
console.error("除數不能為零");
}else{
return a/b;
}
}
- 如果函數體只有一條語句,可以不寫大括弧,此時會隱式返回這行代碼的值:
let double = x => x*2;
特點
-
箭頭函數語法簡潔。
-
箭頭函數不能使用
arguments
、super
,也不能用作構造函數,且箭頭函數沒有prototype
屬性。
參數列表
JS的函數參數和大多數其他語言不同,JS中的函數參數定義是無關緊要的。
假如定義函數時預設有兩個參數,而調用時傳入了0個、1個或者3個參數,解釋器都不會報錯。
原因是使用function
關鍵字定義的函數,可以在函數內部訪問arguments
對象,從中獲取傳入的參數。
function testArgs(){
console.log(arguments);
}
testArgs(1,2,3,4,5,6);
testArgs(1,"Hello",true);
arguments
是一個類數組對象,它並不是Array
的實例對象,並不是真正的數組,但是它具有類似數組的一些操作,比如可以像數組一樣通過[]
訪問元素的值,可以通過length獲取元素個數。
可以通過arguments[0]
訪問到傳入的第一個參數,通過arguments.length
獲取傳入多少個參數。
箭頭函數的參數
註:箭頭函數不能使用arguments
,所以箭頭函數需要多少參數需要在定義函數時就寫明。
不存在函數重載
在其他語言比如C++
、Java
中,存在函數重載,即可以存在多個函數名相同的函數,只要它們的參數個數、參數類型不一樣。在調用函數的時候,會根據傳入的參數數據類型,匹配上對應的函數。
顯然,在JS中,函數的參數列表是隨意的(參數個數與參數數據類型都是隨意的),也就不存在函數重載了。
註:在JS中,如果有多個函數名相同的函數定義,只有最後一個是有效的,前面的定義都將被覆蓋。
預設參數值
// 返回一個由隨機數組成的數組,預設長度為5
function getRandomArray(length = 5){
const arr = new Array();
for(let i=0;i<length;i++){
arr.push(Math.floor(Math.random()*100));
}
return arr;
}
const arr1 = getRandomArray();
console.log(arr1);
//[76, 86, 60, 21, 37]
const arr2 = getRandomArray(10);
console.log(arr2);
//[18, 98, 76, 43, 77, 24, 44, 74, 58, 3]
- 在函數參數後面使用
=
就可以為其設置預設值。
函數聲明和函數表達式
-
函數聲明指通過
function functionName(){}
來得到一個函數. -
函數表達式是指通過
function(){}
創建一個匿名函數,再講這個匿名函數賦值給一個變數functionName
.
區別
函數聲明提升
所有函數聲明在JS引擎執行代碼之前會被提升到代碼頂部,即以下兩段代碼是等價的。
console.log(sum(1,2)); // 不會報錯,因為sum會被提升到頂部
function sum(x,y){
return x+y;
}
function sum(x,y){
return x+y;
}
console.log(sum(1,2));
而函數表達式不會被提升:
console.log(sum(1,2)); // 報錯
let sum = function(x,y){
return x+y;
}
sum
作為一個變數:
-
如果使用
var
聲明會被提升,但是定義不會被提升,undefined
無法被調用; -
如果使用
let
則聲明和定義都不會被提升,一個未聲明的變數顯然無法被調用。
函數作為值
函數名是一個變數,因此函數可以作為函數參數傳遞給另一個函數,並且函數也可以作為一個函數的返回值。
回調函數
如果當前無法決定調用哪個函數,可以先把函數作為參數傳遞,等到可以做出明確的選擇時,再回過頭來調用這個函數。這種情況下作為參數傳遞的函數就被叫做回調函數(callback).
典型應用
向後端介面發送網路請求時,一般會附帶上兩個回調函數,一個success
函數,一個error
函數。在發送網路請求時,是無法確定請求是否發送成功的。只有拿到了返回結果,才能確定執行success
函數或error
函數。
閉包
閉包是指引用了另一個函數作用域中變數的函數。通常在嵌套函數中實現。
// 獲取累加器
function getAccumulator(){
let num = 0; // 內部屬性
return function(){ // 返回一個函數
return num++;
}
}
// 調用函數獲取累加器
let add = getAccumulator();
console.log(add()); // 0
console.log(add()); // 1
console.log(add()); // 2
console.log(add()); // 3
console.log(add()); // 4
在函數getAccumulator
的作用域中,有一個num
屬性,而返回的函數中,引用了這個num
. 即返回的這個函數引用了另一個函數作用域中的變數.
num
這個變數和add
函數綁定在了一起,當getAccumulator
執行結束後也不會被回收,並且無法被直接訪問。
立即調用的函數
立即調用的函數表達式(IIFE, Immediately Invoked Function Expression)
ES5沒有塊級作用域,可以使用立即調用的函數表達式來模擬塊級作用域。
var funcName = function(){...};
funcName();
使用立即調用的函數表達式可以省略一個不必要的函數名。
(function(){
...
})();
ES6之後,IIFE就沒有那麼必要了,因為可以很方便地使用塊級作用域。