我们都知道call与apply能实现this指向的改变,而且该函数执行时立刻返回结果。
let Person = {
name: 'Tom',
say() {
console.log(this.name)
}
}
let person1= {
name: 'Tom1'
}
Person.say.call(person1) //Tom1
想让person1执行 Perosn里的say方法。如果person1这个对象本身也有一个say方法,那么我们只需要通过person1.say()执行就可以里,我们也知道person1本身是没有say方法的,那么我们给他加一个不就行了吗? person.say()。这里我们会有一个疑问?不可能每次都加一个固定的say方法吧?这不合理,我哪知道到底是哪个方法?既然我们不能加固定的,那么我们可以这样
person1.fn=xxxx //这个xxx就是say方法。
我们仔细分析Person.say.call(person1)。对于我们的call函数而言。call函数内部this指向的是谁?还是那句话:谁调用它,this就指向谁。Person.say.call()======》是Person.say调用里call()很明显,call内部的this应该指向的是Person.say,所以我们可以初步写出下面的代码:
//Person.say.call(person1)
Person.say.myCall(person1) //Person.say调用mycall,那mycall内部的this就是Person.say
Function.prototye.myCall=function(context,...args){
//context指的就是person1
//this指向的是Person.say
context.fn=this
context.fn(...args)
context.delete(fn)
}
上一步我们说过我们不可能每次都加一个固定的say方法。而我们需要执行这个say方法,而刚好我们知道mycall内部this指向的就是person.say()。那不刚好吗?
我们执行context.fn(..args)同时把参数给传进去,不就相当于给person添加里一个say方法同时执行它。但是我们知道我们给person1添加里一个静态方法,而这个方法其实和person1本身是解耦的,所以我们在办完事以后还的把它删除。
到这里已经基本实现了。但是还有一些问他需要我们思考:
1 如果我们的mycall(context,args)中context不传会怎样?
2 如果我们的mycall(context,args)中传的是一个基本数据类型怎么办?
其实对于问题1很好解决:
如果我们的mycall(context,args)中context不传或者为null,我们默认它为window
而对于问题2
如果我们的mycall(context,args)中传的是一个基本数据类型,我们把基本数据类型转化为其构造函数的对象。比如字符串,它的构造函数就是String,比如number,它的构造函数就是Number。
所以最后的代码我们可以做如下完善
Function.prototype.myCall=function(context,...args){
let context=context===undefined?window:context;
let type=typeof context;
if(!/^(object|function)$/).test(context){ //是否是个对象,context如果为基本数据类型无法添加fn属性
if(/^(symbol|bigINT)$/.test(type))){
context=Object(context)
}else{
context=new context.constructot(context) //如果是普通类型就直接通过其构造函数包装成一个对象类型
}
}
let key=Symbole("key");
context.key=this
let result=context.key(..args)
delete context.key
return result
}