1. 程式人生 > >js之function

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,以下三個變數在箭頭函式之中也是不存在的,指向外層函式的對應變數:argumentssupernew.target

function foo() {
  setTimeout(() => {
    console.log('args:', arguments);
  }, 100);
}

foo(2, 4, 6, 8) // args: [2, 4, 6, 8]
 

上面程式碼中,箭頭函式內部的變數arguments,其實是函式fooarguments變數。

另外,由於箭頭函式沒有自己的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物件的寫法(callapplybind)。但是,箭頭函式並不適用於所有場合,所以ES7提出了“函式繫結”(function bind)運算子,用來取代callapplybind呼叫。雖然該語法還是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
 

函式表示式和物件字面量是例外,這種情形下必須放在括號裡面,因為它們看起來像是函式宣告和程式碼塊。