再谈JavaScript中bind、call、apply三个方法的区别与使用方式

call的基本使用


 	var ary = [12, 23, 34];
 	ary.slice();

以上两行简单的代码的执行过程为:ary这个实例通过原型链的查找机制找到Array.prototype上的slice方法,让找到的slice方法执行,在执行slice方法的过程中才把ary数组进行了截取。

注意:slice方法执行之前有一个在原型上查找的过程(当前实例中没有找到,再根据原型链查找)。

当知道了一个对象调用方法会有一个查找过程之后,我们再看:


 	var obj = {name:'iceman'};
 	function fn() {
 	     console.log(this);
 	     console.log(this.name);
 	}
 	fn(); // this --> window
 	// obj.fn(); // Uncaught TypeError: obj.fn is not a function
 	fn.call(obj);

call方法的作用:首先寻找call方法,最后通过原型链在Function的原型中找到call方法,然后让call方法执行,在执行call方法的时候,让fn方法中的this变为第一个参数值obj,最后再把fn这个函数执行。

知道这个原型上的原理后,咱们就可以动手分析实现这三个方法了。

bind、call、apply 区别

callapply 都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。
除了第一个参数外,call 可以接收一个参数列表,apply 只接受一个参数数组


 	let a = {
 	     value: 1
 	}
 	function getValue(name, age) {
 	     console.log(name)
 	     console.log(age)
 	     console.log(this.value)
 	}
 	getValue.call(a, 'yck', '24')
 	getValue.apply(a, ['yck', '24'])

bind 和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过 bind 实现柯里化

如何实现一个 bind 函数

对于实现以下几个函数,可以从几个方面思考

不传入第一个参数,那么默认为 window
改变了 this 指向,让新的对象可以执行该函数。那么思路是否可以变成给新的对象添加一个函数,然后在执行完以后删除?

当然是肯定的,于是我们可以这样写:


 	Function.prototype.myBind = function (context) {
 	     if (typeof this !== 'function') {
 	     throw new TypeError('Error')
 	     }
 	     var _this = this
 	     var args = [...arguments].slice(1)
 	     // 返回一个函数
 	     return function F() {
 	     // 因为返回了一个函数,我们可以 new F(),所以需要判断
 	     if (this instanceof F) {
 	         return new _this(...args, ...arguments)
 	     }
 	     return _this.apply(context, args.concat(...arguments))
 	     }
 	}

如何实现一个 call 函数


 	Function.prototype.myCall = function (context,...arg) {
 	     var context = context || window // 给 context 添加一个属性
 	     // getValue.call(a, 'yck', '24') => a.fn = getValue
 	     //使用symbol 选择一个独一无二的值作为新添加的属性
 	     let symbol = new Symbol();
 	     context[symbol] = this;
 	     let result = context[symbol](...arg)
 	     // 删除添加的函数
 	     delete context[symbol]
 	     return result
 	}

如何实现一个apply 函数

apply实现原理与call实现基本类似,只有传值的方式不一样。


 	Function.prototype.myApply = function (context,arg) {
 	     var context = context || window // 给 context 添加一个属性
 	     // getValue.call(a, 'yck', '24') => a.fn = getValue
 	     //使用symbol 选择一个独一无二的值作为新添加的属性
 	     let symbol = new Symbol();
 	     context[symbol] = this;
 	     let result = context[symbol](arg)
 	     // 删除添加的函数
 	     delete context[symbol]
 	     return result
 	}

经过对以上的函数进行检测 , 完美通过。


 	const obj = {
 	     name : 'xiaoxiao',
 	     getName : function (arg) {
 	     console.log(`我是${this.name}里面的,我里面有${arg}`);
 	     }
 	}
 	obj.getName([0,0,0,0,0]); // 我是xiaoxiao里面的
 	const obj2 = {
 	     name : 'huahua'
 	}
 	//传值不一样
 	obj.getName.myCall(obj2,1,1,1,1,1,1);
 	obj.getName.myBind(obj2)(2,2,2,2,2,2);
 	obj.getName.myApply(obj2,[3,3,3,3,3,3]);

更多关于js中call、apply三个方法的区别与使用方式请查看下面的相关链接

标签

发表评论