1、实现call()

js提供的call方法可以实现函数内this指向的改变,我们可以看下面这个例子

1
2
3
4
5
6
7
8
const obj={
a:'1'
}
function fn(){
console.log(this.a)
}
fn() //undefined
fn.call(obj) //1,指向obj

可以看到使用call我们可以将函数中的this强制指向obj对象。
下面我们来自己实现这个函数,我们都知道函数fn如果直接执行,则this是指向window的,而通过对象来调用一个函数,那么这个函数的this是指向这个对象的,那么我们就可以对上面的obj进行改造,将函数赋给obj,然后通过obj来调用这个函数即可

1
2
3
4
5
6
7
const obj={
a:'1',
fn:function(){
console.log(this.a)
}
}
obj.fn() //1

那么上面两个结合我们就可以写成下面这三部,先将函数给对象再通过对象调用,然后在删去这个函数即可

1
2
3
obj.fn=fn
obj.fn()
delete obj.fn

实现:

1
2
3
4
5
6
7
Function.prototype.mycall=function(context=window){
const obj=context||window //如果没有传参,默认指向window
obj.fn=this
const result=obj.fn()
delete obj.fn()
return result
}

当然我们的call还是可以给函数传参的,我们可以改写成下面这个样子

1
2
3
4
5
6
7
Function.prototype.mycall=function(context=window,...args){
const obj=context||window //如果没有传参,默认指向window
obj.fn=this
const result=obj.fn(...args)
delete obj.fn
return result
}

这样就大功完成啦下面就可以来测试啦

1
2
3
4
5
6
7
8
9
const obj={
a:'2'
}
function fn(name,age){
console.log(name)
console.log(age)
console.log(this.a)
}
fn.mycall(obj,'name',12) // 'name',12,2

2、实现apply()

apply的实现和call的实现类似,只不过一个传递的参数形式不同而已

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.myapply=function(context=window,args){
let result;
const obj=context||window
obj.fn=this
if(Array.isArray(args)){
result=obj.fn(...args)
}else{
result=obj.fn()
}
delete obj.fn
return result
}

测试就跟上面的call一样的啦 这边就不贴了。

3、实现bind

bind的作用是返回一个新的函数,且这个函数的this指向已被改变,我们可以通过call或者是apply来完成,通过闭包返回一个函数来实现

1
2
3
4
5
6
Function.prototype.mybind=function(context=window,...args){
const that=this
return Function F(...args2){
return that.apply(context,args.concat(args2))
}
}

由于bind后产生的函数除了可以直接调用还会用作构造函数使用,所以我们在这里加个对他的判断,如果是构造函数,则直接返回实例对象。

1
2
3
4
5
6
7
8
9
10
Function.prototype.mybind=function(context=window,...args){
const that=this
const obj=context||window
return Function F(...args2){
if(this instanceof F){
return new that(...args,...args2)
}
return that.apply(obj,args.concat(args2))
}
}

我们加一层判断就可以啦,如果是构造函数则直接用原本的函数进行new就行啦