函數本質是對象。每一個函數都是 Function 類型的實例。
javascript
定義函數有兩種方式:函數聲明、函數表達式。
java
使用 "function" 關鍵字 (會變量提高,但不建議這樣作),代碼以下:
編程
// 變量提高
console.log(fn); // [Function: fn]
// "function
function fn() {
// 函數內容...
}複製代碼
使用 "var" 或 "let" 關鍵字 ("var"會致使聲明提高,一樣不建議這樣作),代碼以下:數組
// 聲明提高
console.log(fn1); // undefined
var fn1 = function () {
// 處理代碼...
}
// "let" 聲明不會被提高
console.log(fn1); // 報錯!!!
let fn2 = function () {
// 處理代碼...
}複製代碼
其實函數表達式是先定義了匿名函數(也叫拉姆達函數),再將匿名函數賦值給變量。
promise
function plus(num1, num2) {
if (!num1) {
return 0
}
if (num2) {
return num1 + num2
}
return num1
}
console.log(plus()); // 0
console.log(plus(11)); // 11
console.log(plus(2, 1)); // 3複製代碼
Funtion()
和new Funtion()
建立函數let fn1 = new Function('return 1');
let fn2 = Function('return 2');
console.log(fn1(), fn2()); // 1 2複製代碼
雖然以上代碼無語法錯誤,但和eval()
函數同樣,可能會被瀏覽器阻止。
瀏覽器
參數定義時,能夠爲其提供默認值。這部份內容和解構賦值有關,點擊連接看另外一篇文章。下面是簡單的例子:閉包
// 參數的默認值
function fn1(x, y = 2) {
console.log(x, y)
}
// 參數的解構賦值
function fn2({ x, y }) {
console.log(x, y)
}
// 參數聲明時使用解構賦值
function fn3({x, y = 5} = {}) {
console.log(x, y);
}
// 使用展開運算符和解構賦值
function fn4(...value) {
console.log(value)
}
fn1(10); // 10 2
fn2({ y: 22, x: 11 }); // 11 22
fn3(); // undefined 5
fn4(1, 2, 3); // [1,2,3]
複製代碼
length 屬性能夠獲取沒有指定默認值的參數個數。看下面例子:app
function fn1(x, y) { }
function fn2(x, y = 1) { }
console.log(fn1.length); // 2
console.log(fn2.length); // 1複製代碼
箭頭函數的最大特色就是"綁定" this 的指向,使其指向定義時所在的對象。基本語法以下:異步
// 箭頭後的圓括號表示直接返回的值
let addOne = (x, y) => (value + 1)
// 能夠像普通匿名函數同樣,處理和返回值 (單個參數能夠省略箭頭左邊的括號)
let addTen = value => {
value += 10;
return value
}
console.log(addOne(1, 2)); // 3
console.log(addTen(1)); // 11複製代碼
使用注意點:async
new
運算符。函數的 name 屬性,返回該函數的函數名。
function foo() {}
foo.name // "foo"
// 函數賦值,但name仍返回原來函數的名字
baz = foo;
baz.name // "foo"
// 綁定的函數會被加上"bound "
foo.bind({}).name // "bound foo"
// 匿名函數,返回空字符串
(function(){}).name // ""
複製代碼
關於Promise
的介紹在另外一篇文章。
"async function" 的本質是Promise
對象,是Promise
的語法糖。因此,一樣可使用then()
和catch()
。其內部能使用(也只能在其內部使用)的一個關鍵字await
,表示等待該行代碼執行結束,而後再繼續後面的代碼。與Promise
不一樣,"async function" 就是函數,只有被調用時纔會執行,而非定義後立刻執行。下面給出簡單例子:
// 2s 以後返回雙倍的值
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
// 定義async函數
async function testResult() {
console.log('---2s後打印resolve---');
let result = await doubleAfter2seconds(30);
setTimeout(() => {
console.log('再過1.5s後打印"await"後語句')
}, 1500)
console.log(`result:${result}`);
}
testResult(30);
/* 打印結果: ---2s後打印resolve--- result:60 再過1.5s後打印"await"後語句 */複製代碼
爲了加深理解,能夠試試下面的一道題:
async function async1() {
console.log( 'async1 start' );
await async2();
console.log( 'async1 end' );
}
async function async2() {
console.log( 'async2' );
}
console.log( 'script start' );
setTimeout( function () {
console.log( 'setTimeout' );
}, 0);
async1();
new Promise(function (resolve) {
console.log( 'promise1' );
resolve();
}).then(function () {
console.log( 'promise2' );
});
console.log( 'script end' );複製代碼
結果以下:
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout複製代碼
迭代器是一種特殊對象,它具備一些專門爲迭代過程設計的專有接口,全部的迭代器對象都有一個next()
方法,每次調用都返回一個結果對象。結果對象有兩個屬性:一個是value
,表示下一個將要返回的值;另外一個是done
,它是一個布爾類型的值,當沒有更多可返回數據時返回true
。迭代器還會保存一個內部指針,用來指向當前集合中值的位置,每調用一次next()
方法,都會返回下一個可用的值。若是在最後一個值返回後再調用next()
方法,那麼返回的對象中屬性done
的值爲true
,屬性value
若未定義,則爲undefined
。
以爲複雜的話,想一想數組對象,它就是內置迭代器,循環遍歷時(如for
),就是調用迭代器,每一回的循環都.next().value
獲取值,直到結束。
下面以Array
對象的values()
方法獲取一個迭代器:
let arr = ['a', 4, { a: 1 }];
let myIterator = arr.values();
console.log(myIterator.next().value); // 'a'
console.log(myIterator.next().value); // 4
console.log(myIterator.next().value); // { a: 1 }
console.log(myIterator.next().done); // true複製代碼
生成器是生成迭代器的函數,經過function *
或function*
定義,函數中的特有關鍵字yield
,表示當前迭代器返回的值,直到迭代器使用next()
,則跳到下一個yield
停頓,有點相似於return
。下面給出簡單例子:
const arr = ['blue', 'green', 'red'];
function *creatIterator(arr) {
for (let i = 0; i < arr.length; i++) {
// 表示每次調用都返回arr[i]的值,與return相似
yield arr[i];
}
}
const item = creatIterator(arr);
console.log('第一次調用:' + item.next().value);
console.log('第二次調用:' + item.next().value);
console.log('第三次調用:' + item.next().value);
console.log('第四次調用:' + item.next().done);
/* 打印結果: 第一次調用:blue 第二次調用:green 第三次調用:red 第四次調用:true */複製代碼
函數被定義時,則自動建立兩個屬性:arguments
和this
,且只能在函數內部使用。函數執行時會建立caller
屬性,指向調用本函數的外部函數。注意,這三個屬性都在嚴格模式下('use strict'
)沒法使用。
arguments 是一個對應於傳遞給函數的參數的類數組對象。不是數組,但能夠轉化成數組。函數內部一定存在的一個對象。看下面一個例子:
function fn () {
console.log(arguments);
}
fn('a', '', 'c'); // { '0': 'a', '1': '', '2': 'c' }複製代碼
能夠看出 arguments 依次包含 3 個參數。
arguments 對象內部還有 3 個不可遍歷的屬性:
通常來講,this 指向執行本函數的上下文,但能夠經過 "apply" 、 "call" 和 "bind" 方法改變指向。更多詳細描述請查看另外一篇博文。
當本函數執行時,caller
屬性指向調用本函數的外部函數;當本函數執行結束後,caller
會被賦值爲null
。看以下例子:
function foo() {
function fn() {
console.log('fn內部:',fn.caller);
}
function baz() {
let obj = {}
obj.myFn = fn;
let globalFn = fn;
obj.myFn();
globalFn();
}
baz();
console.dir('fn外部:',fn.caller);
}
foo();
// fn內部: function baz()
// fn內部: function baz()
// fn外部: null複製代碼
遞歸的定義是函數內部調用函數自身。上面提到能夠用 "arguments.callee" 獲取函數自己,但嚴格模式下是不容許的,看如下例子:
"use strict";
function fn(num) {
console.log(num--)
if (num > 0) {
// 嚴格模式下會報錯
// arguments.callee(num);
// 建議使用
fn(num);
}
}複製代碼
當一門編程語言的函數能夠被看成變量同樣用時,則稱這門語言擁有頭等函數。例如,在這門語言中,函數能夠被看成參數傳遞給其餘函數,能夠做爲另外一個函數的返回值,還能夠被賦值給一個變量。如下示例都證實 JavaScript 是擁有頭等函數。
// 函數複製給變量
let foo = function () { }
// 函數返回函數
function baz() {
return function () { }
}
// 函數做爲參數
function foz(foo) { }複製代碼
高階函數是一個接受其餘函數做爲參數或將函數做爲返回值返回的函數。如下給出兩種高階函數的例子。
function fn() { }
// 接受函數做爲參數的高階函數
function hof1(fn) { }
// 以函數作爲返回值的高階函數
function hof2() {
return function () { }
}複製代碼
一元函數是一個只接受一個參數的函數。與之對應的是多元函數 (multivariate functions)。
function uf(value) { }複製代碼
柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數且返回結果的新函數的技術。
// 未柯里化的函數
function sum(a, b, c, d) {
return a + b * c - d
}
// 柯里化後的函數
function curryingSum1(a) {
return function (b) {
return function (c) {
return function (d) {
return a + b * c - d
}
}
}
}
// 箭頭函數版的柯里化函數
let curryingSum2 = a => b => c => d => a + b * c - d
// 使用柯里化函數
curryingSum2(1)(2)(3)(4); // 3複製代碼
上面例子能夠看得出,箭頭函數比較適合寫柯里化的函數。
函數反作用是指函數在正常工做任務以外對外部環境所施加的影響,函數不只僅只是返回了一個值,並且還作了其餘的事情。以下例子:
let myObject = { a: 1 }
function fn() {
myObject.a = 11
return true
}複製代碼
函數反作用的函數也稱爲非純函數 (Impure Function)。
輸入輸出數據流全是顯式(Explicit)的,意思是,函數與外界交換數據只有一個惟一渠道 —— 參數和返回值。函數從函數外部接受的全部輸入信息都經過參數傳遞到該函數內部。函數輸出到函數外部的全部信息都經過返回值傳遞到該函數外部。簡單說就是沒有反作用的函數。
function add1(a) {
return a + 1
}複製代碼
閉包函數指的是有權訪問另外一個函數做用域中的變量的函數。這部分是比較大的內容,能夠點擊連接查看詳細。
本篇不做詳述,另開篇幅整理此類容,請點擊連接。