模拟实现Javascript中的call和apply

Call的模拟实现

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

这是MDN上关于call的描述,用通俗的语言解释一下就是:

  • 指定了this值(修改this)
  • 可以传入多个参数
  • 它的调用对象时函数

可能这样解释还是有些模糊,可以看一个例子:

function Test(name, age) {
	console.log(this.name);
	this.name = name;
	this.age = age;
}

var man = {
	name: 'Wei'
};
Test.call(man, 'Willem', 18); // Wei
console.log(man.name); // Willem
console.log(man.age); // 18

Test函数中输出this.name之前其实是没有name属性的,但是在执行Test.call(man, 'Willem', 18);之后却输出了Wei;在man对象中也是没有age属性并且name属性的值为Wei,输出的值却是Willem。说明在Test函数执行的过程中this的值指向了man对象,并且可以通过call函数向Test函数传递参数。

这一通分析下来无非就是一句话:用于函数的call函数可以指定this并传入多个参数。

在开始实现call函数之前,可以思考下如何修改this的指向,其实很简单,直接把目标函数挂载到需要修改的上下文。类似于这种:

var obj = {
	name: 'Willem',
	sayHi: function() {
		console.log(this.name);
	}
};
obj.sayHi(); /* Willem */

那代码如何实现就很清晰了啊。

Function.prototype.wcall = function(context) {
	context = context || window; // 不传入this时,默认是window
	context.__fn__ = this;
	var args = [];
	
	for (var i = 1, len = arguments.length; i < len; i++) {
		args.push('"'+ arguments[i] +'"');
	}
	
	var ret = eval('context.__fn__(' + args + ')');
	delete context.__fn__;
	
	return ret;
}

上述代码中,为了执行函数所以采用了eval,当然es6的扩展运算符也可以实现相同的功能。对eval或者js的类型转换不太熟悉的童鞋可能会对eval那段代码会有些疑惑。

var args = ['a', 'b', 'c'];
eval('context.__fn__(' + args + ')');

// 上面的代码实际上是两句

// 这实际上要说到js的隐式类型转换了,这里就不过多赘述了
// 只说一点:'string ' + [1, 2] 字符串和数组使用 +, 会将数组转换为一个字符串,和join方法的效果差不多
// 所以'string' + [1, 2] === 'string 1,2'
var str = 'context.__fn__(' + args + ')'; // === 'context.__fn__(a,b,c)'

// eval()则是会将传入的字符串当做JavaScript代码进行执行
// eval(str) 就相当于是直接执行 context.__fn__(a,b,c)
// 可以发现a, b, c其实只是参数字符串而不是变量
// 所以在最开始我们需要将args的参数都是用字符串进行包裹,wcall代码中:args.push('"'+ arguments[i] +'"')
// args = ["'a'", "'b'", "'c'"];
// 'context.__fn__(' + args + ')' === 'context.__fn__("a","b","c")'
// 就相当于在直接执行 context.__fn__("a","b","c")
eval(str);

以上就是关于call的实现。

Apply的模拟实现

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

原理啥的就不多说了,apply和call的作用是一样的,区别是apply是将参数作为一个数组传入,而call是将参数一个一个的传入。直接上代码:

Function.prototype.wapply = function(context, args) {
	context = context || window;
	context.__fn__ = this;
	var ret;
	
	if (!args || !(args instanceof Array) || args.length === 0) {
		ret = context.__fn__();
	} else {
		var arr = [];
		for (var i = 0; i < args.length; i++) {
			arr.push('"' + args[i] + '"');
		}

		ret = eval('context.__fn__(' + arr + ')');
	}

	return ret;
}

以上就是关于apply的实现。

更多:使用ts模拟实现的部分JavaScript函数和属性

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值