call:
语法:
1 | fun.call(thisArg[, arg1[, arg2[, ...]]]) |
thisArg:fun函数运行时指定的this值,可能的值为:
- 不传,或者传null,undefined, this指向window对象
- 传递另一个函数的函数名fun2,this指向函数fun2的引用
- 值为原始值(数字,字符串,布尔值),this会指向该原始值的自动包装对象,如 String、Number、Boolean
- 传递一个对象,函数中的this指向这个对象;
-
例如:
1 2 3 4 5 6
function a(){ console.log(this); } function b(){} a.call(b); // function b(){}
经常会看到这种使用情况:
1 2 3 4 5
function list() { // 将arguments转成数组 return Array.prototype.slice.call(arguments); } list(1,2,3); // [1, 2, 3]
为什么能实现这样的功能将arguments转成数组?首先call了之后,this指向了所传进去的arguments。我们可以假设slice方法的内部实现是这样子的:创建一个新数组,然后for循环遍历this,将this[i]一个个地赋值给新数组,最后返回该新数组。因此也就可以理解能实现这样的功能了。
apply:
1 2 3 | // Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。 // thisArg的可能值和call一样 fun.apply(thisArg[, argsArray]) |
例如:
1 2 3 | var numbers = [5, 6, 2, 3, 7]; var max = Math.max.apply(null, numbers); console.log(max) // 7 |
平时Math.max只能这样子用:Math.max(5,6,2,3,7)
;
利用apply的第二个参数是数组的特性,从而能够简便地从数组中找到最大值。
bind:
语法:
1 | fun.bind(thisArg[, arg1[, arg2[, ...]]]); |
bind()方法会创建一个新函数,称为绑定函数。
bind是ES5新增的一个方法,不会执行对应的函数(call或apply会自动执行对应的函数),而是返回对绑定函数的引用。
当调用这个绑定函数时,thisArg参数作为 this,第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
简单地说,bind会产生一个新的函数,这个函数可以有预设的参数。
1 2 3 4 5 6 7 8 9 | function list() { // 将arguments转成数组 return Array.prototype.slice.call(arguments); } var leadingThirtysevenList = list.bind(undefined, 37); // 绑定函数 var list = leadingThirtysevenList(1, 2, 3); // 调用绑定函数 console.log(list); // [37, 1, 2, 3] |
bind调用简单
把类数组换成真正的数组,bind能够更简单地使用:
- apply用法
1 2 3 | var slice = Array.prototype.slice; // ... slice.apply(arguments); // 类似对象的方法那样调用 |
- bind用法
1 2 3 4 | var unboundSlice = Array.prototype.slice; var slice = Function.prototype.apply.bind(unboundSlice); // ... slice(arguments); // 直接调用,简单 |
4、它们的区别
相同之处:改变函数体内 this 的指向。
不同之处:
- call、apply的区别:接受参数的方式不一样。
- bind:不立即执行。而apply、call 立即执行。
- call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。
- call、apply之间的差别在于参数的区别,call和aplly的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。
- 三者第一个参数都是this要指向的对象
bind
是返回对应函数,便于稍后调用;apply
、call
则是立即调用 。
举个栗子
function Person(name){
this.name = name;
}
Person.prototype = {
constructor: Person,
showName: function(){
console.log(this.name);
}
}
var person = new Person('qianlong');
person.showName();
上面的代码中person调用showName方法后会在浏览器的控制台输出qianlong
接下来
var animal = {
name: 'cat'
}
// 1 call
person.showName.call(animal);
// 2 apply
person.showName.apply(animal);
// 3 bind
person.showName.bind(animal)();
控制台输出了三次cat
上面代码中有一个对象字面量,他没有所谓的showName方法,但是我还是想用?怎么办?(坑爹了,这好像在让巧媳妇去做无米之炊),不过没关系,call、apply、bind可以帮我们干这件事。
我们拿别人的showName方法,并动态改变其上下文帮自己输出了信息,说到底就是实现了复用
应用
知道了怎么使用和他们之间的区别,接下来我们来了解一下通过call、apply、bind的常见应用场景。
- 求数组中的最大和最小值
var arr = [34,5,3,6,54,6,-67,5,7,6,-8,687];
Math.max.apply(Math, arr);
Math.max.call(Math, 34,5,3,6,54,6,-67,5,7,6,-8,687);
Math.min.apply(Math, arr);
Math.min.call(Math, 34,5,3,6,54,6,-67,5,7,6,-8,687);
- 数组追加
在js中要往数组中添加元素,可以直接用push方法,
var arr1 = [1,2,3];
var arr2 = [4,5,6];
[].push.apply(arr1, arr2);
// arr1 [1, 2, 3, 4, 5, 6]
// arr2 [4,5,6]
判断变量类型
对于对象型的数据类型,我们可以借助call来得知他的具体类型,例如数组
function isArray(obj){
return Object.prototype.toString.call(obj) == '[object Array]';
}
isArray([]) // true
isArray('qianlong') // false
利用call和apply做继承
var Person = function (name, age) {
this.name = name;
this.age = age;
};
var Girl = function (name) {
Person.call(this, name);
};
var Boy = function (name, age) {
Person.apply(this, arguments);
}
var g1 = new Girl ('qing');
var b1 = new Boy('qianlong', 100);