理解call、 bind、 apply

298 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

在JavaScript中,callapplybindFunction对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向。

callapplybind方法的共同点和区别: applycallbind 三者都是用来改变函数的this对象的指向的;

applycallbind 三者第一个参数都是this要指向的对象,也就是想指定的上下文(函数的每次调用都会拥有一个特殊值——本次调用的上下文(context)——这就是this关键字的值。); applycallbind 三者都可以利用后续参数传参;

bind 是返回对应函数,便于稍后调用; applycall 则是立即调用 。

理解Fuction.prototype.call()

语法:

call([thisObj[,arg1[, arg2[, [,.argN]]]]])

定义:

调用一个对象的一个方法,以另一个对象替换当前对象。

说明:

call 方法可以用来代替另一个对象调用一个方法。 call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

thisObj的取值有以下4种情况: (1) 不传,或者传null,undefined, 函数中的this指向window对象 (2) 传递另一个函数的函数名,函数中的this指向这个函数的引用 (3) 传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如 String、Number、Boolean (4) 传递一个对象,函数中的this指向这个对象

例子1

function class1(){   
  this.name=function(){   
    console.log("我是class1内的方法");   
  }   
}   
function class2(){ 
  class1.call(this);  //此行代码执行后,当前的this指向了class1(也可以说class2继承了class1) 
  console.log(this.name)
}  
class2()

例子2

function Animal(){   
  this.name="animal";   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(){   
  this.name="dog";   
}   
var animal=new Animal();   
var dog=new Dog();       

animal.showName.call(dog);

// dog

继承:

function Animal(name){   
  this.name=name;   
  this.showName=function(){   
    console.log(this.name);   
  }   
}   
function Dog(name){   
  Animal.call(this,name);   
}   
var dog=new Dog("Crazy dog");   
dog.showName();

输出:Crazy dog

Animal.call(this) 的意思就是使用 Animal对象代替this对象,那么Dog就能直接调用Animal的所有属性和方法。

理解Function.prototype.apply()

语法:apply([thisObj[,argArray]])

定义:应用某一对象的一个方法,用另一个对象替换当前对象。

说明: 如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。 如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

call 和 apply的区别 对于 apply、call 二者而言,作用完全一样,只是接受参数的方式不太一样。

function class1(args1,args2){       
  this.name=function(){      
   console.log(args,args);      
  }     
}     
function class2(){    
  var args1="1";
  var args2="2";
  class1.call(this,args1,args2);  
  /*或*/
  class1.apply(this,[args1,args2]);
}

var c=new class2();   
c.name();

输出:1 2

call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。

既然两者功能一样,那该用哪个呢?

在JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call ;而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

理解Function.prototype.bind()

bind是在EcmaScript5中扩展的方法(IE6,7,8不支持) bind() 方法与 apply 和 call 很相似,也是可以改变函数体内 this 的指向。

MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

注意:bind方法的返回值是函数

var bar=function(){  
  const x = '默认值'
  console.log(this.x);   
}
var foo={ 
     x:3   
}   
bar();  
bar.bind(foo)();

输出:
默认值
3

var Animal=function(){  
  const name = 'animal'
  const age = 0
  const language = 'null'
  console.log(`i'm ${this.name},${this.age} year's old,i bark like this ${this.language}`)

}
var dog={ 
    name:'little puppy',
    age:3,
    language:'wang wang'
} 

Animal.bind(dog)()

// i'm little puppy,3 year's old,i bark like this wang wang

手写Function.prototype.bind()

MDN:bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

详情

🚀 给既有函数绑定新的参数值

Function.prototype.bind = function(context, ...args) {
    // context为要改变的执行上下文
    // ...args为传入bind函数的其余参数
    return (...newArgs) => {
        // 这里返回一个新的函数
        // 通过调用call方法改变this指向并且把老参和新参一并传入
        return this.call(context, ...args, ...newArgs);
    }
};
// 测试用例
let o = {name: 'chd'};
function f(...args) {
    console.log(this.name, ...args);
}
let o2 = f.bind(o, 1, '222', null, [1,2,3], {age: 5});
o2();

// chd 1 222 null (3) [1, 2, 3] {age: 5}