模拟 call、apply、bind 方法
模拟 call 方法
关于
call
方法:
- 某个方法/函数中的
this
总是指向调用该方法的对象 - 直接调用函数,则函数中的
this
指向的是 window - 使用函数原型上的
call
方法可以修改this
的指向为指定对象
思路:
- 首选需要明确:实现自定义的
newCall
方法,需要将该方法定义在函数原型上 - 我们有一个普通的函数
person
,现在需要实现的是修改person
的this
指向到指定对象,如foo
上 - 将 person 存储为指定对象 foo 的临时方法
- 通过
foo.person
调用该方法的时候,this
指向的当然就是 foo 对象了 - 临时方法调用完后删除
Function.prototype.newCall = function (context) {
var context = context || window // 若 context 是 null,最后 this 指向 window
// this 指向的是调用 newCall 函数的对象,也就是 person 函数
context.fn = this // 给这个上下文对象添加一个 fn 属性指向调用的函数
// arguments 为伪数组,需要转换成数组才能使用数组方法
var args = [...arguments].slice(1)
var res = context.fn(...args)
// 使用完删除 fn 这个属性
delete context.fn
return res
}
测试:
function person(name, age) {
console.log(name, age)
console.log(this.value)
}
var foo = { value: 100 }
person.newCall(foo, 'Danny', 18)
模拟 apply 方法
关于
apply
方法
- 使用函数原型上的
apply
方法可以修改this
的指向为指定对象 - 区别于
call
的地方在于,apply
传入的参数为数组 - 实现思路跟 call 基本一致
Function.prototype.newApply = function(context, arr) {
// 将函数绑定到上下文对象中执行
var context = context || window
context.fn = this
var res
if (!arr) { // 没有传入任何参数, arr 为 undefined
res = context.fn()
} else {
res = context.fn(...arr)
}
// 执行完后删除在对象添加的 fn 属性
delete context.fn
return res
}
测试:
function person(name, age) {
console.log(name, age)
console.log(this.value)
}
var foo = { value: 100 }
person.newApply(foo, ['Danny', 18])
模拟 bind 方法
关于
bind
方法
bind
方法区别于call
的地方在于,调用bind
返回的是一个函数- 要实现
call
的效果,需要在返回的函数基础上再进行一次调用 - 可以借助
apply
来实现bind
方法
Function.prototype.newBind = function () {
// 拿到所有参数,将伪数组转为数组
const args = [...arguments]
// 获取 context
const context = args.shift()
// 获取 fn,即待执行函数 fn.bind(...) 中的 fn
const fn = this
return function () {
return fn.apply(context, args)
}
}
测试:
function person(name, age) {
console.log(name, age)
console.log(this.value)
}
var foo = { value: 100 }
var fn = person.newBind(foo, 'Danny', 18)
fn()