JavaScript實現(xiàn)手寫call/apply/bind的示例代碼
還記得之前面試得物的時候,上來就是一道手寫bind,當時咱也不知道啥情況,也沒準備什么手寫的題目,就這樣輕輕松松的掛了
現(xiàn)在前端行業(yè)那么的卷,面試的時候讓你手寫的個什么東西是非常常見的。下面是我總結的3道手寫題。希望對你有幫助。
call
call的作用是啥
我們首先看一個案例
let foo = {
value: 1
}
function bar() {
console.log(this.value)
}
bar.call(foo) //1可以總結兩個點:
- call改變了
bar的this指向,指向了foo - bar被執(zhí)行了 那我們是不是可以理解為是這樣的情況呢
let foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar()我們可以看到這個時候this就指向了foo,但是多了一個屬性,那再把這個屬性刪掉就是咯。所以我們的思路可以是這樣的:
- 將函數(shù)設置成foo的屬性
- 執(zhí)行這個函數(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去除第一個參數(shù),然后剩下的參數(shù)在執(zhí)行的時候添加進去就好了
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
當綁定的this指向為null的時候,則認識指向了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ù)有返回值,我們實現(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
}這樣就實現(xiàn)了一個call
總結
- 將函數(shù)設置成要指向的那個this的屬性
- 執(zhí)行這個函數(shù)
- 刪除這個屬性
- 考慮參數(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)建一個新的函數(shù),在 bind() 被調用時,這個新函數(shù)的 this 被指定為 bind() 的第一個參數(shù),而其余參數(shù)將作為新函數(shù)的參數(shù),供調用時使用。 我們可以得出兩個點:
- 返回一個新的函數(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來實現(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還有一個特點。在MDN上這樣說到:綁定函數(shù)自動適應于使用new操作符去構造一個由目標函數(shù)創(chuàng)建的新實例。當一個綁定函數(shù)是用來構建一個值的,原來提供的 this 就會被忽略。不過提供的參數(shù)列表仍然會插入到構造函數(shù)調用時的參數(shù)列表之前。
所以當執(zhí)行bind被返回的那個函數(shù)被當做構造函數(shù)的時候,bind綁定的this值就會失效。 我們看下案例:
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 實現(xiàn)this的綁定
return self.apply(
this instanceof fBound ? this : context,
args.concat(bindArgs)
)
}
fBound.prototype = this.prototype
return fBound
}首先加了this instanceof fBound 這個主要是為了判斷fBound是不是被當做構造函數(shù)使用的,如果是,那么將綁定函數(shù)的this指向該實例。fBound.prototype = this.prototype修改fBound的prototype是為了綁定函數(shù)的prototype,實例就可以繼承綁定函數(shù)原型中的值了。這也是為什么new bindFoo的實例能夠訪問bar原型的屬性。
優(yōu)化
fBound.prototype = this.prototype,當我直接修改fBound的prototype的時候,也會直接修改綁定函數(shù)bar的prototype。這時候我們就需要一個空函數(shù)來中轉:
Function.prototype.myBind = function (context) {
const self = this
const args = [...arguments].slice(1)
var fNOP = function () {}
var fBound = function () {
const bindArgs = [...arguments].slice()
// 用apply 實現(xiàn)this的綁定
return self.apply(
this instanceof fNOP ? this : context,
args.concat(bindArgs)
)
}
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound
}總結
- 執(zhí)行bind返回一個新的函數(shù)
- bind的參數(shù)和返回新的函數(shù)的參數(shù)會拼接,bind的參數(shù)優(yōu)先級更高
- 如果返回的函數(shù)當做構造函數(shù)使用的時候,this會失效
- 修改原型的值需要一個中轉優(yōu)化
以上就是JavaScript實現(xiàn)手寫call/apply/bind的示例代碼的詳細內(nèi)容,更多關于JavaScript call apply bind的資料請關注腳本之家其它相關文章!
相關文章
uniapp內(nèi)置組件scroll-view案例詳解(完整代碼)
這篇文章主要介紹了uniapp內(nèi)置組件scroll-view案例詳解,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-07-07
underscore之function_動力節(jié)點Java學院整理
因為underscore本來就是為了充分發(fā)揮JavaScript的函數(shù)式編程特性,所以也提供了大量JavaScript本身沒有的高階函數(shù)。本文重點給大家介紹underscore之function知識,感興趣的的朋友一起學習吧2017-07-07

