JavaScript 中 call、apply、bind 用法和區別

簡介

JavaScript 中有三個方法Function.prototype.call()Function.prototype.apply()Function.prototype.bind()能夠用來指定函數 this 值。call()apply() 相似,都是調用函數,並指定函數的 this 值thisArg和參數,區別在於call()傳入參數是經過參數列表的形式arg1, arg2, ...apply()傳入參數是經過數組的形式[arg1, arg2, ...]數組

function.call(thisArg, arg1, arg2, ...)
function.apply(thisArg, [arg1, arg2, ...])

bind()方法與前兩個不一樣,它建立一個新的函數,在調用新函數時,會調用原函數,並指定原函數的 this 值和參數。bind() 執行的時候並無調用函數。bind()傳入參數的方式和call()同樣,都是用參數列表:app

fucntion.bind(thisArg, arg1, arg2, ...)

用法

call()

使用call()調用父類構造函數來實現繼承,也能夠用apply(),只不過傳參方式略有區別:函數

// 使用 call 實現繼承
var Pet = function (name, age) {
  this.name = name
  this.age = age
}
var Cat = function (name, age, color) {
  Pet.call(this, name, age)
  this.color = color
}
var cat = new Cat('Garfield', 1, 'orange')
console.log(cat.name)  // Garfield
console.log(cat.age)  // 1
console.log(cat.color)  // orange

// 使用 apply 實現繼承:
var Dog = function (name, age, size) {
  Pet.apply(this, [name, age])
  this.size = size
}

當調用一個對象的方法時,方法中 this 指向的是對象自己,用call()改變方法的 this 值:this

var utils = {
  setName: function (name) {
    this.name = name
  }
}
// 使用 call 指定 setName 中的 this 指向本身
var Person = function (name) {
  utils.setName.call(this, name)
}
var p = new Person('John')
console.log(p.name)  // 'John'

apply()

apply()方法除了用來指定函數 this 值,還能夠用來傳遞參數。例如,Math.max()容許傳入多個參數,求它們的最大值,能夠用apply()方法將數組元素做爲參數傳遞給Math.max()方法:prototype

// 使用循環求最大值
var numbers = [1, 0, 0, 8, 6], max = -Infinity
for (var i = 0; i < numbers.length; i++) {
  max = Math.max(numbers[i], max)
}

// 使用 apply 求最大值,代碼更簡潔
var numbers = [1, 0, 0, 8, 6]
max = Math.max.apply(null, numbers)

// 使用 apply 給數組添加元素
var numbers = [1, 0, 0, 8, 6], arr = []
arr.push.apply(arr, numbers)

另外,由於 JS 引擎有參數長度的限制,若是參數數組太長,可能會形成程序異常。因此,對於超長參數數組,應切分紅更小的尺寸,分屢次調用該方法。code

bind()

在給setTimeoutsetInterval傳入函數時,函數中 this 指向的是全局 window 對象。使用bind()方法,從新指定 this 值:對象

var Person = function (name) {
  this.name = name
}
Person.prototype.say = function () {
  setTimeout(function () {
    console.log('My name is ' + this.name)
  }.bind(this))
}
var p = new Person('John')
p.say()  // My name is John

在給 Dom 對象添加監聽函數時,監聽函數做爲 Dom 對象的一個方法,函數中 this 指向的是 Dom 對象。使用bind()方法,從新指定 this 值,使用箭頭函數也能夠達到一樣的效果:繼承

var fakeDom = { a: 'fakeDom' }
var addEvent = function () {
  this.a = 'addEvent'
  fakeDom.onClick = function () {
    console.log(this.a)
  }
  fakeDom.onClickBind = function () {
    console.log(this.a)
  }.bind(this)
  fakeDom.onClickArrow = () => {
    console.log(this.a)
  }
}
addEvent()
fakeDom.onClick()  // 'fakeDom'
fakeDom.onClickBind()  // 'addEvent'
fakeDom.onClickArrow()  // 'addEvent'

使用bind()綁定參數後,新函數只須要傳入剩餘參數:ip

var add = function (a, b) {
  return a + b
}
var add5 = add.bind(null, 5)
console.log(add5(3))  // 8
相關文章
相關標籤/搜索