一、背景
今天在寫一個(gè)柯里化函數(shù)的時(shí)候,不小心遇到了一個(gè)錯(cuò)誤,導(dǎo)致無法輸出正確的結(jié)果,代碼如下:
function curry(...args) {
function _adder(...args2) {
args.push(...args2);
return _adder;
}
_adder.prototype.toString = function() {
return args.reduce((a, b) => a + b);
}
return _adder;
}
這個(gè)函數(shù)在使用的時(shí)候只需要這樣子調(diào)用:
curry(1, 2, 3, 4, 5, 6)(7)(8).toString()
實(shí)際上安設(shè)想,這樣就會(huì)打印出正確的結(jié)果,但是卻沒有,打印結(jié)果如下:
"function _adder(...args2) {
args.push(...args2);
return _adder;
}"
他把函數(shù)體打印了出來,這是為什么呢?
二、探究
原來我在函數(shù)體里把_adder的toString方法綁到了他的原型上,這樣只有他的實(shí)例能調(diào)用,或者自身通過以下方式調(diào)用:
curry(1, 2, 3, 4, 5, 6)(7)(8).prototype.toString()
36
那么上述的柯里化函數(shù)應(yīng)當(dāng)改寫成如下形式:
function curry(...args) {
function _adder(...args2) {
args.push(...args2);
return _adder;
}
_adder.toString = function() {
return args.reduce((a, b) => a + b);
}
return _adder;
}
也就是給函數(shù)添加靜態(tài)方法toString,這樣通過顯示調(diào)用toString或者隱式類型轉(zhuǎn)換都可以計(jì)算柯里化的值。
但我今天要探討的重點(diǎn)不在這里,而是Fuction.prototype與普通函數(shù)的關(guān)系。因?yàn)楹瘮?shù)也是Object的實(shí)例,這兩個(gè)東西高度抽象,互為實(shí)例。當(dāng)我們通過類名直接在函數(shù)上添加屬性或者方法的時(shí)候,這個(gè)屬性就稱之為類屬性,只能函數(shù)本身調(diào)用,new 出來的對(duì)象不能調(diào)用。
今天我發(fā)現(xiàn)所有的函數(shù)都是Function的實(shí)例:
function test() {}
undefined
test instanceof Function
true
test.__proto__
? () { [native code] }
Function.prototype
? () { [native code] }
另附instanceof內(nèi)部原理:
function instance_of(L, R) {//L 表示左表達(dá)式,R 表示右表達(dá)式
var O = R.prototype; // 取 R 的顯示原型
L = L.__proto__; // 取 L 的隱式原型
while (true) {
if (L === null)
return false;
if (O === L) // 當(dāng) O 顯式原型 嚴(yán)格等于 L隱式原型 時(shí),返回true
return true;
L = L.__proto__;
}
}
其實(shí)這和函數(shù)的實(shí)現(xiàn)原理有關(guān),當(dāng)我們通過如下方式聲明函數(shù)時(shí):
let f = new Function();
實(shí)際上就構(gòu)造了一個(gè)函數(shù)出來,并且該函數(shù)是可執(zhí)行的,只不過沒有函數(shù)參數(shù)和函數(shù)體而已,我們可以改寫為如下形式:
var func = new Function('arg1', 'arg2', 'return arg1 + arg2');
undefined
func(1, 2)
3
// 實(shí)際上等同于
function func(arg1, arg2) {
return arg1 + arg2;
}
注意:盡管可以使用 Function 構(gòu)造函數(shù)創(chuàng)建函數(shù),但最好不要使用它,因?yàn)橛盟x函數(shù)比用傳統(tǒng)方式要慢得多。不過,所有函數(shù)都應(yīng)看作 Function 類的實(shí)例。
這就很好解釋了,因?yàn)楹瘮?shù)進(jìn)行了new操作,而new操作的內(nèi)部原理如下:
function new(fn) {
let target = {};
target.__proto__ = fn.prototype;
let res = fn.call(target);
if(typeof res == 'object' || typeof res == 'function') {
return res;
}
return target;
}
三、總結(jié)
說了這么多,F(xiàn)unction.prototype就是所有function的原型,當(dāng)我們通過屬性描述符'.'或者中括號(hào)的形式去獲取函數(shù)的靜態(tài)屬性的時(shí)候,其實(shí)首先看的是自身有沒有該屬性,沒有再去function.prototype上去找,再找不到就是Object.prototype了,原型鏈的終端,因?yàn)?,prototype就是個(gè)對(duì)象既然是對(duì)象,那么就是Object的實(shí)例。
如果我們需要給所有函數(shù)定義公共方法,那么可以考慮Function.prototype。