apply,call,bind详解
三者区别
- apply,call,bind都能改变函数内部this指向
- apply,call会运行函数且返回函数结果,区别只是参数传递方式不一样
- bind不会运行函数,只绑定this且传参,依然返回一个函数
一、apply源码实现
实现之前,先写一下我们的测试案例
var obj = {
a: 1,
b: 2
}
function add(c, d){
return this.a + this.b + c + d;
}
console.log(add.myApply(obj) ) // 期望能 返回3
写之前请注意两点:
- apply需要运行调用函数
- 需绑定this指向
Function.prototype.myApply = function(context){
if(typeof this !== 'function'){
console.error('error: this is not a function');
}
// 保存this
context.fn = this;
// 处理参数,注意第二个参数是数组形式
let [args] = [...arguments].slice(1);
let result = context.fn(...args);
delete context.fn
return result;
}
运行截图如下:
可以看到函数中的this.a和this.b都拿到obj中的值了,说明没问题.
二、call
call与apply大致相同,只是参数处理稍微有点区别,感兴趣的同学可以自己去试试
三、bind
要点:
- 更改this指向
- 返回的还是一个函数
- 参数处理
Function.prototype.myBind = function(context){
if(typeof this !== 'function'){
console.error('error: this is not a function');
}
let self = this;
let args = [...arguments].slice(1);
let fn = function(){
return self.apply(context, args.concat([...arguments]))
}
return fn;
}
测试案例不变,结果如下:
面试题进阶
题目: 实现如下代码中的bind,可以让success成功打印出来
function Animal(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.say = function () {
return `I'm a ${this.color} ${this.name}`;
}
const Cat = Animal.bind(null, 'cat');
const cat = new Cat('white');
if (cat.say() === "I'm a white cat" && cat instanceof Cat && cat instanceof Animal) {
console.log('success')
}
这一题主要考的是继承,有几点要素:
- 原型继承
- apply只能继承对象的内部属性,不能继承其原型(prototype)
Function.prototype.bind = function (context) {
let self = this;
let args = Array.prototype.slice.call(arguments, 1);
let bound = function () {
let boundArgs = args.concat(Array.prototype.slice.call(arguments));
self.apply(this, boundArgs);
}
bound.prototype = new self()
return bound;
}
运行成功,没有问题。
要注意这里的bind返回的应该还是一个构造函数,且能继承绑定函数的内部属性以及原型。
你学会了吗?