JS Function.prototype 與函數(shù)的靜態(tài)屬性探討

一、背景

今天在寫一個(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。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容