js之function
function*
function*
這種宣告方式(function
關鍵字後跟一個星號)會定義一個生成器函式 (generator function),它返回一個 Generator
物件。
你也可以定義 生成器函式 使用建構函式 GeneratorFunction
和一個 function* expression
。
語法
function* name([param[, param[, ... param]]]) { statements }
-
name
- 函式名
-
param
- 要傳遞給函式的一個引數的名稱,一個函式最多可以有255個引數。
-
statements
- 普通JS語句。
描述
生成器函式在執行時能中途退出,後面又能重新進入繼續執行。而且在函式內定義的變數的狀態都會保留,不受中途退出的影響。
呼叫一個生成器函式並不會馬上執行它裡面的語句,而是返回一個這個生成器的迭代器(iterator)物件。當這個迭代器的 next()
方法被首次(後續)呼叫時,其內的語句會執行到第一個(後續)出現yield
表示式的位置為止,該表示式定義了迭代器要返回的值,或者被 yield*
委派至另一個生成器函式。next()方法返回一個物件,這個物件包含兩個屬性:value 和 done,value 屬性表示本次 yield
表示式的返回值,done 屬性為布林型別,表示生成器是否已經產出了它最後的值,即生成器函式是否已經返回。
呼叫 next()
方法時,如果傳入了引數,那麼這個引數會取代生成器函式中對應執行位置的 yield
表示式(整個表示式被這個值替換)
當在生成器函式中顯式 return
時,會導致生成器立即變為完成狀態,即呼叫 next()
方法返回的物件的 done
true
。如果 return
了一個值,那麼這個值會作為下次呼叫 next()
方法返回的 value 值。
示例
簡單示例
- function* idMaker(){
- var index = 0;
- while(index<3)
- yield index++;
- }
- var gen = idMaker();
- console.log(gen.next().value); // 0
- console.log(gen.next().value); // 1
- console.log(gen.next().value); // 2
- console.log(gen.next().value); // undefined
Arrow function
基本用法
ES6允許使用“箭頭”(=>
)定義函式。
var f = v => v;
上面的箭頭函式等同於:
var f = function(v) {
return v;
};
如果箭頭函式不需要引數或需要多個引數,就使用一個圓括號代表引數部分。
var f = () => 5;
// 等同於
var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同於 var sum = function(num1, num2) { return num1 + num2; };
如果箭頭函式的程式碼塊部分多於一條語句,就要使用大括號將它們括起來,並且使用return
語句返回。
var sum = (num1, num2) => { return num1 + num2; }
由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號。
var getTempItem = id => ({ id: id, name: "Temp" });
箭頭函式可以與變數解構結合使用。
const full = ({ first, last }) => first + ' ' + last;
// 等同於
function full(person) { return person.first + ' ' + person.last; }
使用注意點
箭頭函式有幾個使用注意點。
(1)函式體內的this
物件,就是定義時所在的物件,而不是使用時所在的物件。
(2)不可以當作建構函式,也就是說,不可以使用new
命令,否則會丟擲一個錯誤。
(3)不可以使用arguments
物件,該物件在函式體內不存在。如果要用,可以用Rest引數代替。
(4)不可以使用yield
命令,因此箭頭函式不能用作Generator函式。
this
指向的固定化,並不是因為箭頭函數內部有繫結this
的機制,實際原因是箭頭函式根本沒有自己的this
,導致內部的this
就是外層程式碼塊的this
。正是因為它沒有this
,所以也就不能用作建構函式。
除了this
,以下三個變數在箭頭函式之中也是不存在的,指向外層函式的對應變數:arguments
、super
、new.target
。
function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}
foo(2, 4, 6, 8) // args: [2, 4, 6, 8]
上面程式碼中,箭頭函式內部的變數arguments
,其實是函式foo
的arguments
變數。
另外,由於箭頭函式沒有自己的this
,所以當然也就不能用call()
、apply()
、bind()
這些方法去改變this
的指向。
(function() {
return [
(() => this.x).bind({ x: 'inner' })() ]; }).call({ x: 'outer' }); // ['outer']
上面程式碼中,箭頭函式沒有自己的this
,所以bind
方法無效,內部的this
指向外部的this
。
長期以來,JavaScript語言的this
物件一直是一個令人頭痛的問題,在物件方法中使用this
,必須非常小心。箭頭函式”繫結”this
,很大程度上解決了這個困擾。
函式繫結
箭頭函式可以繫結this
物件,大大減少了顯式繫結this
物件的寫法(call
、apply
、bind
)。但是,箭頭函式並不適用於所有場合,所以ES7提出了“函式繫結”(function bind)運算子,用來取代call
、apply
、bind
呼叫。雖然該語法還是ES7的一個提案,但是Babel轉碼器已經支援。
函式繫結運算子是並排的兩個雙冒號(::),雙冒號左邊是一個物件,右邊是一個函式。該運算子會自動將左邊的物件,作為上下文環境(即this物件),繫結到右邊的函式上面。
foo::bar;
// 等同於
bar.bind(foo);
foo::bar(...arguments);
// 等同於 bar.apply(foo, arguments); const hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn(obj, key) { return obj::hasOwnProperty(key); }
如果雙冒號左邊為空,右邊是一個物件的方法,則等於將該方法繫結在該物件上面。
var method = obj::obj.foo;
// 等同於
var method = ::obj.foo; let log = ::console.log; // 等同於 var log = console.log.bind(console);
由於雙冒號運算子返回的還是原物件,因此可以採用鏈式寫法。
尾呼叫優化
什麼是尾呼叫?
尾呼叫(Tail Call)是函數語言程式設計的一個重要概念,本身非常簡單,一句話就能說清楚,就是指某個函式的最後一步是呼叫另一個函式。
function f(x){
return g(x);
}
上面程式碼中,函式f的最後一步是呼叫函式g,這就叫尾呼叫。
“尾呼叫優化”(Tail call optimization),即只保留內層函式的呼叫幀。如果所有函式都是尾呼叫,那麼完全可以做到每次執行時,呼叫幀只有一項,這將大大節省記憶體。這就是“尾呼叫優化”的意義。
嚴格模式
ES6的尾呼叫優化只在嚴格模式下開啟,正常模式是無效的。
這是因為在正常模式下,函式內部有兩個變數,可以跟蹤函式的呼叫棧。
func.arguments
:返回呼叫時函式的引數。func.caller
:返回呼叫當前函式的那個函式。
尾呼叫優化發生時,函式的呼叫棧會改寫,因此上面兩個變數就會失真。嚴格模式禁用這兩個變數,所以尾呼叫模式僅在嚴格模式下生效。
function restricted() {
"use strict";
restricted.caller; // 報錯
restricted.arguments; // 報錯 } restricted();
箭頭函式與常規函式對比
一個箭頭函式與一個普通的函式在兩個方面不一樣:
- 下列變數的構造是詞法的:
arguments
,super
,this
,new.target
- 不能被用作建構函式:沒有內部方法
[[Construct]]
(該方法允許普通的函式通過new
呼叫),也沒有prototype
屬性。因此,new (() => {})
會丟擲錯誤。
除了那些意外,箭頭函式和普通的函式沒有明顯的區別。例如, typeof
和 instanceof
產生同樣的結果:
> typeof () => {}
//'function'
> () => {} instanceof Function
//true
> typeof function () {}
//'function'
> function () {} instanceof Function
//true
函式表示式和物件字面量是例外,這種情形下必須放在括號裡面,因為它們看起來像是函式宣告和程式碼塊。