call方法和apply方法的作用及意义是什么?什么时候使用?
call方法和apply方法的作用及意义是什么?什么时候使用?
`call()`和`apply()`都是JavaScript 中函数对象上的方法:
```
var f = function () {};
'call' in f; // true
'apply' in f; // true
```
说到函数,JavaScript 中有个`this`的概念与函数调用相关,`this`指向一个对象,表示函数调用时的执行上下文;当在函数内引用`this`的时候,就会指向这个对象。
```
var man = {
name: 'Jason',
sayName: function() {
console.log(this.name);
}
}
man.sayName(); // 输出'Jason'
```
上面的例子中,`sayName()`是对象`man`上的方法,`sayName()`里`this`指向`sayName()`所在的对象(即`man`),`this.name`实际上是`man.name`,因此最后输出'Jason'。
如果函数不是对象上的方法,那`this`默认情况下会指向全局上下文,在浏览器中,也就是`window`:
```
window.name = 'Tommy'
function foo() {
console.log(this.name);
}
foo(); // `this`指向`window`,所以输出'Tommy'
```
明白了`this`之后,`call()`和`apply()`就好解释了。这两个方法的作用是在函数调用时改变函数的执行上下文,也就是函数内的`this`;这两个方法第一个参数,就是希望得到的`this`。
比如上面的`foo`函数,由于定义在全局环境中,`this`默认指向`window`;如果想更改里面指向的`this`,例如改成`man`,可以调用`foo`的`call()`方法,然后把`man`传进来:
```
foo.call(man); // 输出`man`
foo.apply(man); // 也是输出`man`
```
两个方法都可以更改函数执行时绑定的`this`;那它们有什么不同呢?主要是在传参上。
假如`foo`定义时包括了形参:
```
window.name = 'Tommy'
function foo(a, b) {
console.log(a + b + this.name);
}
```
也就是说`foo`调用时期望传进两个参数,所以对于`foo.call()`和`foo.apply()`的形式,第一个参数指定绑定的`this`,后面的参数指定`foo`调用时期望传入的参数(有2个);对于`call`来说,两个参数一个一个传进来;而对于`apply`来说,两个参数必须组成一个数组,以数组方式传进来:
```
foo.call(man, 'Hello, ', 'Mr.'); // 输出‘Hello, Mr.Jason’
foo.apply(man, ['Hello, ', 'Mr.']); // 同样输出‘Hello, Mr.Jason’
```
比较下直接调用`foo`的结果
```
foo('Hello, ', 'Mr.'); 输出‘Hello, Mr.Tommy’
```
call和apply都用于函数调用,和使用函数名直接调用不同,call和apply可以指定一个额外的参数作为函数体内的this对象。
看下面示例:
function greet(){
console.log("hello " + this.name);
}
//三种函数调用方法的比较
greet(); // 输出: hello
greet.call({name:"John"}); //输出:hello John
greet.apply({name:"Mary"}); //输出:hello Mary
call采用不定长的参数列表,而apply使用一个参数数组。
看下面示例:
function sub(a,b){
console.log(a-b);
}
sub(10,3); //输出:7
sub,call(null,10,3); //输出:7
sub.apply(null,[10,3]);//输出:7
由于call和apply可以改变函数体内的this指向,因此通常被用来将一个对象原型上的方法应用到另一个对象上。一个常见的应用是
处理函数的arguments,将其转换为Array类型:
function test(){
var args = Array.prototype.slice.call(arguments,0); //将arguments转化为数组
console.log(args.join("|")); //输出各参数拼接结果,以|隔开
}
test(10,23,190); // 输出:10|23|190