写在前面: 其实已经有很多优秀的文章去介绍前端相关知识,自己在学习过程中也是看了很多别人总结的知识点。决定开始自己写文章主要是为了对所学东西有个总结,希望后续慢慢也能起到分享交流的作用。
一、call、apply、bind的异同
在javascript中,这三个方法主要都是为了改变函数内部this指向的,作用大体相同,下面将依次介绍三个方法。
1. call()
语法: call(thisArgs, args1, args2,...),其中,thisArgs指的是函数运行时指向的this,arg1, arg2, ...指的是调用的参数;
返回值: 返回的是指定的this调用该函数以后的结果;
示例:
function say(name, hobby){
console.log(this)
console.log(`my name is ${name}, my age is ${this.age},my hobby is ${hobby}`)
}
let obj = {
age: 18
}
say('shaoyan', 'js')
say.call(obj, 'shaoyan', 'js')
在控制台执行上面的代码,执行结果如下图:
再来一个示例:
function Animal(name, age) {
this.name = name;
this.age = age;
}
function Cat(name, age) {
Animal.call(this, name, age);
this.category = '猫科';
}
let cat1 = new Cat('feta', 2); //来一个2岁的猫
console.log(cat1)
同样的控制台上执行,
2. apply()
apply方法和call方法区别在于就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
语法: apply(thisArgs, [argsArray]),其中,thisArgs指的是函数运行时指向的this,[argsArray]传入的参数,数组或者类数组对象;
返回值: 返回的是指定的this调用该函数以后的结果;
示例:
// 实例和上面call方法一致,除了参数是通过数组的形式传入,不多做介绍
function say(name, hobby){
console.log(this)
console.log(`my name is ${name}, my age is ${this.age},my hobby is ${hobby}`)
}
let obj = {
age: 18
}
say.apply(obj, ['shaoyan', 'js']) // 打印结果和call也是一致的
3.bind()方法
语法: bind(thisArgs, args1, args2,...),其中,thisArgs指的是函数运行时指向的this,arg1, arg2, ...指的是调用的参数;
返回值: 返回一个原函数的拷贝,并拥有指定的 this 值和初始参数;
bind()方法和call方法很相似,区别在于call方法返回的时函数的调用结果,而bind方法仅仅返回一个函数,如果要调用的话需要去执行它;
function say(name, hobby){
console.log(this)
console.log(`my name is ${name}, my age is ${this.age},my hobby is ${hobby}`)
}
let obj = {
age: 18
}
say.bind(obj, 'shaoyan', 'js')
//并不会执行函数,在控制台上打印结果为:
//ƒ say(name, hobby){
// console.log(this)
// console.log(`my name is ${name}, my age is ${this.age},my hobby is ${hobby}`)
//}
say.bind(obj, 'shaoyan', 'js')() //这样才会执行
4. 总结
来一个简短的总结
| 函数名 | 语法 | 参数方式 | 返回值 |
|---|---|---|---|
| call | call(thisArgs,args1,arg2,...) | 参数列表 | 函数的执行结果 |
| apply | apply(thisArgs,[argsArray]) | 参数数组 | 函数的执行结果 |
| bind | call(thisArgs,args1,arg2,...) | 参数列表 | 函数 |
二、 手动实现一个call方法
话不多说,我们来自己手动实现一个call方法
Function.prototype.myCall = function(){
//执行期上下文
let content = [...arguments][0] || window
content.fn = this
let args = [...arguments].slice(1)
return content.fn(...args)
}
写完以后还是用上面的例子来验证一下:
function say(name, hobby){
console.log(this)
console.log(`my name is ${name}, my age is ${this.age},my hobby is ${hobby}`)
}
let obj = {
age: 18
}
say.myCall(obj, 'shaoyan', 'js')
来控制台上打印一下:
Function.prototype.myCall = function(){
//执行期上下文
let content = [...arguments][0] || window
content.fn = this
let args = [...arguments].slice(1)
let result = content.fn(...args)
delete content.fn // 多了,我把你给删除掉
return result
}
这下再验证一下,ok没有问题啦,这样就实现了一个自己的call方法
三、 手动实现apply方法
apply方法就简单了,基本是call方法一致,只要将参数方式改为数组就可以了,上代码
Function.prototype.myApply = function() {
let content = [...arguments][0] || window
content.fn = this
let args = [...arguments][1]
let result = content.fn(...args)
delete content.fn
return result
}
// 测试也是ok的
四、手动实现bind方法
Function.prototype.myBind = function() {
let args = [...arguments]
let content = args.shift() || window
let _self = this
return function(){
let newAgrs = [...arguments]
_self.apply(content, args.concat(newAgrs))
}
}
// bind方法借助apply方法去实现
至此,自己手动实现call、apply、bind方法就此完成。