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;
}
posted @ 2020-12-17 22:44  树干  阅读(75)  评论(0编辑  收藏  举报