使用模擬實(shí)現(xiàn)的方式探究call 和 apply 的原理
call
作用:
call() 方法就是在使用一個(gè)指定 this 值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或者方法。
var foo = {
value : 1
}
function bar() {
console.log(this.value)
}
// 如果不對(duì)this進(jìn)行綁定執(zhí)行bar() 會(huì)返回undefined
bar.call(foo) // 1
也就是說(shuō)call()改變了 this 的指向到了 foo 。
下面進(jìn)行一下模擬實(shí)現(xiàn)
試想當(dāng)調(diào)用 call 的時(shí)候,也就是類似于
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
}
foo.bar() // 1
這樣就把 this 指向到了 foo 上,但是這樣給 foo 對(duì)象加了一個(gè)屬性,有些瑕疵,不過(guò)不要緊,執(zhí)行完刪除這個(gè)屬性就可以完美實(shí)現(xiàn)了。
也就是說(shuō)步驟可以是這樣:
- 將函數(shù)設(shè)為對(duì)象的屬性
- 執(zhí)行這個(gè)函數(shù)
- 刪除這個(gè)函數(shù)
下面就試著去實(shí)現(xiàn)一下:
Function.prototype.call2 = function(context) {
context.fn = this // this 也就是調(diào)用call的函數(shù)
var result = context.fn()
delete context.fn()
return result
}
var foo = {
value: 1
}
function bar() {
console.log(this.value)
}
bar.call2(foo) // 1
但是這樣有一個(gè)小缺陷就是call() 不僅能指定this到函數(shù),還能傳入給定參數(shù)執(zhí)行函數(shù)比如:
var foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
bar.call(foo, 'black', '18')
// black
// 18
// 1
特別要注意的一點(diǎn)是,傳入的參數(shù)的數(shù)量是不確定的,所以我們要使用arguments 對(duì)象,取出除去第一個(gè)之外的參數(shù),放到一個(gè)數(shù)組里:
Function.prototype.call2 = function(context) {
context.fn = this // this 也就是調(diào)用call的函數(shù)
var args = [...arguments].slice(1)
var result = context.fn(...args)
delete context.fn()
return result
}
var foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'black', '18') // black 18 1
還有一點(diǎn)需要注意的是,如果不傳入?yún)?shù),默認(rèn)指向?yàn)?window,所以最終版代碼:
Function.prototype.call2 = function(context) {
var context = context || window
context.fn = this // this 也就是調(diào)用call的函數(shù)
var args = [...arguments].slice(1)
var result = context.fn(...args)
delete context.fn()
return result
}
var value = 1
function bar() {
console.log(this.value)
}
bar.call2()
apply
apply的方法和 call 方法的實(shí)現(xiàn)類似,只不過(guò)是如果有參數(shù),以數(shù)組形式進(jìn)行傳遞,直接上代碼:
Function.prototype.apply2 = function(context) {
var context = context || window
context.fn = this // this 也就是調(diào)用apply的函數(shù)
var result
// 判斷是否有第二個(gè)參數(shù)
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn()
return result
}
var foo = {
value: 1
}
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.apply2(foo, ['black', '18']) // black 18 1