「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
call,apply,bind三兄弟的作用类似都是可以改变函数执行的上下文,即改变函数运行时的this指向
区别与使用
传参方式
Function.call(obj,[param1[,param2[,…[,paramN]]]])
Function.apply(obj[,argArray])
Function.bind(thisArg[, arg1[, arg2[, ...]]])
- 三个方法的第一个参数都是
this
指向的对象,差别在于后面的参数 call
的参数是直接传进去,用逗号分割apply
后面的参数放在一个数组中作为第二个参数bind
传参和call
一样,不直接执行函数而是返回一个函数
使用案例
var obj = {
name: 'Shane',
age: 30,
intro: function(city) {
console.log('I am ' + this.name + ' ' + this.age + ' years old' + ' from ' + city)
}
}
obj.intro('Shanghai') // I am Shane 30 years old from Shanghai
var ot = {
name: 'Peter',
age: 20
}
obj.intro.call(ot, 'Beijing') // I am Peter 20 years old from Beijing
obj.intro.apply(ot, ['Beijing']) // I am Peter 20 years old from Beijing
obj.intro.bind(ot, 'Beijing')() // I am Peter 20 years old from Beijing
手动实现
分析一波
call,apply,bind
方法第一个参数都函数执行的上下文,如果不传默认指向全局window
,后面的参数传参的形式不同- 调用结果
call,apply
一样直接执行函数并返回结果,bind则返回一个函数 - 由于函数都可以使用这三种方法,所有函数都是
Function
的实例,因此可以在Function
的原型上直接进行修改或者扩充,即Function.prototype
上
Function.prototype.myCall = function() {} // 手动实现call
- 在
Function.prototype
定义的方法中this
指向实例,也就是说当声明一个函数时,Function.prototype
原型上的方法中的this
都指向这个声明的函数
Function.prototype.myCall = function() {
// 当fn调用myCall时,这里的this指向fn
}
var fn = function(name) {
this.name = name
}
- 我们知道,执行一个对象中的函数时,函数中的
this
指向这个对象
。因此在手动实现时,在函数中传入第一个参数ctx
就是上下文的对象,在对象里新增一个属性指向this
,这样就将函数的作用域指向了ctx
然后执行这个新增的属性将剩余参数传入,最后返回。 - 看到这里可能有点懵圈,接下来看一下代码实现
call,apply
call apply类似只是传参方式不同
Function.prototype.myCall = function(ctx, ...args) {
ctx = ctx || window // 执行上下文
const symbol = Symbol() //使用symbol避免上下文中有重复属性名
ctx[symbol] = this // 新增一个属性指向调用的函数
const result = ctx[symbol](...args) // 储存调用结果
delete ctx[symbol] // 执行完成删除属性
return result // 返回执行的结果
}
Function.prototype.myApply = function(ctx, args) {
ctx = ctx || window
const symbol = Symbol()
ctx[symbol] = this
const result = ctx.fn(...args)
delete ctx[symbol]
return result
}
bind
因为返回的是一个函数,因此需要考虑如果是new一个实例的情况,需要继承原函数原型链上的方法
Function.prototype.myBind = function(ctx, ...args) {
ctx = ctx || window
ctx.fn = this
return function newFn(...returnArgs) {
// 是否实例化操作
if(this instanceof newFn) {
return new ctx.fn(...args, ...returnArgs)
}
return ctx.fn(...args, ...returnArgs)
}
}