答案:
他们都可以改变函数的作用域。
call/apply 可以直接执行该函数,而 bind 不会立刻执行
fucntion.bind(thisArg, arg1, arg2, ...)
call/apply 作用类似,都可以改变指针和执行函数,区别在于传参不同,call 需要单个传参,apply 通过数组传参
function.call(thisArg, arg1, arg2, ...)
function.apply(thisArg, [arg1, arg2, ...])
详细解析:
this代表函数(方法)执行的上下文环境,但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。
JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
call 和 aplly 作用完全一样,都是在特定的上下文中调用函数,或者说改变函数内部的this指向;区别仅在于接收参数的方式不同。
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
console.log(add.call(sub, 2, 1));//3
为什么add.call(sub, 2, 1)的执行结果是3呢,因为call()方法改变了this的指向,使得sub可以调用add的方法,也就是用sub去执行add中的内容。
var dog = {
name: "dog"
};
var cat = {
name: "cat"
};
var sayName = function (age, gender) {
console.log(this.name + "," + age + "," + gender)
};
sayName(); //undefined
sayName.call(dog, 2, "male"); //dog,2,male
sayName.call(cat, 3, "female"); //cat,3,female
sayName.apply(dog, [2, "male"]); //dog,2,male
sayName.apply(cat, [3, "female"]); //cat,3,female
我们在全局环境定义了一个 sayName 方法,调用该方法时会将当前环境的 name 属性 (this.name) 显示到控制台上。当我们在全局环境调用 sayName() 时,由于全局环境并没有 this.name 属性,因此显示了 undefined。
使用 call 或者 apply,就可以改变函数的执行环境,或者说改变函数内部的 this 指向。例如 sayName.call(dog),表示将sayName 内部的 this 指向了 dog 对象,因此 this.name 显示为 "dog"。当我们调用sayName.call(cat)时,sayName 内部的 this 又指向了 cat 对象。总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。
可以看出来,call与apply的作用完全相同,区别仅在于接收参数的方式不同。
fn.call(context, arg1, arg2, ...)
fn.apply(context, [arg1, arg2, ...])
call 和 apply 接收的第一个参数都是函数的运行环境。使用 call 时传递给函数的参数必须逐个传入,而使用 apply 时函数的参数应该是一个数组,或者类数组对象(如 arguments 对象)。至于使用 call 还是 apply,完全取决于采用哪种方法给函数传递参数更方便。在不需要给函数传递参数的情况下,使用哪个方法都无所谓。
apply的2种写法:
People.apply(this, [name, age]);
People.apply(this, arguments);
在这里arguments和[name, age]是等价的。
bind 与前两者作用类似,都是改变函数内部的 this 指向,区别在于 bind 会创建一个新的函数实例,每次调用该实例时,都会在被绑定的环境中运行。简单来说就是,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6
这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)。
var dog = {
name: "dog"
};
var cat = {
name: "cat"
};
var sayName = (function (age, gender) {
console.log(this.name + "," + age + "," + gender)
}).bind(dog, 3, "male");
sayName.call(cat, 2, "female"); //dog,3,male
我们创建 sayName 函数时,使用 bind 将该函数的 this 指向了 dog 对象,同时还绑定了传入函数的参数。
现在我们调用 sayName.call(cat, 2, "female"),发现不论是this对象,还是传入函数的参数,都是之前被绑定的值了。