深入理解Javascript中call()、apply()、bind()这几个方法的区别!

237 阅读4分钟

首先,我们先了解下函数内部的 this 指向:

  1. 普通函数的 this 指向:window
function func() {
  console.log("普通函数的this指向:" + this);
}
func(); // 控制台打印输出:普通函数的this指向:[object Window]
  1. 对象方法调用的 this 指向:该方法所属的对象
var obj = {
  name: 'Oct13_JJP',
  sayHello: function() {
    console.log("对象方法调用的this指向:" + this);
  }
}
obj.sayHello(); // 控制台打印输出:对象方法调用的this指向:[object Object]
  1. 构造函数调用的 this 指向:实例对象。原型对象里面的this也是指向实例对象
function SayHello() {
  console.log("构造函数调用的this指向:" + this);
}
new SayHello(); // 控制台打印输出:构造函数调用的this指向:[object Object]
  1. 事件绑定方法的 this 指向:事件的调用对象
<button id="btn">点我</button>

var btn = document.getElementById("btn");
btn.onclick = function() {
  console.log("事件绑定方法的this指向:" + this); // 控制台打印输出:事件绑定方法的this指向:[object HTMLButtonElement]
}
  1. 定时器函数的 this 指向:window
setTimeout(function() {
  console.log("定时器的this指向:" + this); // 控制台打印输出:定时器的this指向:[object Window]
}, 1000)
  1. 立即执行函数的 this 指向:window
(function() {
  console.log("立即执行函数的this指向:" + this); // 控制台打印输出:立即执行函数的this指向:[object Window]
})();

清楚了函数内部的this指向之后,要改变函数内部的this指向,也非常简单!
在Javascript中,要改变函数内部的 this 指向问题,通常有call()、apply()、bind()这三种方法。

call()方法

语法:func.call(thisObj, arg1, arg2, ...)

var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func() {
  console.log(this); // 控制台打印输出:window
}
func(); // 调用方法 func(),此时的this指向是 window

// 如果我们要将 this 指向 person 这个对象,可以调用 call() 方法
func.call(person); // 此时的 this 指向的就是 person 这个对象
// -----------------------------------------------------------------------------
// call()方法除了传递对象,还可以传递实参
var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func(name, age) {
  console.log("姓名:" + name + ",性别:" + age);
}
func.call(person, "Oct13_JJP", "男");

call()方法的作用:

  • 可以调用函数
  • 可以改变函数内部的 this 指向
  • 可以实现继承
// 有两个构造函数 Father 和 Son,现在想让 Son 继承 Father 的name,age,sex属性
function Father(name, age, sex) {
  this.name = name;
  this.age = age;
  this.sex = sex;
}
function Son(name, age, sex) {
  Father.call(this, name, age, sex)
}
var son = new Son("Oct13_JJP", "男", 20)
console.log(son); // 控制台打印输出:Son {name: "Oct13_JJP", age: "男", sex: 20}

apply()方法

语法:func.apply(thisObj, [arg1, arg2, ...])

var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func() {
  console.log(this); // 控制台打印输出:window
}
func(); // 调用方法 func(),此时的this指向是 window

// 如果我们要将 this 指向 person 这个对象,可以调用 apply() 方法
func.apply(person); // 此时的 this 指向的就是 person 这个对象
// -----------------------------------------------------------------------------
var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func(name, age) {
  console.log("姓名:" + name + ",性别:" + age);
}
// 与 call() 方法不同的是,apply() 的参数必须是数组形式
func.apply(person, ["Oct13_JJP", "男"]); // 控制台打印输出:姓名:Oct13_JJP,性别:男

apply()方法的作用:

  • 可以调用函数
  • 可以改变函数内部的 this 指向

bind()方法

语法:func.bind(thisObj, arg1, arg2, ...)

var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func(name, age) {
  console.log(this);
}
func.bind(person); // 控制台不输出任何内容,因为 bind 方法不会调用函数,只会改变函数内部的 this 指向

// 其返回值由 this 指向的值和初始化参数改造的原函数拷贝
var f = func.bind(person);
f(); // 控制台打印输出:{fullName: ƒ} 

// -----------------------------------------------------------------------------
var person = {
  fullName: function() {
    return this.firstName + '-' + this.lastName
  }
}
function func(name, age) {
  console.log("姓名:" + name + ",性别:" + age);
}
var fn = func.bind(person, "Oct13_JJP", "男");
fn(); // 控制台打印输出:姓名:Oct13_JJP,性别:男

bind()方法的作用:

  • 不会调用函数
  • 可以改变函数内部的 this 指向
  • 返回值由 原函数改变this之后产生的新函数(原函数拷贝)

总结

相同点:

  • 都可以改变函数内部的 this 指向
    区别:
  • call()、apply()会调用函数
  • bind()不会调用函数,而是会创建一个新函数
  • call()、apply()传递的参数格式不一样,call()传递参数的格式:arg1, arg2, ... ,apply()传递的参数必须是数组的格式:[arg1, arg2, ...]
  • bind()是返回原函数拷贝后的新函数,便于稍后调用;call()、apply()则是立即调用 。 常见的应用场景:
  • call() 常用于继承
  • apply() 常用于获取数组中的最大值与最小值
  • bind() 常和 setTimeout 一起使用