JavaScript 中的 call()、apply()、bind() 的详解

时间:2023-01-01 09:42:18
三种方法的作用

在 JavaScript 中

  1. callapplybind 是 Function 对象自带的三个方法,都是为了改变函数体内部 this 的指向。
  2. callapplybind 三者第一个参数都是 this指向的对象,也就是想指定的上下文
  3. callapplybind 三者都可以利用后续参数传参。
  4. bind 是返回对应 函数,便于稍后调用;applycall 则是立即调用 。
举个栗子
function fruits() {}

fruits.prototype = {
   color: 'red',
   say: function() {
       console.log('My color is ' + this.color); 
   }
}

var apple = new fruits;
apple.say();   // 此时方法里面的this 指的是fruits // 结果: My color is red

如果我们有一个对象 banana= {color : 'yellow'} ,我们不想重新定义 say 方法,那么我们可以通过 callapply 用 apple 的 say 方法:

var banana = {
    color: 'yellow'
};
apple.say.call(banana); 
// 此时的this的指向已经同过call()方法改变了,指向的是banana,this.color就是banana.color='yellow';
// 结果是My color is yellow 

apple.say.apply(banana);
// 同理,此时的this的指向已经同过apply()方法改变了,指向的是banana,this.color就是banana.color ='yellow';
// 结果是My color is yellow


apple.say.apply(null); 
// nullwindow下的,此时,this 就指向了window ,但是window下并没有clolr这个属性,因此this.clolr就是window.color=undefined;
// 结果是My color is undefined
callapply 的区别

二者的作用完全一样,知识接受 参数 的方式不太一样。

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

var array1 = [12,'foo',{name:'Joe'},-2458];
var array2 = ['Doe' , 555 , 100];

Array.prototype.push.call(array1, array2);
// 这里用 call 第二个参数不会把 array2 当成一个数组,而是一个元素
// 等价于 array1.push("'Doe' , 555 , 100");
// array1.length=5;

Array.prototype.push.apply(array1, array2); 
// 这里用 apply 第二个参数是一个数组
// 等价于: array1.push('Doe' , 555 , 100);
// array1.length=7;
类(伪)数组使用数组方法
var divElements = document.getElementsByTagName('div'); 
// 虽然 divElements 有 length 属性,但是他是一个伪数组,不能使用数组里面的方法
Array.isArray(divElements);// false

var domNodes = Array.prototype.slice.call(document.getElementsByTagName('div'));
// 将数组对象 Array 里的 this 指向伪数组 document.getElementsByTagName('div'), 
// slice() 方法可从已有的数组中返回选定的元素,不传参数是,返回整个数组 
Array.isArray(domNodes);// true
验证一个对象的类型可以用
Object.prototype.toString.call(obj)
bind() 方法

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

注意bind()方法创建的函数不会立即调用,在下面的例子中,最后 func() 才调用了函数,这是它与 callapply的区别。

var bar = function(){
    console.log(this.x);
}
var foo = {
    x:3
}
bar(); // undefined
var func = bar.bind(foo); 
//此时this已经指向了foo,但是用bind()方法并不会立即执行,而是创建一个新函数,如果要直接调用的话 可以bar.bind(foo)()

func(); // 3

在 Javascript 中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

var bar = function(){
  console.log(this.x);
}
var foo = {
  x:3
}
var sed = {
  x:4
}
var func = bar.bind(foo).bind(sed);
func(); //3

var fiv = {
  x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3