JavaScript中的call和apply
了解到了JS中的this值,接下来就得学习下与this十分相关的call和apply方法了。
一、call()方法
语法:Function.prorotype.call(thisArg[, arg1[, arg2[, ...]]])
thisArg参数是函数在运行时指定的值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
该方法可以让call()中的对象调用当前对象所拥有的function(函数冒充)。
var obj1 = {
a: 1
};
var obj2 = {
a: 2
};
function show() {
console.log(this.a);
}
show.call(obj1); // 1
show.call(obj2); // 2
当我们调用show函数时,this分别会被绑定到obj1上和obj2上,所以对应的输出了1和2。 在继承中,我们还可以使用call调用父构造函数。
二、apply()方法
语法:Function.prorotype.apply(thisArg[, argsArray])
该函数的语法与call()方法的语法几乎一样。唯一的区别在于call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组(或类数组对象)。
三、bind()方法
既然提到了call和apply方法,就不得不提ECMAScript 5中的bind方法。
语法:Function.prorotype.bind(thisArg[, arg1[, arg2[, ...]]])
参数与call十分的类似。不过他实际是一个绑定函数,当函数被调用时,this值会被绑定到bind的第一个参数上,而且该参数不会被重写。
var a = 100;
var obj1 = {
a: 1,
show: function() {
console.log(this.a);
}
};
var obj1Show = obj1.show.bind(obj1);
obj1Show(); // 1
var winShow = obj1.show;
winShow(); // 100
与不带bind的进行对比,我们很快就可以发现bind方法的好处。
四、简单模拟实现
假如call和apply方法我们不能使用时,我们可以自己实现的方式模拟call和apply方法。
我们知道,call能正常运行,只是this指向的正确,所以我们可以采用先前讲到的this绑定的显示绑定的方式:
var a = 100;
var obj1 = {
a: 1
};
function show(name) {
console.log(this.a);
}
Function.prototype.Call = function(obj) {
obj.fn = this;
obj.fn();
delete obj.fn;
}
show.Call(obj1); // 1
这样就实现了最基本的call方法,下面,给call方法增加参数。
var a = 100;
var obj1 = {
a: 1
};
function add(num1, num2) {
console.log(num1 + num2 + this.a);
}
Function.prototype.Call = function(obj, name, age) {
var args = [];
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
obj.fn = this;
obj.fn(args[0], args[1]);
delete obj.fn;
}
add.Call(obj1, 1, 1); // 3
定长的参数我们可以使用这种方式,但是如果是不定长的情况下呢?我们可以使用eval方法:
var a = 100;
var obj1 = {
a: 1
};
function add(num1, num2, num3) {
console.log(num1 + num2 + this.a + num3);
}
Function.prototype.Call = function(obj, name, age) {
var args = [];
for (var i = 1; i < arguments.length; ++i) {
args.push(arguments[i]);
}
obj.fn = this;
var re = eval('obj.fn(' + args + ')');
delete obj.fn;
return re;
}
add.Call(obj1, 1, 1, 1);
这样的话,不定长参数的call方法就实现了。apply方法与call相同:
Function.prototype.Apply = function(obj, arg) {
var args = [];
for (var i = 0; i < arg.length; ++i) {
args.push(arg[i]);
}
obj.fn = this;
var re = eval('obj.fn(' + args + ')');
delete obj.fn;
return re;
}
同样的,我们只需要改下传参和使用参数的方式就行了,毕竟call与apply相差的并不是很大。这样,call和apply方法的简单实现就完成了。