bind的那些事

2022-01-19 16:26:34 字數 3062 閱讀 6432

最近面頭條的時候,要求自己手動實現乙個bind函式,然後又問了bind還能幹嘛。這裡就圍繞這兩個好好寫一下。

bind()方法建立乙個新的函式, 當被呼叫時,將其this關鍵字設定為提供的值,在呼叫新函式時,在任何提供之前提供乙個給定的引數序列。

fun.bind(thisarg[, arg1[, arg2[, ...]]])

引數

bind() 函式會建立(返回)乙個新函式(稱為繫結函式),新函式與被調函式(繫結函式的目標函式)具有相同的函式體(在 ecmascript 5 規範中內建的call屬性)。當新函式被呼叫時 this 值繫結到 bind() 的第乙個引數,該引數不能被重寫。繫結函式被呼叫時,bind() 也接受預設的引數提供給原函式。(注意這句話是重點,也就是說bind支援了這個功能)

我們一般快速實現乙個bind:

/*

同時,將第乙個引數(context)以外的其他引數,作為提供給原函式的預設引數,這也是基本的柯里化基礎。(應該是偏函式)

*/function.prototype.bind = function.prototype.bind || function(context)

}

但是這個實現有個問題,我們將引數限定了arguments.slice(1),我們返回的繫結函式中,如果想實現預設傳參,上個**就不能滿足了。

eg:

function sum(a, b) 

var sum2 = sum.bind(null,2); // 固定引數a, 值為2

sum2(4) // 傳入引數b, 值為4, 結果為6 重在我們在呼叫函式的時候可以預設傳參

那麼我們將上個bind的實現更完善一下:

function.prototype.bind =  function.prototype.bind || function(context) 

}

這樣就是實現了偏函式功能,在一些資料裡是說「柯里化」,我覺得還是有點區別的,共同特點是實現了引數復用。

關於柯里化和偏函式舉個小例子就知道了:

假設有乙個add(x,y,z)函式,接收x,y,z三個引數,返回x+y+z

addbyseven =otherbind(add, 7);

addbyseven(5, 10); // returns 22;

這是偏函式,固定了你函式的某乙個或幾個引數,返回乙個新的函式,接收剩下的引數, 引數個數可能是1個,也可能是2個,甚至更多。

curryadd = curry(add);

addbyseven = curryadd(7);

addbyseven(5)(10); // returns 22

// curryadd(7)(5)(10)

add = (x, y, z) => x + y + z

變成了curryadd = x => y => z => x + y + z

很多資料有的叫柯里化有的叫偏函式,這點我覺得還是讀者自己判斷把。

到這裡可能大家覺得上面的實現已經完美了,但是js的坑是補不完的,問題又來了!

看過文件的就知道,在文件上介紹bind時還說了這點:

乙個繫結函式也能使用new操作符建立物件:這種行為就像把原函式當成構造器。提供的 this 值被忽略,同時呼叫時的引數被提供給模擬函式。

那麼如果bind返回的函式當做構造函式呼叫的時候,我們就需要在內部重新構造原型鏈了。所以更相容的寫法來了:

function.prototype.bind =  function.prototype.bind || function(context) 

var self = this;

var args = array.prototype.slice.call(arguments,1);

var fun = {};

fun.prototype = this.prototype;//繼承原來函式

var comb = function()

comb.prototype = new fun();

return comb;

}

這裡的點:

第乙個this是第一次呼叫bind的函式,也必須是函式,所以在之前就做了乙個容錯判斷。

如果最後我們是以new 呼叫bind返回的函式,即當做構造函式呼叫,那麼這裡的this就是comb的例項,這時候因為fun繼承了之前呼叫的函式,comb又new了fun,comb即是fun的派生類,因此 this instanceof fnop === true,這時候無視 bind 效果,因此 this 該是什麼還是什麼。模仿了原本bind的feature,如果這個條件判斷失敗,則使用context 去替換 this。如果context沒傳,那麼this該是什麼就是什麼。

但這裡再想想面試官當時的的問題,"bind還能幹嘛",其實這個bind就是改變上下文還能幹嘛,其實面試管的意思是利用bind的這個feature可以幹嘛

把bind本身的作用講講,在把上面bind本身的偏函式功能(允許第一次傳參不完全,後面呼叫可以繼續傳參),自己實現的偏函式,作為new 建構函式來呼叫這些講講,就夠了。

最後再加乙個bind的小功能把,平常我們轉換偽陣列,通常是使用:

var slice = array.prototype.slice;

// ...

slice.call(arguments);//arguments是乙個偽陣列

如果我們用bind把物件引數繫結到call上返回給slice,每次就不用呼叫call了,而且還不影響原函式的this:

var combslice = array.prototype.slice;

var slice = function.prototype.call.bind(combslice);

// ...

slice(arguments);//slice是乙個新函式

remap的那些事

月14日 今天還在看啟動 看到target.c這裡。先說說target.c的職責。target.c檔案包含和目標初始化相關的 如remap設定 系統時鐘設定和儲存器加速模組設定等,以及irq和fiq的異常處理空函式。好吧,這裡 看到了remap就好好查資料把它搞清楚咯!其實我前面看過這個了,只是人上...

AfxWinMain的那些事

afxwinmain函式原形如下 去掉了原來的很多沒用的注釋和累贅 cpp view plain copy print?int afxapi afxwinmain afxwininit函式 建立當前應用程式主線程 initinstance函式 內部通過create 函式來完成視窗的註冊,建立更新和顯...

void 的那些事

參考 void 是一種特殊的指標型別,可用於存放任意物件的位址。舉個例子 int p 0,pi p void pv p pv pd 但是一旦某個位址放到了void pv中,就不能利用 pv進行操作了 void 中存放的是未知物件的位址,所以無法操作該指標所指向的內容,但是可以對該指標進行操作,比如賦...