对this的理解
this指向最后一次调用这个方法的对象。
this,可以通过四种模式来判断:
- 函数调用模式:当一个函数不是对象属性,直接作为函数调用时,
this指向全局。 - 方法调用模式:如果一个函数作为对象的方法调用,那么
this指向这个对象。 - 构造器模式:如果一个函数用
new调用,函数执行前会创建一个新对象,this指向这个新创建的对象。 apply、call和bind调用模式:这三个方法都可以显式的指定this方向。其中apply方法只接受两个参数,第一个参数是this绑定的对象,第二个是参数数组。call方法接收多个参数,第一个是this绑定的对象,后面都是函数执行的参数。
apply、call、bind三者的异同
- 都可以改变函数的
this指向 - 第一个参数都是
this要指向的对象,如果没有这个参数或者这个参数为undefined或null,则默认指向全局的window apply直接收两个参数,第一个参数是this要指向的对象,第二个是一个数组,以数组的形式进行传参;而call接收多个参数,除了第一个是this指向的对象外其他的都是要传入的参数。且两个都是一次传参,然后立即执行。bind可以分为多次传参,返回的是绑定好this之后的函数。
三者的实现
call的实现
实现步骤:
- 首先判断调用
ownCall方法的是不是一个函数对象 - 获取要传入的参数列表
- 判断
this指向的是否为一个对象 - 通过
symbol()定义一个唯一属性,并让this要指向的对象拥有改属性并且把调用ownCall的方法设置为改属性的值 - 调用这个属性方法然后删除改属性并返回结果
Function.prototype.ownCall = function(context) {
//判断调用ownCall方法的是否是一个函数
if (typeof this !== 'function') {
console.log("type error");
}
//获取参数
let args = [...arguments].slice(1);
//判断this的指向是否为一个对象类型
context = (typeof context === 'object' ? context : window)
// 防止覆盖掉原有属性
const key = Symbol()
// 这里的this为需要执行的方法
context[key] = this
// 方法执行
const result = context[key](...args)
delete context[key]
return result
}
apply的实现
和Call的实现方式差不多,不同之处在于给调用ownApply方法传参时要判断伪数组下表为1的元素是否存在且是否为伪数组。
Function.prototype.ownApply = function(context) {
//判断调用ownCall方法的是否是一个函数
if (typeof this !== 'function') {
console.log("type error");
}
//获取参数
//判断this的指向是否为一个对象类型
context = (typeof context === 'object' ? context : window)
// 防止覆盖掉原有属性
const key = Symbol()
// 这里的this为需要执行的方法
context[key] = this
// 方法执行
let result
if (arguments[1] && Array) {
result = context[key](...arguments[1])
} else {
result = context[key]();
}
delete context[key]
return result
}
bind实现
- 判断调用对象是否为函数
- 获取其余传入的值
- 将调用mybind函数在内部保存
- 创建一个函数返回
- 返回的函数内部使用apply来绑定函数调用
Function.prototype.myBind = function(context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
var args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 根据调用方式,传入不同绑定值
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
);
};
};