關於apply,call,bind的理解
在javascript中, call和apply都是為了改變某個函式執行時的上下文而存在的,換句話說,就是為了改變函式體內部的this的指向
javascript的一大特點是,函式存在[定義時上下文]和[執行時上下文]以及[上下文是可以改變的]這樣的概念
function fruits() { } fruits.prototype = { color: 'red', say: function () { console.log('My color is ' + this.color) } } var apple = new fruits apple.say() // My color is red
但是如果我們有一個物件banana = { color: “yellow” }, 我們不想對它重新定義 say 方法,那麼我們可以通過 call 或 apply 用 apple 的 say 方法:
var banana = {
color: 'yellow'
}
apple.say.call(banana) // My color is yellow
apple.say.apply(banana) // My color is yellow
所以, 可以看出 call 和 apply 是為了動態改變 this 而出現的, 當一個 object 沒有某個方法( 本栗子中banana沒有say方法), 但是其他的有( 本栗子中apple有say方法), 我們可以藉助call或apply用其它物件的方法來操作。
apply、 call 區別
對於 apply、 call 二者而言, 作用完全一樣, 只是接受引數的方式不太一樣。apply接受一個數組或者一個類陣列,而call接受一個個單獨的引數。
例子:
1) 陣列之間追加
var array1 = [12, 'foo', { name: 'joe' }, -123] var array2 = ['Doe', 123, 100] Array.prototype.push.apply(array1, array2) console.log('array1: ', array1) console.log('array2: ', array2)
2)獲取陣列中的最大值和最小值
var numbers = [1, 3, 4, 9, 45, 28]
var min = Math.min.apply(Math, numbers)
var max = Math.max.apply(Math, numbers)
console.log('最大值: ', max)
console.log('最小值: ', min)
3) 驗證是否是陣列
function isArray(obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
4)寫一個log方法來代替console.log
function log() {
return console.log.apply(console, arguments)
}
bind 繫結函式, bind()最簡單的用法是建立一個函式,使這個函式不論怎麼呼叫都有同樣的this值。
num = 9
var mymodule = {
num: 81,
getNum: function () {
console.log(this.num)
}
}
mymodule.getNum()
var getNum = mymodule.getNum
getNum()
getNum.bind(mymodule)()
bind()方法與apply與call很相似,也是可以改變函式體內this的指向。
MDN的解釋是:bind()方法會建立一個新函式,稱為繫結函式,當呼叫這個繫結函式時,繫結函式會以建立它時傳入bind()方法的第一個引數作為this,傳入bind()方法的第二個以及以後的引數加上繫結函式執行地本身的引數按照順序作為原函式的引數來呼叫原函式
var foo = {
bar: 1,
eventBind: function () {
var _this = this
return function () {
console.log(_this.bar)
}
}
}
foo.eventBind()()
// 使用bind來解決
var foo = {
bar: 2,
eventBind: function () {
return function () {
console.log(this.bar)
}.bind(this)
}
}
foo.eventBind()()
偏函式
這是一個很好的特性,使用bind()我們設定函式的預定義引數,然後呼叫時候傳入其他引數即可:
function list() {
return Array.prototype.slice.call(arguments)
}
var list1 = list(1, 2, 3) // [1, 2, 3]
var leadingList = list.bind(null, 10)
var list2 = leadingList(2, 3, 4)
console.log(list2)
function Bloomer() {
this.petalCount = Math.ceil(Math.random() * 12 ) + 1
}
Bloomer.prototype.bloom = function () {
setTimeout(this.declare.bind(this), 100);
}
Bloomer.prototype.declare = function () {
console.log(`我有${this.petalCount}朵花瓣`)
}
var bloo = new Bloomer()
bloo.bloom()
區別是,當你希望改變上下文環境之後並非立即執行,而是回撥執行的時候,使用 bind() 方法。而 apply / call 則會立即執行函式。
再總結一下:
apply 、 call 、bind 三者都是用來改變函式的this物件的指向的;
apply 、 call 、bind 三者第一個引數都是this要指向的物件,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用後續引數傳參;
bind 是返回對應函式,便於稍後呼叫;apply 、call 則是立即呼叫 。
var obj = {
x: 81
}
var foo = {
getX: function () {
return this.x
}
}
console.log(foo.getX.apply(obj))
console.log(foo.getX.call(obj))
console.log(foo.getX.bind(obj)())