「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」
call,bind,apply是改变this指向的法宝。
三者的区别是:
call:第一个参数是this,后面的传入的一序列值,立即调用。 apply :第一个参数是this,第二个参数是数组,-立即调用。
bind:第一个参数是this,接收参数可以分次传入,使用了柯里化,返回的是return 的值,需要手动调用执行。
使用方法:
在需要改变this执行的函数后面使用call,bind,apply:
上例子:
let obj1 = {
from:'obj1:',
sayHello(...name){
console.log(this.from+name)
}
}
let obj2 = {
from :'obj2:',
sayHello(...name){
console.log(this.from+name)
}
}
console.log(obj2.sayHello('苹果','香蕉'))
console.log(obj2.sayHello.call(obj1,'苹果','香蕉'))
console.log(obj2.sayHello.apply(obj1,['苹果','香蕉']))
let coo = obj2.sayHello.bind(obj1)
console.log(coo('苹果','香蕉'))
接下来实现一下call,bind,apply是怎么实现的?
首先实现call:
Function.prototype.myCall=function(context){
// 获取待指向的函数,没有默认window
let ctx = context||window;
// 赋值当前this //此时的this是需要改变this的函数
ctx.fn = this;
// 获取参数,把类数组转换为数组,去掉第一个参数
let arg = [...arguments].slice(1)
// 存一份调用的结果
let res = ctx.fn(...arg)
// 删除绑定的函数
delete ctx.fn(...arg)
// 返回结果
return res
}
obj2.sayHello.myCall(obj1,'苹果','香蕉') //obj1:苹果,香蕉
实现apply :
apply 跟call只是传递的参数不一样,
Function.prototype.myApply=function(context,arg){
//定义上下文
const ctx = context || window
// 绑定函数
ctx.fn = this
let res = ctx.fn(...arg)
delete ctx.fn
return res
}
obj2.sayHello.myApply(obj1,['苹果','香蕉']) //obj1:苹果,香蕉
实现bind
先实现一个简单版,第一次调用绑定this ,第二次调用传递参数。
Function.prototype.myBind = function(context){
let that =this
return function(...arg){
console.log(arg)
return that.myApply(context,arg)
}
}
obj2.sayHello.myBind(obj1)('雪梨','哈密瓜') //obj1:雪梨,哈密瓜
bind是支持柯里化传递参数的,先处理第一次传递部分参数,第二次传递剩下的参数。 为了支持柯里化,代码还需要修改下:
Function.prototype.myBind = function(context){
let that =this
let par = [...arguments].slice(1)
console.log(par)
return function(...arg){
console.log(arg)
return that.myApply(context,[...par,...arg])
}
}
obj2.sayHello.myBind(obj1,'圣女果')('雪梨','哈密瓜') // obj1:圣女果,雪梨,哈密瓜
bind有一个特点是可以使用bind的函数new对象。
function Person(name){
this.name = name
}
Person.prototype.do=function(){
alert(this.name)
}
Function.prototype.myBind = function(context){
let that =this
let par = [...arguments].slice(1)
let fn = function(...arg){
let ctx = context //默认使用传入this执行的对象
if(this instanceof fn){ //此时的this是new person('bar')的实例
ctx =this
}
return that.myApply(ctx,[...par,...arg])
}
return fn
}
let person = Person.myBind(null)
let p = new person('bar')
console.log(p.name) //'bar'
console.log(p.do()) // p.do is not a function
还有一个问题,如果使用上面的myBind,使用原型上面的方法会报错,需要把Person构造函数的原型,赋值给内部的fn。
function Person(name){
this.name = name
}
Person.prototype.do=function(){
alert(this.name)
}
Function.prototype.myBind = function(context){
let that =this
let par = [...arguments].slice(1)
let fn = function(...arg){
let ctx = context //默认使用传入this执行的对象
if(this instanceof fn){ //此时的this是new person('bar')的实例
ctx =this
}
return that.myApply(ctx,[...par,...arg])
}
fn.prototype =this.prototype
return fn
}
let person = Person.myBind(null)
let p = new person('bar')
console.log(p.do())
完。