call apply bind 的模拟
call apply bind 的模拟
一、先复习一下
为了应对 function 复杂多变的 this 指向,ES3提出了 call、apply 两种函数方法来显示绑定函数的 this,ES5 也提出一种 bind 方法来达到类似的效果。
下面我们复习一下这三种方法的使用。
let obj = {
a: 'A',
b: 'B'
}
let fun = function(a, b) {
console.log(this.a, this.b, a, b);
}
fun.call(obj, 'a', 'b');
fun.apply(obj, ['a', 'b']);
fun.bind(obj, 'a', 'b')();
// 上面三个都输出 A B a b
二、call 和 apply 的模拟
首先我们要知道 call 和 apply 函数的具体作用,这样才能做到全面模拟。模拟 call 和 apply 呢,主要需要注意以下四点:
1. 基本功能
2. 传入的 context 是 null/undefined 的时候,非严格模式要将 context 自动转化为 window
3. 传入的 context 不是对象,要转化为对象
4. 函数要有返回值
然后,我们就可以开工了:
Function.prototype.mycall = function(context, ...args) {
if (context === undefined || context === null){
// 处理 context 为 undefined/null
context = window;
} else if (typeof context !== Object) {
// 处理 context 为非对象
context = new Object(context);
}
context._fun = this;
let res = context._fun(...args);
delete context._fun;
return res;
};
同理,apply 也差不了多少:
Function.prototype.myapply = function(context, args) {
if (context === undefined || context === null){
// 处理 context 为 undefined/null
context = window;
} else if (typeof context !== Object) {
// 处理 context 为非对象
context = new Object(context);
}
context._fun = this;
let res = context._fun(...args);
delete context._fun;
return res;
};
三、bind 的模拟
bind 的模拟会稍微复杂点,因为需要注意的点模拟麻烦忆点点:
1. 基础功能
2. 两次传参
3. bind 出来的函数,可以被 new。而且对于 this 的指定,new 的权重比 bind 的大
4. 处理原型继承问题:用 fun.bind(...) 出来的函数去 new 一个对象 obj,obj 的原型需要继承 fun
5. 被 bind 过一次后,后面再 bind 是无效的(这个其实是不需要刻意去模拟的,知道就行)
Function.prototype.mybind = function(context, ...args1) {
if (context === undefined || context === null){
// 处理 context 为 undefined/null
context = window;
} else if (typeof context !== Object) {
// 处理 context 为非对象
context = new Object(context);
}
let that = this;
let fun = function(...args2) {
/* 第一次 bind,that 是待执行函数,that 的作用域已经被 apply 绑定了,
第二次 bind,that 就是第一次 bind 返回的 fun,对 fun bind 多少次都没有用了,
因为最后执行的都是第一次 bind 得到的 fun 里的 that.apply()
*/
return that.apply(
this instanceof fun ? this : context,
// new 的时候 this 指向实例,fun 为构造函数
args1.concat(args2)
// 参数合并
);
}
fun.prototype = that.__proto__;
/* new 出来的实例需要继承被 bind 的函数 that 的原型*/
return fun;
}