bind的那些事
最近面頭條的時(shí)候,要求自己手動(dòng)實(shí)現(xiàn)一個(gè)bind函數(shù),然后又問(wèn)了bind還能干嘛。這里就圍繞這兩個(gè)好好寫一下。
首先bind的文檔說(shuō)明: (鏈接:傳送門 )
bind()方法創(chuàng)建一個(gè)新的函數(shù), 當(dāng)被調(diào)用時(shí),將其this關(guān)鍵字設(shè)置為提供的值,在調(diào)用新函數(shù)時(shí),在任何提供之前提供一個(gè)給定的參數(shù)序列。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
參數(shù)
- thisArg
當(dāng)綁定函數(shù)被調(diào)用時(shí),該參數(shù)會(huì)作為原函數(shù)運(yùn)行時(shí)的 this 指向。當(dāng)使用new 操作符調(diào)用綁定函數(shù)時(shí),該參數(shù)無(wú)效。 - arg1, arg2, ...
當(dāng)綁定函數(shù)被調(diào)用時(shí),這些參數(shù)將置于實(shí)參之前傳遞給被綁定的方法。 - 返回值
返回由指定的this值和初始化參數(shù)改造的原函數(shù)拷貝
bind() 函數(shù)會(huì)創(chuàng)建(返回)一個(gè)新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)新函數(shù)被調(diào)用時(shí) this 值綁定到 bind() 的第一個(gè)參數(shù),該參數(shù)不能被重寫。綁定函數(shù)被調(diào)用時(shí),bind() 也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。(注意這句話是重點(diǎn),也就是說(shuō)bind支持了這個(gè)功能)
一般新手分不清于call,apply的關(guān)系,call/apply一般會(huì)伴隨這函數(shù)的調(diào)用的,而bind只是返回了帶新this的函數(shù),將在下次調(diào)用。一般用法是調(diào)用bind后賦給一個(gè)變量,支持調(diào)用的時(shí)候繼續(xù)傳參。
我們一般快速實(shí)現(xiàn)一個(gè)bind:
/*函數(shù)體內(nèi)的this,就是需要綁定this的實(shí)例函數(shù),或者說(shuō)是原函數(shù)。最后我們使用apply來(lái)進(jìn)行參數(shù)(context)綁定,并返回。 同時(shí),將第一個(gè)參數(shù)(context)以外的其他參數(shù),作為提供給原函數(shù)的預(yù)設(shè)參數(shù),這也是基本的柯里化基礎(chǔ)。(應(yīng)該是偏函數(shù))*/ Function.prototype.bind = Function.prototype.bind || function(context) {var self = this;arg = Array.prototype.slice.call(arguments,1);return function() {return self.apply(context,arr);} }但是這個(gè)實(shí)現(xiàn)有個(gè)問(wèn)題,我們將參數(shù)限定了arguments.slice(1),我們返回的綁定函數(shù)中,如果想實(shí)現(xiàn)預(yù)設(shè)傳參,上個(gè)代碼就不能滿足了。
eg:
那么我們將上個(gè)bind的實(shí)現(xiàn)更完善一下:
Function.prototype.bind = Function.prototype.bind || function(context) {var self = this;var args = Array.prototype.slice.call(arguments,1);return function() {var innerArgs = Array.prototype.slice.call(arguments);var FinalArgs = args.concat(innerArgs);return self.apply(context,FinalArgs);} }這樣就是實(shí)現(xiàn)了偏函數(shù)功能,在一些資料里是說(shuō)“柯里化”,我覺(jué)得還是有點(diǎn)區(qū)別的,共同特點(diǎn)是實(shí)現(xiàn)了參數(shù)復(fù)用。
關(guān)于柯里化和偏函數(shù)舉個(gè)小例子就知道了:
假設(shè)有一個(gè)Add(x,y,z)函數(shù),接收x,y,z三個(gè)參數(shù),返回x+y+z
- 偏函數(shù)
AddBySeven =Otherbind(Add, 7);
AddBySeven(5, 10); // returns 22;
這是偏函數(shù),固定了你函數(shù)的某一個(gè)或幾個(gè)參數(shù),返回一個(gè)新的函數(shù),接收剩下的參數(shù), 參數(shù)個(gè)數(shù)可能是1個(gè),也可能是2個(gè),甚至更多。
- 柯里化:把一個(gè)有n個(gè)參數(shù)的函數(shù)變成n個(gè)只有1個(gè)參數(shù)的函數(shù)
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
很多資料有的叫柯里化有的叫偏函數(shù),這點(diǎn)我覺(jué)得還是讀者自己判斷把。
到這里可能大家覺(jué)得上面的實(shí)現(xiàn)已經(jīng)完美了,但是JS的坑是補(bǔ)不完的,問(wèn)題又來(lái)了!
看過(guò)文檔的就知道,在文檔上介紹bind時(shí)還說(shuō)了這點(diǎn):
一個(gè)綁定函數(shù)也能使用new操作符創(chuàng)建對(duì)象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。提供的 this 值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)。
那么如果bind返回的函數(shù)當(dāng)做構(gòu)造函數(shù)調(diào)用的時(shí)候,我們就需要在內(nèi)部重新構(gòu)造原型鏈了。所以更兼容的寫法來(lái)了:
Function.prototype.bind = Function.prototype.bind || function(context) {if(typeof this !== 'function') {throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');}var self = this;var args = Array.prototype.slice.call(arguments,1);var Fun = {};Fun.prototype = this.prototype;//繼承原來(lái)函數(shù)var Comb = function(){var innerArgs = Array.prototype.slice.call(arguments);var FinalArgs = args.concat(innerArgs);return self.apply(this instanceof Fun ? this : context || this ,FinalArgs);}Comb.prototype = new Fun();return Comb;}這里的點(diǎn):
第一個(gè)this是第一次調(diào)用bind的函數(shù),也必須是函數(shù),所以在之前就做了一個(gè)容錯(cuò)判斷。
如果最后我們是以new 調(diào)用bind返回的函數(shù),即當(dāng)做構(gòu)造函數(shù)調(diào)用,那么這里的this就是Comb的實(shí)例,這時(shí)候因?yàn)镕un繼承了之前調(diào)用的函數(shù),Comb又new了Fun,Comb即是Fun的派生類,因此 this instanceof fNOP === true,這時(shí)候無(wú)視 bind 效果,因此 this 該是什么還是什么。模仿了原本bind的feature,如果這個(gè)條件判斷失敗,則使用context 去替換 this。如果context沒(méi)傳,那么this該是什么就是什么。
但這里再想想面試官當(dāng)時(shí)的的問(wèn)題,"bind還能干嘛",其實(shí)這個(gè)bind就是改變上下文還能干嘛,其實(shí)面試管的意思是利用bind的這個(gè)feature可以干嘛
把bind本身的作用講講,在把上面bind本身的偏函數(shù)功能(允許第一次傳參不完全,后面調(diào)用可以繼續(xù)傳參),自己實(shí)現(xiàn)的偏函數(shù),作為new 構(gòu)造函數(shù)來(lái)調(diào)用這些講講,就夠了。
最后再加一個(gè)bind的小功能把,平常我們轉(zhuǎn)換偽數(shù)組,通常是使用:
var slice = Array.prototype.slice;// ...slice.call(arguments);//arguments是一個(gè)偽數(shù)組如果我們用bind把對(duì)象參數(shù)綁定到call上返回給slice,每次就不用調(diào)用call了,而且還不影響原函數(shù)的this:
var combSlice = Array.prototype.slice; var slice = Function.prototype.call.bind(combSlice);// ...slice(arguments);//slice是一個(gè)新函數(shù)好了,bind的這些事到此結(jié)束,歡迎在下方交流評(píng)論。
轉(zhuǎn)載于:https://www.cnblogs.com/zhangmingzhao/p/8660985.html
總結(jié)
- 上一篇: [CF396E]On Iteration
- 下一篇: 《王者荣耀》技术总监复盘回炉历程:没跨过