好程式設計師web前端分享詳細瞭解JavaScript函式
好程式設計師web前端分享詳細瞭解JavaScript函式,如果你曾經接觸過JavaScript程式設計,你一定不會陌生如何定義並且呼叫一個函式。但是你知道在JavaScript中有多少種定義函式的方法嗎?如果想要在Test262中編寫和維護這些方法的測試,那可真是一個很大的挑戰,尤其當一些新特性和現有函式語法相關,或者擴充套件了函式的API時。但是,想要斷言新提出或被提案的語法、API有效時,測試所有既存變式又是非常必要的。 下面會針對JavaScript中已經存在的函式定義方式進行一個概述。本文不包含Class宣告和表示式,因為這些方式建立的物件是“不可呼叫的”,本文旨在那些可生成“可呼叫”物件的函式定義方式。也就是說,我們不會研究那些複雜的引數列表(包含預設引數、結構賦值或者尾後逗號),因為那足夠另起文章介紹了。
以前的方式
函式宣告以及函式表示式
最為出名以及應用最廣的同樣也是這些舊方式:函式宣告和函式表示式。前者設計(1995)和出現在第一版的規範(1997)(pdf)中。後者則是出現在第三版中(1999)(pdf)。仔細研究,你會從它們當中提取出三種不同的方式。
// 函式宣告
function BindingIdentifier() {}
// 命名函式表示式
// (BindingIdentifier在函式外部是訪問不到的)
(function BindingIdentifier() {});
// 匿名函式表示式
(function() {});
值得注意的是,匿名函式表示式仍然可能有名字,Mike Pennisi在什麼是函式名稱?中有深度解釋。
Function建構函式
當研究一門語言的"function API"的時候,也就到了這門語言的底層。在這門語言的設計之初,函式宣告方式可以被理解為是Function建構函式API的最直接實現。Function建構函式提供了一種定義函式的方式:通過指明Function的引數,其中最後一個引數就是函式的函式體(必須要說明的是,這是一種動態程式碼方式,可能存在安全問題)。在大多數情況下,這種方式是不合適的,所以用的人很少,但是在第一版的ECMAScript中,這種方式就出現了。
new Function('x', 'y', 'return x ** y;');
新的方式
自從ES2015釋出以來,幾種新的定義函式方式被引入進來,這些方式的變式更是非常繁多。
另類匿名函式
這是一種新式的匿名函式。如果你曾經接觸過ES的模組化,那麼你很有可能已經接觸過這種定義函式的方式了。儘管這種方式看起來和匿名函式的定義方式很像,但是他確實有自己的名字:default
// 另類匿名函式宣告
export default function() {}
順便一提,這個名字並不是專屬的標識,並沒有進行繫結。
方法定義
下面這些方式定義的函式表示式、匿名函式或者命名函式,都是某個物件的屬性。注意這些並不是新的語法,只是應用上面提及的那些語法,寫在了某個物件的初始化器中。這種方式最早引入在ES3中。
let object = {
propertyName: function() {},
};
let object = {
// (BindingIdentifier不能再函式外部呼叫)
propertyName: function BindingIdentifier() {},
};
下面是存取器屬性,引入在ES5。
let object = {
get propertyName() {},
set propertyName(value) {},
};
在ES2015中,JavaScript中提供了一種定義方法的簡潔語法,不管是直接命名的方式還是計算屬性名的方式,都可以使用,而且,存取器同樣適用。
let object = {
propertyName() {},
["computedName"]() {},
get ["computedAccessorName"]() {},
set ["computedAccessorName"](value) {},
};
你也可以把這些定義屬性或者方法的新方式應用在建立類時。
// 類宣告
class C {
methodName() {}
["computedName"]() {}
get ["computedAccessorName"]() {}
set ["computedAccessorName"](value) {}
}
// 類表示式
let C = class {
methodName() {}
["computedName"]() {}
get ["computedAccessorName"]() {}
set ["computedAccessorName"](value) {}
};
...在定義靜態方法時,同樣可以使用。
// 類宣告
class C {
static methodName() {}
static ["computedName"]() {}
static get ["computedAccessorName"]() {}
static set ["computedAccessorName"](value) {}
}
// 類表示式
let C = class {
static methodName() {}
static ["computedName"]() {}
static get ["computedAccessorName"]() {}
static set ["computedAccessorName"](value) {}
};
箭頭函式
箭頭函式首次出現在ES2015中,儘管起初飽受爭議,但是現在已經被廣泛應用了。箭頭函式的定義根據是否簡寫有兩種不同的語法:賦值表示式(在箭頭後面沒有花括號)和函式體(當函式包含零個或者多個表示式時)。語法規定,當函式只有一個引數時,可以不用小括號括起來,但是當沒有引數或者多餘一個引數時,就必須用小括號括起來了。(這種語法就決定了箭頭函式會有多種定義形式)
// 零引數, 賦值表示式
(() => 2 ** 2);
// 一個引數, 可以省略小括號, 賦值表示式
(x => x ** 2);
// 一個引數, 可以省略小括號, 函式體
(x => { return x ** 2; });
// 多個引數, 賦值表示式
((x, y) => x ** y);
上面的最後一種形式中,引數是用引數列表來表示的,因為它們用小括號括了起來。類似於用小括號來標識引數列表的語法,還有其他形式,諸如({ x }) => x。 如果引數不用小括號括起來,那麼只能給引數起一個獨一的識別符號名稱,以在箭頭函式中使用。當箭頭函式被定義為非同步函式或者Generator函式時,這個識別符號名稱還可以加上await 或者 yield的字首,但那也已經是在不用括號情形中,考慮足夠深遠的了。 箭頭函式可以並且也經常出現在初始化器或者物件屬性的定義中,但是這種情況大部分使用的是上面介紹的賦值表示式形式,舉例如下:
let foo = x => x ** 2;
let object = {
propertyName: x => x ** 2
};
Generators
Generator函式的語法是在其他定義函式的方式上加點東西,但箭頭函式和存取器方法除外。你可以使用和之前函式宣告,函式表示式,函式定義甚至是建構函式等相似的方式。所有方法列舉如下:
// Generator 宣告
function *BindingIdentifer() {}
// 另類匿名 Generator 宣告
export default function *() {}
// Generator 表示式
// (BindingIdentifier只能在函式內部呼叫)
(function *BindingIdentifier() {});
// 匿名 Generator 表示式
(function *() {});
// 方法定義
let object = {
*methodName() {},
*["computedName"]() {},
};
// 在類宣告中定義方法
class C {
*methodName() {}
*["computedName"]() {}
}
// 在類宣告中定義靜態方法
class C {
static *methodName() {}
static *["computedName"]() {}
}
// 在類表示式中定義方法
let C = class {
*methodName() {}
*["computedName"]() {}
};
// 在類表示式中定義靜態方法
let C = class {
static *methodName() {}
static *["computedName"]() {}
};
ES2017
非同步函式
經過幾年的發展,非同步函式將會發布ES2017——第八版EcmaScript語言規範——規範會在2017年6月在正式釋出。但其實,很多開發者早已經開始使用非同步函數了,這還要歸功於Babel的支援。 非同步函式語法提供了一個乾淨的、統一的方式來描述非同步操作。當被呼叫時,非同步函式會返回一個Promise物件。當非同步執行結束後,這個Promise物件即會被相應執行。當函式中含有await表示式時,非同步函式就會暫停執行,這時,await表示式結果就會作為非同步函式的返回值。 非同步函式的語法並沒有太多的不同,只是在我們熟知的那些方式前面加上一個字首:
// 非同步函式宣告
async function BindingIdentifier() { /**/ }
// 另類匿名非同步函式宣告
export default async function() { /**/ }
// 命名非同步函式表示式
// (BindingIdentifier只能在函式內部呼叫)
(async function BindingIdentifier() {});
// 匿名非同步函式表示式
(async function() {});
// 非同步方法
let object = {
async methodName() {},
async ["computedName"]() {},
};
// 類宣告中的非同步方法
class C {
async methodName() {}
async ["computedName"]() {}
}
// 類宣告中的靜態非同步方法
class C {
static async methodName() {}
static async ["computedName"]() {}
}
// 類表示式中的非同步方法
let C = class {
async methodName() {}
async ["computedName"]() {}
};
// 類表示式中的靜態非同步方法
let C = class {
static async methodName() {}
static async ["computedName"]() {}
};
非同步箭頭函式
async 和 await並不是只侷限在常規函式的宣告或者表示式中,它們同樣適用於箭頭函式:
// 單一引數,賦值表示式
(async x => x ** 2);
// 單一引數,函式體
(async x => { return x ** 2; });
// 引數列表,賦值表示式
(async (x, y) => x ** y);
// 引數列表,函式體
(async (x, y) => { return x ** y; });
與ES2017結合
非同步Generator函式
和ES2017結合,async 和 await將會被擴充套件支援非同步函式。這一特性的發展可以追蹤推薦的github倉儲。正如你猜想到的,非同步Generator函式的語法是結合async、await以及已經存在的Generator函式宣告和表示式而來。當非同步Generator函式被呼叫的時候,會返回一個迭代器,這個迭代器的next()方法會返回一個Promise物件來處理迭代器的返回物件,而不是直接返回迭代器的結果物件。 非同步Generator函式已經開始在很多地方使用,你很有可能已經碰到過。
// 非同步Generator宣告
async function *BindingIdentifier() { /**/ }
// 另類匿名非同步Generator宣告
export default async function *() {}
// 非同步Generator表示式
// (BindingIdentifier只能在函式內部訪問)
(async function *BindingIdentifier() {});
// 匿名非同步Generator表示式
(async function *() {});
// 匿名非同步Generator方法定義
let object = {
async *propertyName() {},
async *["computedName"]() {},
};
// 類宣告中非同步Generator原型方法定義
class C {
async *propertyName() {}
async *["computedName"]() {}
}
// 類表示式中非同步Generator原型方法定義
let C = class {
async *propertyName() {}
async *["computedName"]() {}
};
// 類宣告中非同步Generator靜態方法定義
class C {
static async *propertyName() {}
static async *["computedName"]() {}
}
// 類表示式中非同步Generator靜態方法定義
let C = class {
static async *propertyName() {}
static async *["computedName"]() {}
相關推薦
好程式設計師web前端分享詳細瞭解JavaScript函式
好程式設計師web前端分享詳細瞭解JavaScript函式,如果你曾經接觸過JavaScript程式設計,你一定不會陌生如何定義並
好程式設計師web前端分享HTML元素強制不換行
好程式設計師web前端分享HTML元素強制不換行,HTML 中 nowrap是用來強制不換行的 在排版中
好程式設計師web前端分享JS檢查瀏覽器型別和版本
好程式設計師web前端分享JS檢查瀏覽器型別和版本,先取得Navigator物件的userAgent屬性的小寫資訊,之後根據正則表
好程式設計師web前端分享主流CSS image比較
好程式設計師web前端分享主流CSS image比較在還原設計圖的時候,難免會碰到一些樣式圖片的引用。如何來對這些圖片做優化呢?本
好程式設計師web前端分享css常用屬性縮寫
好程式設計師web前端分享css常用屬性縮寫,使用縮寫可以幫助減少你CSS檔案的大小,更加容易閱讀。css縮寫的主要規則如下:
好程式設計師web前端分享想要學習前端需要學那些課程
仔細思考了一下如何回答好這個話題,其實前端是一個涵蓋面非常之廣泛的一個職位,所需知識體系非常龐雜,與傳統語言“想要精一行,必先通一
好程式設計師web前端分享如何理解JS的單執行緒
好程式設計師web前端分享如何理解JS單執行緒,JS本質是單執行緒的。也就是說,它並不能像JAVA語言那樣,兩個執行緒併發
好程式設計師web前端分享CSS元素型別
好程式設計師web前端教程分享CSS元素型別 學習目標 1、元素型別分類依據和元素型別分類 2、元素型別的轉換 3、i
好程式設計師web前端分享CSS屬性組成及作用
好程式設計師web前端分享CSS屬性組成及作用 學習目標 1、css屬性和屬性值的定義 2、css文字屬性
好程式設計師web前端分享CSS基礎篇
好程式設計師web前端分享CSS基礎篇 學習目標 1、CSS簡介 2、CSS語法 3、樣式的建立 4、兩種引
好程式設計師web前端分享HTML基本結構和基本語法
好程式設計師web前端分享HTML基本結構和基本語法 HTML基本結構和HTML基本語法 HTML基本結構 HTML的基
好程式設計師web前端分享HTML基礎篇
好程式設計師web前端分享HTML基礎篇最近遇到很多新手,都會問,如果要學web前端開發,需要學什麼?難不難學啊?多久能入門之類的
好程式設計師web前端分享CSS3彈性盒
好程式設計師web前端分享CSS3彈性盒 一、盒模型 box-sizing box-sizing 屬性允許您以特定的方式定
好程式設計師web前端帶你瞭解JS的作用域鏈
好程式設計師web前端帶你瞭解JS的作用域鏈,我們都知道js是一個基於物件的語言,系統內建各種物件。 而wind
好程式設計師web前端分享常見html5語義化標籤
好程式設計師web前端分享常見html5語義化標籤,我們知道,建立結構清晰的頁面可以建立良好的語義化基礎,也降低了使用css的難度
好程式設計師web前端分享CSS學習:HSLA顏色模式
好程式設計師web前端分享CSS學習:HSLA顏色模式 一、理論: 1.HSLA顏色模式 a.HSLA在HSL基礎上增加了不透明
好程式設計師Web前端分享一些小知識!
好程式設計師Web前端分享一些小知識!為了幫助正在從事Web前端工作和正在學習Web前端的小夥伴更好的瞭解Web前端技術,今天為大
好程式設計師web前端分享web開發概況
今天介紹一下web開發的大體概況,讓大家能夠從整體對web開發有一個相對全面的瞭解,隨著科學技術的發展,各種移動端(mobil
好程式設計師web前端分享JS引擎的執行機制
好程式設計師web前端分享JS引擎的執行機制,一、JS是單執行緒語言。JS的EventLoop是JS的執行機制。深入瞭解JS的
好程式設計師web前端分享函式作用域及遞迴
作用域的生命週期。 var a = 10; function m1(){ &nb