JavaScript實(shí)現(xiàn)手寫call/apply/bind的示例代碼
還記得之前面試得物的時(shí)候,上來就是一道手寫bind
,當(dāng)時(shí)咱也不知道啥情況,也沒準(zhǔn)備什么手寫的題目,就這樣輕輕松松的掛了
現(xiàn)在前端行業(yè)那么的卷,面試的時(shí)候讓你手寫的個(gè)什么東西是非常常見的。下面是我總結(jié)的3道手寫題。希望對你有幫助。
call
call的作用是啥
我們首先看一個(gè)案例
let foo = { value: 1 } function bar() { console.log(this.value) } bar.call(foo) //1
可以總結(jié)兩個(gè)點(diǎn):
- call改變了
bar
的this指向,指向了foo
- bar被執(zhí)行了 那我們是不是可以理解為是這樣的情況呢
let foo = { value: 1, bar: function() { console.log(this.value) } } foo.bar()
我們可以看到這個(gè)時(shí)候this就指向了foo
,但是多了一個(gè)屬性,那再把這個(gè)屬性刪掉就是咯。所以我們的思路可以是這樣的:
- 將函數(shù)設(shè)置成foo的屬性
- 執(zhí)行這個(gè)函數(shù)
- 刪除這個(gè)函數(shù) 暫時(shí)可以先寫成這樣
Function.prototype.myCall = function (context) { context.fn = this context.fn() delete context.fn }
現(xiàn)在我們再回到最初的案例,然后加上參數(shù)
let foo = { value: 1 } function bar(name, age) { console.log(this.value) console.log(name) console.log(age) } bar.call(foo, "mick", 18) // 1 // mick // 18
那我們就可以把call去除第一個(gè)參數(shù),然后剩下的參數(shù)在執(zhí)行的時(shí)候添加進(jìn)去就好了
Function.prototype.myCall = function (context) { context.fn = this const args = [...arguments].slice(1) context.fn(...args) delete context.fn }
我們修改下案例
var value = 1 function bar() { console.log(this.value) } bar.call(null) // 1
當(dāng)綁定的this指向?yàn)閚ull的時(shí)候,則認(rèn)識(shí)指向了window
let foo = { value: 1 } function bar(name, age) { return { value: this.value, name, age } } console.log(bar.call(foo, "mick", 18)) // { value: 1, name: 'mick', age: 18 }
如果函數(shù)有返回值,我們實(shí)現(xiàn)的call不能僅僅是執(zhí)行了,也要有返回值。
Function.prototype.myCall = function (context) { context = context || window context.fn = this const args = [...arguments].slice(1) const res = context.fn(...args) delete context.fn return res }
這樣就實(shí)現(xiàn)了一個(gè)call
總結(jié)
- 將函數(shù)設(shè)置成要指向的那個(gè)this的屬性
- 執(zhí)行這個(gè)函數(shù)
- 刪除這個(gè)屬性
- 考慮參數(shù)問題
- 考慮this為null的情況
- 考慮下返回值
apply
apply和call差不多,只是入?yún)⒉灰粯?,apply的參數(shù)是數(shù)組
Function.prototype.myApply = function (context, arr) { context = context || window context.fn = this var res if (!arr) { res = context.fn() } else { res = context.fn(...arr) } delete context.fn return res }
bind
MDN上解釋的bind為:bind()
方法創(chuàng)建一個(gè)新的函數(shù),在 bind()
被調(diào)用時(shí),這個(gè)新函數(shù)的 this
被指定為 bind()
的第一個(gè)參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調(diào)用時(shí)使用。 我們可以得出兩個(gè)點(diǎn):
- 返回一個(gè)新的函數(shù)
- 可以傳入?yún)?shù)
先看下案例:
var foo = { value: 1 } function bar(name, age) { console.log(this.value) console.log(name) console.log(age) } var bindFoo = bar.bind(foo, "mick") bindFoo(18) // 1 // mick // 18
可以看到 bind的參數(shù)和返回的bindFoo的參數(shù)是合并的,而改變this可以利用apply來實(shí)現(xiàn)
Function.prototype.myBind = function (context) { const self = this const args = [...arguments].slice(1) return function () { const bindArgs = [...arguments].slice() return self.apply(context, args.concat(bindArgs)) } }
然而bind還有一個(gè)特點(diǎn)。在MDN上這樣說到:綁定函數(shù)自動(dòng)適應(yīng)于使用new操作符去構(gòu)造一個(gè)由目標(biāo)函數(shù)創(chuàng)建的新實(shí)例。當(dāng)一個(gè)綁定函數(shù)是用來構(gòu)建一個(gè)值的,原來提供的 this
就會(huì)被忽略。不過提供的參數(shù)列表仍然會(huì)插入到構(gòu)造函數(shù)調(diào)用時(shí)的參數(shù)列表之前。
所以當(dāng)執(zhí)行bind被返回的那個(gè)函數(shù)被當(dāng)做構(gòu)造函數(shù)的時(shí)候,bind綁定的this值就會(huì)失效。 我們看下案例:
var value = 2 var foo = { value: 1 } function bar(name, age) { this.hobby = "studying" console.log(this.value) console.log(name) console.log(age) } bar.prototype.friend = "randy" var bindFoo = bar.bind(foo, "mick") var obj = new bindFoo(18) // undefined // mick // 18 console.log(obj.hobby) console.log(obj.friend) // studying // randy
可以看出this失效了,所以我們要完善一下
Function.prototype.myBind = function (context) { const self = this const args = [...arguments].slice(1) var fBound = function () { const bindArgs = [...arguments].slice() // 用apply 實(shí)現(xiàn)this的綁定 return self.apply( this instanceof fBound ? this : context, args.concat(bindArgs) ) } fBound.prototype = this.prototype return fBound }
首先加了this instanceof fBound
這個(gè)主要是為了判斷fBound是不是被當(dāng)做構(gòu)造函數(shù)使用的,如果是,那么將綁定函數(shù)的this指向該實(shí)例。fBound.prototype = this.prototype
修改fBound的prototype是為了綁定函數(shù)的prototype,實(shí)例就可以繼承綁定函數(shù)原型中的值了。這也是為什么new bindFoo
的實(shí)例能夠訪問bar原型的屬性。
優(yōu)化
fBound.prototype = this.prototype
,當(dāng)我直接修改fBound的prototype的時(shí)候,也會(huì)直接修改綁定函數(shù)bar的prototype。這時(shí)候我們就需要一個(gè)空函數(shù)來中轉(zhuǎn):
Function.prototype.myBind = function (context) { const self = this const args = [...arguments].slice(1) var fNOP = function () {} var fBound = function () { const bindArgs = [...arguments].slice() // 用apply 實(shí)現(xiàn)this的綁定 return self.apply( this instanceof fNOP ? this : context, args.concat(bindArgs) ) } fNOP.prototype = this.prototype fBound.prototype = new fNOP() return fBound }
總結(jié)
- 執(zhí)行bind返回一個(gè)新的函數(shù)
- bind的參數(shù)和返回新的函數(shù)的參數(shù)會(huì)拼接,bind的參數(shù)優(yōu)先級更高
- 如果返回的函數(shù)當(dāng)做構(gòu)造函數(shù)使用的時(shí)候,this會(huì)失效
- 修改原型的值需要一個(gè)中轉(zhuǎn)優(yōu)化
以上就是JavaScript實(shí)現(xiàn)手寫call/apply/bind的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于JavaScript call apply bind的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS實(shí)現(xiàn)定時(shí)頁面彈出類似QQ新聞的提示框
類似QQ新聞的提示框要求頁面每隔半小時(shí)彈出一次提示消息,下面有個(gè)不錯(cuò)的實(shí)現(xiàn)方法,感興趣的朋友可以參考下2013-11-11uniapp內(nèi)置組件scroll-view案例詳解(完整代碼)
這篇文章主要介紹了uniapp內(nèi)置組件scroll-view案例詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-07-07underscore之function_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
因?yàn)閡nderscore本來就是為了充分發(fā)揮JavaScript的函數(shù)式編程特性,所以也提供了大量JavaScript本身沒有的高階函數(shù)。本文重點(diǎn)給大家介紹underscore之function知識(shí),感興趣的的朋友一起學(xué)習(xí)吧2017-07-07JavaScript實(shí)現(xiàn)全選與反選功能
這篇文章主要為大家詳細(xì)介紹了如何分別使用Vue和JavaScript實(shí)現(xiàn)全選與反選功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04