对this的理解,说说call apply bind
# 问题: 对this的理解,说说call apply bind
- 对this对象的理解
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在实际开发中,this 的指向可以通过四种调用模式来判断。
- 第一种是 函数调用模式 ,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是 方法调用模式 ,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是 构造器调用模式 ,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply 、 call 和 bind 调用模式 ,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply、call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。
2. call、apply 和 bind 都是用来改变函数中 this 的指向的方法。
call
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
举个例子:
var obj = {
value: "vortesnail",
};
function fn() {
console.log(this.value);
}
fn.call(obj); // vortesnail
通过 call
方法我们做到了以下两点:
call
改变了 this 的指向,指向到obj
。fn
函数执行了。
apply
我们会了 call
的实现之后,apply
就变得很简单了,他们没有任何区别,除了传参方式。
Function.prototype.myApply = function (context) {
if (typeof this !== "function") {
throw new Error("Type error");
}
let result = null;
context = context || window;
// 与上面代码相比,我们使用 Symbol 来保证属性唯一
// 也就是保证不会重写用户自己原来定义在 context 中的同名属性
const fnSymbol = Symbol();
context[fnSymbol] = this;
// 执行要被调用的方法
if (arguments[1]) {
result = context[fnSymbol](...arguments[1]);
} else {
result = context[fnSymbol]();
}
delete context[fnSymbol];
return result;
};
bind
bind
返回的是一个函数,这个地方可以详细阅读这篇文章,讲的非常清楚:解析 bind 原理,并手写 bind 实现 (opens new window)。
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new Error("Type error");
}
// 获取参数
const args = [...arguments].slice(1),
const fn = this;
return function Fn() {
return fn.apply(
this instanceof Fn ? this : context,
// 当前的这个 arguments 是指 Fn 的参数
args.concat(...arguments)
);
};
};