模拟实现call和apply
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
上面是 call 的定义,apply 与 call 类似只是第二个参数是一个数组而 call 接收的则是一个参数列表。
看一个例子
var obj = {
name: "obj"
};
function foo() {
return this.name;
}
foo.call(obj); //obj
上面通过 call 改变了 this 的指向,下面就是模拟实现
实现
call 和 apply 是 es5 的方法,既然是模拟实现那肯定不能用 es5 方法了,我们先分析一下怎么来指定 this,this 跟动态作用域类似是在执行时确定,那我们在指定 this 的属性上添加一个方法并且执行,那么这个方法的 this 就是指定 this
var obj = {
name: "obj",
foo: function foo() {
return this.name;
}
};
obj.foo();
上面解决了 this,下面来看下怎么实现参数的传递,call 方法可以传递任意参数列表,我们可以通过arguments
来获取,它是一个类数组。
function getName() {
console.log(arguments); //Arguments(5) [1, 2, 3, 4, 5]
}
function foo() {
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push("arguments[" + i + "]");
}
eval("getName(" + args + ")");
}
foo(1, 2, 3, 4, 5);
OK,这两块已经搞定了,下面就是实现
call
Function.prototype.calls = function(con) {
con.fn = this;
// 获取参数
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push("arguments[" + i + "]");
}
var result = eval("con.fn(" + args + ")");
delete con.fn;
return result;
};
上面删除属性是为了避免污染,这里的fn
可以是任意属性名只要保证不重复就行了,不过仔细观察上面函数还是有问题
- call 的 this 可以是
null
或者undefined
; - 可以是字符串数字之类的,会转化为包装对象;
Function.prototype.calls = function(con) {
if (con == null) {
con = typeof window === "object" ? window : global;
}
con = Object(con);
con.fn = this;
// 获取参数
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push("arguments[" + i + "]");
}
var result = eval("con.fn(" + args + ")");
delete con.fn;
return result;
};
测试一下
function foo() {
return this.length;
}
console.log(foo.calls("obj")); // 3
撒花,这样就实现了 call
apply
与 call 十分类似,这里就直接贴代码了
Function.prototype.applys = function(con, arr) {
if (con == null) {
con = typeof window === "object" ? window : global;
}
con = Object(con);
con.fn = this;
var result;
if (typeof arr === "object" && arr.length) {
var args = [];
for (var i = 0; i < arr.length; i++) {
args.push("arr[" + i + "]");
}
result = eval("con.fn(" + args + ")");
} else {
result = eval("con.fn()");
}
delete con.fn;
return result;
};