其实大家都会使用call、apply、bind来修改this的指向,但是第一次在面试中提到让我实现一下这几个function,我是有点懵逼的(前端小白)。 在《你不知道的JavaScript》上卷一书中详解了关于this的指向问题。我在思考实现的时候没有看具体的源码,就是从这本书列举的this绑定的优先级来考虑。 如果有错误,请大佬指教。
列举一下this绑定的优先级规则:
- new调用?绑定到新创建的对象
- call、apply、bind调用?绑定到指定的对象
- 由上下文调用?绑定到上下文对象
- 默认:严格模式?undefined : 全局对象
例外情况
- call、apply、bind调用时,如果指定的对象为null、undefined,使用默认绑定规则
- 箭头函数:书里用
this词法的概念,如果是初入前端,对词法类的概念不太理解的话,可以简单理解为定义箭头函数的的this,即外层作用域来决定this。我理解的就是直接使用父级作用域的this。
我考虑的过程很简单,this的优先级由上面四个顺序决定,那就直接使用上下文调用来实现就好。也就是将函数作为指定对象的一个属性,使用该对象来调用函数。
// call实现
function _call() {
let args = [...arguments];
let context = args.shift();
context.func = this;
const result = context.func(...args);
delete context.func;
return result;
}
// apply的实现
function _apply() {
let args = [...arguments];
let context = args.shift();
context.func = this;
const result = context.func(...args[0]);
delete context.func;
return result;
}
// bind的实现
function _bind() {
let args = [...arguments];
let context = args.shift();
context.func = this;
const result = function() {
const result = context.func(...args);
delete context.func;
return result
}
return result;
}
Function.prototype._call = _call;
Function.prototype._apply = _apply;
Function.prototype._bind = _bind;
下面是写的简单测试代码
function foo() {
return this.name;
}
const person = {
name: "Jack",
age: 23
};
const result_call = foo._call(person, "1", "2");
const result_apply = foo._apply(person, ["3", "4"]);
const result_bind = foo._bind(person, "5", "6");
console.log('result_call :', result_call); // Jack
console.log('result_apply :', result_apply); // Jack
console.log('result_bind :', result_bind);
// function() {
// const result = context.func(...args);
// delete context.func;
// return result
//}
与call、apply不同,bind的返回的是一段硬编码的函数。 bind实现,可以直接使用call和apply,我这里写的代码重复。 以上代码使用了es6的语法,只适用于简单理解,不适合实际开发。源码中的实现比这里要复杂。 遗留一个问题:关于柯里化的实现和原理,我一直没有弄明白。今天写bind的实现的时候,感觉柯里化基本就是使用闭包。等我研究一下,出个详解。