call
逐步实现,最后得出最终版
先熟悉一下call的功能及使用
需求:想让函数打印出user 的name,即想让函数中的this指向user(修改this指向)
const user = {
name: "zhangsan"
}
function sayYourName(title) {
console.log(title,this.name)
}
// 使用js内置的api call方法
sayYourName.call(user,'hello') //函数立即调用,并打印
// 或者利用对象属性 直接调用
const user1 = {
name: "zhangsan",
sayYourName:function(title) {
console.log(title,this.name)
}
}
user1.sayYourName('hello')
根据上面的思路,我们自己一个封装一个call方法
function myCall(fn,context,...args){ // ES6剩余参数
context.fn = fn
const res = context.fn(...args)//扩展运算符
delete context.fn
return res
}
myCall(sayYourName,user,'hello')
// 优化:如果context 里面本身就有一个fn的属性,给它重新赋值,然乎又删掉,明显不合理,所以要保证利用的属性名的唯一性
function myCall(fn,context,...args){ // ES6剩余参数
const fnName = Symbol()
context[fnName]=fn
const res = context[fnName ](...args)//扩展运算符
delete context[fnName ]
return res
}
myCall(sayYourName,user,'hello')
总结:
- 先把函数赋值给目标的的属性
- 利用目标对象对象 调用函数
- 删除目标对象的属性
- 将函数结果返回
但是仍然封装好的myCall方法并不能采用 函数.myCall的形式调用
js内置的call方法 采用 函数.call的形式调用,但是函数本身并没有call方法,js就会向函数的原型链上查找,其实call方法就是存在与 Function.prototype上的一个属性。
所以我们进行下一步:把我们的myCall方法也挂载到函数原型上
Function.prototype.myCall = function(fn,context,...args){
const fnName = Symbol()
context[fnName]=fn
const res = context[fnName ](...args)//扩展运算符
delete context[fnName ]
return res
}
//使用:
sayYourName.myCall(sayYourName,user,'hello')
- 我们不难发现 myCall方法中的this是调用它的对象,即sayYourName,所以实参中的sayYourName可以用this代替
- 同时我们的函数形参也要替换成this,如下:call最终版
Function.prototype.myCall = function(context,...args){
const fnName = Symbol()
context[fnName]=this
const res = context[fnName ](...args)//扩展运算符
delete context[fnName ]
return res
}
//使用:
sayYourName.myCall(user,'hello')
apply
基本与myCall实现一样,只是apply的传参形式为数组
Function.prototype.myApply = function(context,args){ //只需要把...args 去掉...,直接传递数组过去
const fnName = Symbol()
context[fnName]=this
const res = context[fnName ](...args)//扩展运算符
delete context[fnName ]
return res
}
//使用:
sayYourName.myApply(user,['hello'])
bind
- bind 方法将创建并返回一个新的函数,新函数称为绑定函数,并且绑定函数包裹着原始函数
- 在执行时,会显示的将原始函数内部的this指向context
- 除context外,所有剩余参数将全部传递给原始函数function
- 执行绑定函数时,如果传递了参数,这些参数将全部传递给原始函数 function
- 如果使用了new运算符调用生成的绑定函数,则忽略context