一、call()
call()
通过指定this
的指向来实现函数的间接调用,并且可以传入函数调用的参数
举个例子:
var foo = {
value: 1
};
function bar() {
console.log(this.value)
}
bar.call(foo); // 1
首先我们要知道call()
在这里起的作用:
- 指定this的指向为foo
- 执行bar函数
我们可以模拟一下这两个效果:
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
};
foo.bar(); // 1
可以看到将bar
设为foo
的属性,便实现了将this
指向foo
。但是这样做给foo
对象平白无故的添加了一个属性,因此我们还要通过delete
删除它。
因此主要实现思路如下:
// 1.将函数设为对象的属性
foo.fn = bar
// 2.执行该函数
foo.fn()
// 3.从对象中删除该函数
delete foo.fn
根据这个思路先实现一个简易版的:
Function.prototype.call2 = function(context) {
// 首先要获取调用call的函数,用this可以获取
context.fn = this;
context.fn();
delete context.fn;
}
接下来实现call
给定参数执行函数:
要注意传入的参数个数是不确定的,我们可以从arguments
对象中取值,即第二个到最后一个参数便是要传入的参数。
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
// 遍历arguments类数组对象,取得第二个开始的参数,放到数组里面去
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 执行后 args为 [arguments[1], arguments[2], arguments[3]]
// 这里 args 会自动调用Array.toString()方法
eval('context.fn(' + args +')');
delete context.fn;
}
此时call()
方法的核心已经实现了,下面要完善call()
的功能:
- 第一个参数可以传入null,表示指向
window
- 函数是可以有返回值的
完整实现
Function.prototype.call2 = function(context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args + ')');
delete context.fn
return result;
}
// 测试
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call2(null); // 2
console.log(bar.call2(obj, 'kevin', 18));
// 1
// Object {
// value: 1,
// name: 'kevin',
// age: 18
// }
二、apply()
apply的实现跟call类似,区别在于apply第二个参数传入的是一个参数数组
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
结尾
系列文章: