Description
apply
函数对象具有apply方法,用于在特定作用域中调用函数,接受两个参数,this作用域和参数(arguments或[ , , ]形式的数组)。
call
函数对象具有call方法,用于在特定作用域中调用函数,接受两个参数,this作用域和参数(准确地传入每一个参数,必须逐个列举出来)。
bind
函数对象具有bind方法,用于创建一个新的绑定函数,该函数的this值会被绑定到bind()的参数,接受一个参数,this作用域。
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3
apply、call、bind比较
当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
总结:
apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用
call和apply和比较
call和apply的作用相同,他们的区别在于接收参数的方式不同
进阶
-
他们正在强大的地方在于扩充函数赖以生存的作用域。
-
最大的好处是:对象不需要与方法有任何关系,就能指定作用域。之前在指定的作用域中调用函数,需要将该函数放到对象中,再通过对象来调用该函数。
call和apply是为了动态改变this。函数的this不在调用时改变的话,就是全局变量;对象的this就是自身。call和apply可以动态地改变this的值。
当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。
function fruits() {}
fruits.prototype = {
color: "red",
say: function() {
console.log("My color is " + this.color);
}
}
var apple = new fruits;
apple.say(); //My color is red
banana = {
color: "yellow"
}
apple.say.call(banana); //My color is yellow
apple.say.apply(banana); //My color is yellow
- 借用JavaScript中的原生引用类型中内置的方法。比如Array、Math、String。
例一:
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
var maxInNumbers2 = Math.max(5, 458 , 120 , -215), //458
number 本身没有 max 方法,但是 Math 有,我们就可以借助apply 使用其方法。
实际上这里是借助apply方法,就可以直接将数组传入,而不用手动地将数组中的项一个一个写入。
Math是javascript中的单体内置对象。Math对象包含数学计算中的一些特殊值;还包含一些简繁或者复杂的计算方法
例二:
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。
通过 Array.prototype.slice.call 转化为标准数组,再使用数组方法。
但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
例三:
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
例四:
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
- apply有一个特点,就是可以传入一个数组作为参数,这样对于数组操作很方便。
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
这样就不用将array2转换为一个个参数依次传入。
补充
JavaScript 的一大特点是,函数存在「定义时上下文」和「运行时上下文」以及「上下文是可以改变的」这样的概念。
参考资料
1.【优雅代码】深入浅出 妙用Javascript中apply、call、bind - ChokCoco - 博客园
2. (1 封私信 / 10 条消息)如何理解和熟练运用js中的call及apply? - 知乎