其实call、apply、bind大家都用过,这三个方法都可以改变函数整体内部的this指向,下面就简单说下这三个方法的使用以及不同点。
call
call 是属于所有Function的方法,类似于 Function.prototype.call
const obj = {
a: 't'
}
function fun(b, c, d){
this.b = b;
this.c = c;
this.d = d;
return this.a + this.b + this.c + this.d;
}
const value = fun.call(obj, 'e', 's', 't');
console.log(value, 'value')
apply
apply 与 call的语法几乎完全相同,唯一区别在于传参。
const obj = {
a: 't'
}
function fun(b, c, d){
this.b = b;
this.c = c;
this.d = d;
return this.a + this.b + this.c + this.d;
}
const value = fun.apply(obj, ['e', 's', 't']);
console.log(value, 'value')
bind
bind 返回的是一个新的函数
- bind是es5新增的一个方法
- 传参和call类似
- 不会执行对应的函数,call或者apply会自动执行对应的函数
- 返回对函数的引用
const obj = {
a: 't'
}
function fun(b, c, d){
this.b = b;
this.c = c;
this.d = d;
return this.a + this.b + this.c + this.d;
}
const value = fun.bind(obj,'e', 's', 't');
const newValue = value();
console.log(newValue, 'value')
对比
- call、apply会直接执行函数、bind会创建一个新的函数
- call、bind的传参类似,apply第二个参数为一个数组
指定this 注意事项
- 不传或者传null、undefined,函数中this指向window
- 传递另一个函数的函数名,函数中this指向这个函数的引用,并不一定是该函数执行时真正的this值
- 值为原始值的this会指向该原始值的自动包装对象
- 传递一个对象,函数中的this指向这个对象
模拟实现call
首先我们先知道call方法做了什么事情
- call 改变了this的指向
- 执行函数
- 不指定this的话指向window
- 从第二个参数开始为函数的入参 我们来简单实现下
function testCall (testContext) {
// 当传入为空时,默认使用window;
const context = testContext || window;
context.fn = this;
// const args = [].slice.call(arguments, 1); // 不能使用call
const args = [];
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn('+ args +')'); // evel 函数执行字符串
delete context.fn
}
Function.prototype.testCall = testCall;
var a = 1;
const obj = {
a:2
}
function testFun (name, age) {
console.log(name, age, this.a, '测试')
}
testFun.testCall(obj, '你好', '18')
testFun.testCall(null, '你好', '18')
apply
实现了call之后,apply就很简单
function testApply (testContext, arr) {
// 当传入为空时,默认使用window;
const context = testContext || window;
context.fn = this;
// const args = [].slice.call(arguments, 1); // 不能使用call
const args = [];
for (let i = 0; i < arr.length; i++) {
args.push('arr[' + i + ']');
}
eval('context.fn('+ args +')'); // evel 函数执行字符串
delete context.fn
}
Function.prototype.testApply = testApply;
var a = 1;
const obj = {
a:2
}
function testFun (name, age) {
console.log(name, age, this.a, '测试')
}
testFun.testApply(obj, ['你好', '18'])
testFun.testApply(null, ['你好', '24'])
bind
bind的实现依靠call 和 apply 来完成,具体如下
function testBind (testContext) {
// 当传入为空时,默认使用window;
const that = this;
// 第一次传参剩余参数;
const args = [].slice.call(arguments, 1);
return function () {
// 第二次传参参数
const argsTwo = [].slice.call(arguments);
// 执行
that.apply(testContext, args.concat(argsTwo))
}
}
Function.prototype.testBind = testBind;
var a = 1;
const obj = {
a:2
}
function testFun (name, age) {
console.log(name, age, this.a, '测试')
}
const r = testFun.testBind(obj, '你好');
const p = testFun.testBind(null);
r(28);
p('我好', 28)