前言
首先,值得一提的是js中call()、apply()和bind()这三种方法是继承于Function.prototype中的实例方法,它们的作用都可以改变函数的调用对象(改变函数运行时上下文),也就是可改变函数体内
this
的指向,而this
的指向只有当函数被调用时才可被确定指向谁(此处若不了解js中this
指向问题,可自行百度查询),最后的返回值是调用方法的返回值,若没有返回值,则返回undefined,这三种实例方法同时也可简化代码的调用。
先来看看下面的几个例子:
例 1:
var name = '李四';
function one() {
var name = '张三';
console.log(this); // Window
console.log(this.name); // 李四
}
one();
从上面代码可以看出此时的this指向是Window对象(严格模式),Window是js中的全局对象,this.name指向的是全局变量。
例 2:
var a = {
name: '张三',
fn: function() {
console.log(this.name); // undefined
}
}
var b = a.fn;
b();
例 3:
var a = {
name: '张三',
fn: function() {
console.log(this.name); // 张三
}
}
a.fn();
从例2和例3可以看出,我们只能通过a.fn()方式达到所预期的效果,因为在例3中this的指向是函数a,a中存在name属性,而在例2中this的指向是函数b,此时函数b中并没有this.name,所以例2中会出现undefined的结果。但有时我们也需要将对象赋值于一个变量,此时我们就可以通过以下的call()、apply()、bind()方法来改变this的指向,从而达到预期的结果。
call()方法可以用来调用函数,改变this
的指向,实现继承,调用其他对象中的方法,并会立即执行当前函数,call()方法中第一个参数是this
的指向对象,第二个第三个或第n个参数用逗号隔开指函数中需传递的参数。
/* call() 方法-无参实例 */
var name = '小小';
var age = 10;
var test = {
name: '张三',
age: 18,
foo: function() {
console.log(this.name + ' ' + this.age);
}
}
var result = {
name: '李四',
age: 19
}
test.foo(); // 张三 18
test.foo.call(result); // 李四 19
test.foo.call(null); // 小小 10
test.foo.call(undefined); // 小小 10
test.foo.call({}); // undefined undefined
var way = test.foo;
way(); // 小小 10
way.call(result); // 李四 19
way.call(test); // 张三 18
/* call() 方法-有参实例 */
var person = {
name: '张三',
age: '18',
foo: function(a, b, c) {
console.log(a,b,c);
}
}
var student = {
name: '李四',
age: 19
};
person.foo(); // undefined undefined undefined
person.foo.call(student, 1, 2, 3); // 1 2 3
var foo = person.foo;
foo.call(student, 1, 2, 3); // 1 2 3
从上述实例中可看出call中若不传参或传入null、undefined时(非严格模式下),this指向window,若传入空对象时,返回undefined
,同时call方法在接受参数时可接受参数列表,并立即执行第一个参数指向的函数。
apply()方法和call()方法作用类似,可改变this指向,可继承其他对象的方法,被调用的方法函数会被立即执行,只是call()方法接收的参数是列表,而apply()方法接收的参数是数组。
/* apply() 方法-无参实例 */
var name = '小小';
var age = 10;
var test = {
name: '张三',
age: 18,
foo: function() {
console.log(this.name + ' ' + this.age);
}
}
var result = {
name: '李四',
age: 19
}
test.foo(); // 张三 18
test.foo.apply(result); // 李四 19
test.foo.apply(null); // 小小 10
test.foo.apply(undefined); // 小小 10
test.foo.apply({}); // undefined undefined
var way = test.foo;
way(); // 小小 10
way.call(result); // 李四 19
way.call(test); // 张三 18
/* apply() 方法-有参实例 */
var person = {
name: '张三',
age: '18',
foo: function(a, b, c) {
console.log(a,b,c);
}
}
var student = {
name: '李四',
age: 19
};
person.foo(); // undefined undefined undefined
person.foo.apply(student, [1, 2, 3]); // 1 2 3
var foo = person.foo;
foo.apply(student, [1, 2, 3]); // 1 2 3
从上面代码可以看出apply()方法在不传参时和call()方法类似,当传参时则需传入一个数组。
bind()方法可以改变函数内部的this
指向,但会返回一个新函数,函数不会被立即执行,需再次调用才能被执行。
/* bind() 方法-无参实例 */
var name = '小小';
var age = 10;
var test = {
name: '张三',
age: 18,
foo: function() {
console.log(this.name + ' ' + this.age);
}
}
var result = {
name: '李四',
age: 19
}
test.foo(); // 张三 18
test.foo.bind(result)(); // 李四 19
test.foo.bind(null)(); // 小小 10
test.foo.bind(undefined)(); // 小小 10
test.foo.bind({})(); // undefined undefined
var way = test.foo;
way(); // 小小 10
way.bind(test)(); // 张三 18
var outcome = way.bind(result);
outcome(); // 李四 19
/* bind() 方法-有参实例 */
var person = {
name: '张三',
age: '18',
foo: function(a, b, c) {
console.log(a,b,c);
}
}
var student = {
name: '李四',
age: 19
};
person.foo(); // undefined undefined undefined
person.foo.bind(student, 1, 2, 3)(); // 1 2 3
var foo = person.foo;
var result = foo.bind(student, 1);
result(2, 3); // 1 2 3
从上面代码可以看出bind 不会像call()和apply()方法一样调用后立即执行,它需要单独再调用bind()()
方法才可被执行。
总结
call、apply、bind三者的区别:
- call、apply、bind三者都可以改变函数(函数执行时所在的作用域)的this指向。
- call、apply、bind三者都可传递参数,其第一个参数都是this所指向的对象,call和bind的第二个第三个或第n个参数需用逗号隔开依次直接传递,而apply后面的参数需要放入一个数组中进行传递。
- apply和call会立即执行调用的函数,而bind会返回一个新函数,需再次调用才会被执行。
- 当确定函数中传递参数个数时,可选用call()方法;当不确定函数中传入的参数个数时,可选用apply();当不需要立即调用函数时,可选择bind()方法。