在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,
换句话说,就是为了改变函数体内部 this 的指向。
bind,apply,call的区别 | |||
---|---|---|---|
方法 | 调用方式 | 第一个参数 | 其余参数 |
bind | 返回一个函数 | 第一个参数是改变this指向的对象 | 直接传参 |
apply | 对函数的直接调用 | 第一个参数是改变this指向的对象 | 参数用数组包裹 |
call | 对函数直接调用 | 第一个参数是改变this指向的对象 | 直接传参 |
function fn(){
console.log('调用函数')
}
fn.call()
fn.apply()
②传参数
function addNumber(x,y){
return x +y
}
let arr = [2, 3]
addNumber.apply(null, arr)
③改变函数内部的this指向
var Person = {
name:"devincob",
age:26
}
function fn(x,y){
console.log(x+","+y);
console.log(this);
}
fn("Tina",20);
此时函数的this指向window
现在让我们更改this指向
var Person = {
name:"devincob",
age:26
}
function fn(x,y){
console.log(x+","+y);
console.log(this);
console.log(this.name);
console.log(this.age);
}
fn.call(Person,"Tina",20);
现在this就指向person了
let arr = [1, 10, 11, -1, '-5', 12, 13, 14, 15, 2, 3, 4, 7, 8, 9]
function maxMinPlus(arr) {
// 返回最大值与最小值之差
let max =Math.max.apply(Math, arr)
let min =Math.min.apply(Math, arr)
return Array.isArray(arr) ? max - min : console.log('传入不是数组亦或者others错误')
}
maxMinPlus(arr)
function maxMin(arr) {
// 返回最大值与最小值之差
let max =Math.max(...arr)
let min =Math.min(...arr)
return Array.isArray(arr) ? max - min : console.log('传入不是数组亦或者others错误')
}
maxMin(arr)
戏说一个栗子:
function Person() {//首先我造了一个人
}
Person.prototype = {//然后给人加上天赋status和符文run
status: "good",
run: function () {
console.log("My run status is " + this.status);
}
}
var xiaoMing = new Person();//小明就是我创造出来的这批人中的某一个
xiaoMing.run(); //所以小明自带有符文run和天赋status
var xiaoQiang = {//小强不是我造出来的人 但是他也有天赋status似乎更好
status: "very good"
}
xiaoMing.run.call(xiaoQiang); //小强像看看自己练了多少级别是什么段位,就借了小明的符文显现出段位
所以,可以看出 call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法(本栗子中小强没有run方法),但是其他的有(本栗子中小明有run方法),我们可以借助call或apply用其它对象的方法来操作。
看法(不同角度):
- Person() 打印出别的status 用call修改status的this指针
- xiaoQiang没有run() 所以借用Person中的run() 来打印
apply、call 的区别:
对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。
俩者的格式和参数定义:
call的语法:call(thisObj,arg1,arg2……)
apply的语法:apply(thisObj,[argArray])
function xiaoYing(name){//例如,有一个姑娘叫小樱定义如下:
console.log('my name is',name)//普通男孩能得到的信息
console.log.apply(console, arguments);//pua男能得到的信息
//因为pua借用了高科技apply->有钱有权...(开玩笑)这个高科技
}
xiaoYing('xiaoying')
xiaoYing('xiaoYing',18, '上海','爱狗',{height:'170',weight:'55kg',sanwei:'...',family:'xxx'})
// 大家都在追xiaoYing小樱那么优秀吗?让我们看看小樱的实力和背景
// 给xiaoYing 添加个背景吧
function xiaoYing(){
let specialty =Array.prototype.slice.call(arguments)
specialty.unshift('老爸是马云')
console.log.apply(console, specialty)//完犊子 彻底没机会了,人家不缺钱呐
}
关于bind
说完了 apply 和 call ,再来说说bind。bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。
把foo.bar的值传入bar()中来
我们创建了一个新的函数 func,当使用 bind() 创建一个绑定函数之后,它被执行的时候,
它的 this 会被设置成 foo , 而不是像我们调用 bar() 时的全局作用域。
let bar = function(){
console.log(this.x);
}
let foo = {
x:3
}
let sed = {
x:4
}
let func1 = bar.bind(foo).bind(sed);
func1(); //3
let fiv = {
x:5
}
let func2 = bar.bind(foo).bind(sed).bind(fiv);
func2(); //3
bar(); // undefined
let func3 = bar.bind(foo);
func3(); // 3
答案是,将输出undefined/非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效更深层次的原因是bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。
MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
var foo = {
bar : 1,
eventBind: function(){
var _this = this;
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(_this.bar); //1
});
}
}
由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $(’.someClass’).on(‘click’,function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:
var foo = {
bar : 1,
eventBind: function(){
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(_this.bar); //1
}.bind(this));
}
}
在上述代码里,bind() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。
apply、call、bind比较
那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。简单的一个栗子:
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
- bind() 回调函数 被调用才执行 若立即执行函数 bind()() 多个括号
- call() apply() 立即执行函数 马上执行`
三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。
也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
总结:
- apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
- apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
- apply 、 call 、bind 三者都可以利用后续参数传参;
- bind是返回对应函数,便于稍后调用;apply、call则是立即调用 。
本文借鉴 来源链接:https://www.jianshu.com/p/c0472778622b