Call-apply-bind实现
2020-10-27 本文已影响0人
Raral
call apply bind 原理解析和实现
call
概念:
简单来说就是当我执行 A 方法的时候,希望通过传入参数的形式将一个对象 B 传进去,用以将 A 方法的作用域对象替换为对象 B。
代码:
var value = "全局的value";
let tempObj = {
value: "指定对象的value"
}
function test() {
console.log(arguments); //类数组
console.log(this);
console.log("输出:" + this.value);
}
test(); //输出:全局的value
console.dir(test)
test.call(tempObj, 1, 2, 3); //输出:指定对象的value
应用场景(es5构造函数的继承):
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, score) {
//调用Person的call方法把当前this传入替换调Person内部this作用域对象。
Person.call(this, name, age);
this.score = score;
}
var s = new Student("lisi", 12, 100);
console.log(s) //{name: "lisi", age: 12, score: 100}
功能实现和思路:
- 赋值作用域参数,如果没有则默认为 window,即访问全局作用域对象
- 绑定调用函数(.call之前的方法即this,前面提到过调用call方法会调用一遍自身,所以这里要存下来)
- 截取作用域对象参数后面的参数
- 执行调用函数,记录拿取返回值
- 销毁调用函数,以免作用域污染
具体实现:
Function.prototype.mCall = function(context) {
// 1. 赋值作用域参数,如果没有则默认为 window,即访问全局作用域对象
console.log(this);
context = context || window;
// 2.绑定调用函数(.call之前的方法即this,前面提到过调用call方法会调用一遍自身,所以这里要存下来临时挂在参数里)
context.fn = this; //
// 3.截取作用域对象参数后的参数
let args = [...arguments].slice(1);
console.log(args instanceof Array);
// 4.执行调用函数就是第二步保存的fn,记录返回值
let result = context.fn(...args);
// 5.销毁调用函数,以免作用域污染;
Reflect.deleteProperty(context, 'fn');
return result;
}
test.mCall(tempObj, 1, 2, 3);//输出:指定对象的value
apply
使用过的人应该都知道,apply 和 call 的功能完全一致,区别唯有使用上的一丝丝差别
Function.prototype.call = function(context, args1, args2, args3 ...);
Function.prototype.apply = function(context, [args1, args2, args3 ...]);
具体实现:
Function.prototype.mApply = function(context) {
context = context || window;
context.fn = this;
let result;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
Reflect.deleteProperty(context, "fn");
return result;
}
test.mApply(tempObj, [1, 2, 3]);
bind
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用
这同样是 MDN 上给出的解释,意思应该已经很明显了,和 call 方法类似,调用是都是将内部的 this 作用域对象替换为第一个参数,不过需要注意开始和结尾,调用 bind 方法时会创建一个新的函数返回待调用
具体实现:
Function.prototype.mBind = function (context) {
// 获取绑定时的传参
let args = [...arguments].slice(1),
// 定义中转构造函数,用于通过原型连接绑定后的函数和调用bind的函数
F = function () {},
// 记录调用函数,生成闭包,用于返回函数被调用时执行
self = this,
// 定义返回(绑定)函数
bound = function () {
// 合并参数,绑定时和调用时分别传入的
let finalArgs = [...args, ...arguments]
// 改变作用域,注:aplly/call是立即执行函数,即绑定会直接调用
// 这里之所以要使用instanceof做判断,是要区分是不是new xxx()调用的bind方法
return self.call((this instanceof F ? this : context), ...finalArgs)
}
// 将调用函数的原型赋值到中转函数的原型上
F.prototype = self.prototype
// 通过原型的方式继承调用函数的原型
bound.prototype = new F()
return bound
}