首先从以下几点来考虑如何实现这几个函数
- 不传入第一个参数,那么上下文默认为
window - 改变了
this指向,让新的对象可以执行该函数,并能接受参数
实现call
- 首先
context为可选参数,如果不传的话默认上下文为window - 接下来给
context创建一个fn属性,并将值设置为需要调用的函数 - 因为
call可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来 - 然后调用函数并将对象上的函数删除
Function.prototype.call=function(context=window,...args){
if(typeof this!=='function') {
throw new Error('this must be function')
}
const key=Symbol('key')
context[key]=this
//执行function
const result=context[key](...args)
// 需要删除key,不然每次调用都会在context新增一个绑定的key
delete context[key]
return result
}
实现apply
- apply和call类似区别在于参数不同
Function.prototype.apply=function(context=window,args){
if(typeof this!=='function'){
throw new Error('this must be function')
}
const key =Symbol('key')
context[key]=this
//执行function
const result=context[key](...args)
// 需要删除key,不然每次调用都会在context新增一个绑定的key
delete context[key]
return result
}
bind的实现对比其他两个函数略微地复杂了一点,因为bind需要返回一个函数,需要判断一些边界问题,以下是bind的实现
bind返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过new的方式,我们先来说直接调用的方式- 对于直接调用来说,这里选择了
apply的方式实现,但是对于参数需要注意以下情况:因为bind可以实现类似这样的代码f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现args.concat(...arguments) - 最后来说通过
new的方式,对于new的情况来说,不会被任何方式改变this,所以对于这种情况我们需要忽略传入的this
Function.prototype.bind=function(context,...outerArgs){
if(typeof this!=='function'){
throw new Error('this must be function')
}
const self=this
// 函数柯里化
return function Fn(...innerArgs){
// 函数bind之后 直接通过new的方式来调用
if(self instanceof Fn){
return new self(...outerArgs,...innerArgs)
}
return self.apply(context,[...outerArgs,...innerArgs])
}
}
// new的测试用例
// 普通函数
function test() {
// new 的方式调用 bind 参数输出换做 [...arguments]
console.log(this.name);
}
// 自定义对象
var obj = { name: 'PJ' }
// 调用函数的 call 方法
let F = test._bind(obj, 1, 2, 3);
// 返回对象
let obj1 = new F();