本文是作者在重讀 javascript 權(quán)威指南函數(shù)調(diào)用部分的時(shí)候的一個(gè)筆記, 算下來(lái)大概一半是書上的話, 一半是自己的理解再配上一些例子來(lái)加深印象, 分享給大家。
正文從這里開始艇挨。
函數(shù)調(diào)用
構(gòu)成函數(shù)主體的 js
代碼在定義的時(shí)候是不會(huì)執(zhí)行的, 只有在調(diào)用該函數(shù)的時(shí)候它們才會(huì)執(zhí)行台谍。
一共有四種方式來(lái)調(diào)用 js
函數(shù)
- 作為函數(shù)
- 作為方法
- 作為構(gòu)造函數(shù)
- 通過(guò)它們的
call
和apply
來(lái)進(jìn)行間接調(diào)用
函數(shù)調(diào)用
使用調(diào)用表達(dá)式可以進(jìn)行普通的函數(shù)調(diào)用也可以進(jìn)行方法調(diào)用姑裂。
對(duì)于普通的函數(shù)調(diào)用, 函數(shù)的返回值就是調(diào)用表達(dá)式的值港粱。如果該函數(shù)返回是因?yàn)榻忉屍鬟_(dá)到了結(jié)尾, 返回值就是 undefined
富俄。 如果函數(shù)返回是因?yàn)榻忉屍鲌?zhí)行到一條 return
語(yǔ)句, 則返回至就是 return
會(huì)后的表達(dá)式的值, 如果 return
語(yǔ)句沒(méi)有值, 則返回 undefined
眼刃。
方法調(diào)用
一個(gè)方法無(wú)非就是個(gè)保存在一個(gè)對(duì)象的屬性里的 js
函數(shù)。 如果有一個(gè)函數(shù) func
和一個(gè)對(duì)象 obj
, 我們就可以為 obj
定義一個(gè)名為 method
的方法 :
obj.method = func;
調(diào)用的時(shí)候就類似這樣 :
obj.method();
在方法調(diào)用中, 函數(shù)表達(dá)是本身其實(shí)就是一個(gè)屬性訪問(wèn)表達(dá)式, 只不過(guò)這個(gè)屬性訪問(wèn)表達(dá)式最終取到的是函數(shù)的引用而非是一個(gè)具體的值脓诡。
對(duì)方法調(diào)用的參數(shù)和返回值的處理, 和上面描述的普通函數(shù)調(diào)用完全一致无午。
方法調(diào)用和函數(shù)調(diào)用有一個(gè)重要的區(qū)別, 就是調(diào)用上下文。在剛剛的 obj.method()
里面, 函數(shù)的 context
會(huì)變?yōu)?obj
, 所以在函數(shù)內(nèi)部可以通過(guò) this
來(lái)取到 obj
的引用祝谚。
方法和 this
關(guān)鍵字是面向?qū)ο缶幊谭独暮诵南艹佟H魏魏瘮?shù)只要作為方法調(diào)用實(shí)際上都會(huì)傳入一個(gè)隱式的實(shí)參, 這個(gè)實(shí)參實(shí)際上是一個(gè)對(duì)象, 方法調(diào)用的母體就是這個(gè)對(duì)象。
需要注意的就是, this
是一個(gè)關(guān)鍵字, 不是變量也不是屬性名, 而且 js
的語(yǔ)法也不允許給 this
賦值, 但是可以預(yù)存 this
, 比如我們?cè)诤瘮?shù)內(nèi)部經(jīng)常會(huì)這樣 :
function Promise (func) {
var resolve = function (val) {
this.resolve(val);
};
var reject = function (val) {
this.reject(val);
};
}
Promise.prototype.resolve = function () {};
Promise.prototype.reject = function () {};
接下來(lái)我們使用 var pms = new Promise(func)
來(lái)搞一個(gè)實(shí)例, 會(huì)驚奇的發(fā)現(xiàn)報(bào)錯(cuò)了.. 因?yàn)榇藭r(shí)的 this
會(huì)指向全局變量, 而全局變量上面是沒(méi)有 resolve
和 reject
方法的, 我們的本意是想要通過(guò) this
來(lái)拿到實(shí)例 pms
的引用, 進(jìn)而從 pms
上去找到 Promise.prototype.resolve
, 這個(gè)時(shí)候我們就需要把 this
的值預(yù)存一下... 就類似這種 :
function Promise (func) {
var me = this;
var resolve = function (val) {
me.resolve(val);
};
var reject = function (val) {
me.reject(val);
};
}
Promise.prototype.resolve = function () {};
Promise.prototype.reject = function () {};
這樣就可以保證在實(shí)例化的時(shí)候, resolve
函數(shù)中的那個(gè) me
指向了實(shí)例, 至于這個(gè) Promise
的實(shí)現(xiàn), 具體參考了這里 https://github.com/hanan198501/promise, 當(dāng)然這不是本文要說(shuō)的, 只是意在講預(yù)存 this
的重要性
方法鏈
其實(shí)這里就涉及到原來(lái)有看過(guò)源碼的 jQuery
的鏈?zhǔn)秸{(diào)用的核心了
當(dāng)方法不需要返回值的時(shí)候, 最好直接返回 this
, 如果在設(shè)計(jì) API
的時(shí)候一直采用這個(gè)方式, 就可以構(gòu)成一種鏈?zhǔn)秸{(diào)用交惯。
構(gòu)造函數(shù)調(diào)用
如果函數(shù)或者方法調(diào)用之前帶有關(guān)鍵字 new
, 它就構(gòu)成了構(gòu)造函數(shù)調(diào)用次泽。這里其實(shí)說(shuō)明一件事情就說(shuō)明了整個(gè)過(guò)程了。
...所以下面就說(shuō)一下 var person = new Person()
到底發(fā)生了什么...
- 創(chuàng)建一個(gè)新的空對(duì)象
- 完成內(nèi)部 [[prototype]] 的指向綁定
- 把
Person
內(nèi)部的this
全部指向新生成的對(duì)象 - 最后檢測(cè)
Person
內(nèi)部有沒(méi)有return
一個(gè)對(duì)象, 如果 return的是一個(gè)對(duì)象, 則調(diào)用表達(dá)式的值就是這個(gè)對(duì)象, 沒(méi)有
return或者
return` 的是一個(gè)原始值的話, 則調(diào)用表達(dá)式的結(jié)果就是這個(gè)新生成的對(duì)象
這里會(huì)對(duì)第二條和第三條特殊的講一下 :
首先是第二條 : 完成內(nèi)部 [[prototype]] 的綁定
其實(shí)我們打開 chrome 控制臺(tái), 敲下 {}
按回車, 點(diǎn)開生成的那個(gè)東西, 會(huì)有一個(gè) __proto__
的東西, 這個(gè)東西就是內(nèi)部 [[prototype]] 在瀏覽器里的實(shí)現(xiàn), 在 es6
里雖然沒(méi)有寫入正文, 但也寫入了附錄, 所以可以認(rèn)為是新的標(biāo)準(zhǔn), 在所有瀏覽器(包括 IE11) 也都部署了這個(gè)屬性(__proto__
的描述來(lái)自于阮一峰 ECMAScript 6 入門, 傳送門 :
http://es6.ruanyifeng.com/#docs/object#proto屬性席爽,Object-setPrototypeOf意荤,Object-getPrototypeOf)。
而第二條就是做了這一個(gè)指向, 會(huì)把實(shí)例 person
內(nèi)部的 [[prototype]] (我還是更喜歡叫 __proto__
來(lái)著...) 來(lái)指向 Person.prototype
, 其實(shí)也就是這條句子所表明的那樣 :
person.__proto__ === person.constructor.prototype;
當(dāng)然明眼人有看的出來(lái)... 其實(shí) person.constructor
就是 Person
, 當(dāng)然 constructor
不是這里要講的了, 只是用到了....
接下來(lái)是第三條 : 把 Person
內(nèi)部的 this
全部指向新生成的對(duì)象
這里的意思其實(shí)是, 比如 Person
內(nèi)部會(huì)有很多東西 :
function Person () {
this.name = 'anning';
this.age = 22;
}
在調(diào)用 var person = new Person()
的時(shí)候, 在 Person
內(nèi)部的 this
指向了新生成的對(duì)象, 也就是執(zhí)行了 :
person.name = 'anning';
person.age = 22;
這一點(diǎn)在很多地方其實(shí)都有使用, 比如 jQuery
中對(duì) jQuery.fn.init
構(gòu)造函數(shù)的實(shí)現(xiàn), 就是在 init
函數(shù)里面大量使用了 this
, 在 this
上掛非繼承屬性, 最后在生成 jQuery.fn.init
的實(shí)例的時(shí)候, 使實(shí)例的非繼承屬性, 如 context
屬性, 整型屬性 [0]
等掛在了每個(gè)實(shí)例上只锻。
間接調(diào)用
間接調(diào)用中其實(shí)就只有兩個(gè)方法, call
和 apply
玖像。
兩個(gè)方法都允許顯式的指定 this
的取值, 也就是說(shuō), 所有的函數(shù)都可以作為任何對(duì)象的方法阿萊調(diào)用, 哪怕這個(gè)函數(shù)不是那個(gè)對(duì)象的方法。
我們?cè)诳吹揭恍?kù)的 this
非常強(qiáng)大的時(shí)候, 可能就是在實(shí)現(xiàn)的時(shí)候, 用 call
和 apply
強(qiáng)行指定的...
原文地址 : http://www.amnhh.xyz/2017/02/07/%E6%B5%85%E8%B0%88%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8/