this、call()、apply()、bind()
1.this 的理解
this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在开发中,this 的指向可以通过 4 中调用模式来判断。
- 1.1.函数调用模式:
当一个函数不是一个对象的属性时候,直接作为一个函数来调用,this 指向全局对象。 - 1.2.方法调用模式:
当一个函数是有一个对象来调用时,this 指向这个对象。 - 1.3.构造器调用模式:
当一个函数用 new 调用时,函数执行前会去创建一个对象,this 指向这个新创建的对象。 - 1.4.apply、bind、call 调用模式:
this 指向方法中的第一个参数
2.call、apply、bind
这三个方法都可以改变 this 的指向。
三者的区别
- call()、apply()方法是立即调用当前函数,而 bind()是返回一个改变了 this 指向的新函数,并不立即调用。
- call()传递的参数形式是:call(newThis,参数 1,参数 2...),传递参数数量不固定,
apply()传递的参数形式是:apply(newThis,[参数 1,参数 2,...]),接收 2 个参数,第一个参数是函数体内 this 指向,第二
各参数是一个数组。 - bind()可以像 call()一样传参,但是由于它返回的是一个函数,也可以在返回的函数调用时传参。
2.1.call()
使用方法: fun.call(newThis,参数 1,参数 2...)
手写 call 函数步骤
- 考虑写在哪里,应该卸载构造函数的原型对象上
- 考虑传入那些参数,原生 call 传入上下文对象和参数,且参数是一个一个传递
- 考虑传入的上下文对象是原始类型还是引用类型,如果传入的是原始类型,其中上下文指向包装类型的对象,
比如传入数字 1,this 指向[Number: 1],传入 null 和 undefined,this 指向全局对象,传入引用引用类型,则
指向该引用类型自身。所以传入的上下文对象可以使用 Object()构造函数将传入的值转换成一个对象
(原始类型中比如 number、string、boolean 会转换成包装类对象,引用类型为自身)。- 想知道谁在调用 myCall()方法, 就需要知道 this,this 就是调用者,因此需要给上下文对象加一个 this 属性,
但是不可以随便定义属性名,可以使用 Symbol(),创建的值是唯一的。同时也让这个属性不可枚举。- 调用该方法,并接收一个返回值
- 调用完成之后,删除这个属性
- 返回函数调用的返回值
Function.prototype.myCall = function (context, ...args) {
context = context == null || context == undefined ? globalThis : Object(context);
const key = Symbol('this');
Object.defineProperty(context, key, {
value: this,
enumerable: false,
});
const result = context[key](...args);
delete context[key];
return result;
};
function method(a, b) {
console.log(a, b); // 1 2
console.log(this); // { name: '张三' }
}
method.myCall({ name: '张三' }, 1, 2);
2.2.apply()
使用方法: fun.apply(newThis,[参数 1,参数 2...])
手写 apply 函数步骤
apply 和 call 的区别在于传参,所以更改传参即可, 给第二个入参给一个默认值 [ ]
Function.prototype.myApply = function (context, args = []) {
context = context == null || context == undefined ? globalThis : Object(context);
const key = Symbol('this');
Object.defineProperty(context, key, {
value: this,
enumerable: false,
});
const result = context[key](...args);
delete context[key];
return result;
};
function method(a, b) {
console.log(a, b);
console.log(this);
}
method.myApply({ name: '张三' }, [1]);
2.1.bind()
使用方法: let newFun = fun.bind(newThis,参数 1,参数 2...); newFun(参数 3,参数 4...)
手写 bind 函数步骤
其大体步骤和手动实现 call 和 apply 类似,但是 bind 是返回一个新的函数,所以要返回一个新的函数,这个函数内部调用了调用者自身的方法,也要注意传入参数的时机和个数。
Function.prototype.myBind = function (context, ...args1) {
return (...args2) => {
context = context == null || context == undefined ? globalThis : Object(context);
const key = Symbol('this');
Object.defineProperty(context, key, {
value: this,
enumerable: false,
});
const result = context[key](...args1, ...args2);
delete context[key];
return result;
};
};
function method(a, b, c, d) {
console.log(a, b, c, d); // 1 2 3 4
console.log(this); // { name: '张三' }
}
let result = method.myBind({ name: '张三' }, 1, 2);
result(3, 4);