Javascript 中嘅 this, call 同 apply

Javascript lexical functional scope,姐係variable嘅lifespan係一個個function咁計,每當有一個新function,就有一個新嘅scope。
另一個javascript嘅特點係first-class function姐係function可以係一個variable type,個function可以pass入第2個function,或姐一個function return另一個function.E一個特點令javascript好多時都會用到callback function E個pattern.

以上2個特點加埋一齊就會出現一D麻煩。

例如callback function入邊個this有時會唔小心指左去第2個scope。

1
2
3
4
5
6
7
8
9
10
app.name = 'my app'
app.getUsers = function( callback ){
$.get( '/users/', function( data ){
callback && callback( this, data );
});
};

app.getUsers(function countUser(data){
console.log('There are ' + data.length + ' users using ' + this.name);
})

以上果段code個this就會指左去jQuery個scope。當callback function入邊有用到this果時,咁就會有D unexpected behaviour出現。以上例子中嘅this.name會指左去$.name,而唔係app.name

有D人可能會諗

Callback果度用app.name唔好用this.name咪得囉。

E個solution有2個缺點

  1. hardcode caller個variable name會少左flexibility。
    有時後個callback可以用係唔同地方,但係一要hardcode個caller variable入去,個callback就變左context specific,廢左武功,冇得reuse。咁當然可以用D wrapper 去fix E 個problem,但係就要寫好多boilerplate code,搞到段code好冗長。
  2. abstract唔到個function interface做一個API
    其實一個function已經係一個abstraction,當個user 入個callback去call你個function,佢suppose唔洗知你個function點implement。以上文為例,人地跟本唔知你用jQuery咁樣implement,唔知道用this會有bug。咁樣寫getUsers係做唔到一個public api。

咁點樣寫先啱?個問題主要係function個scope冇清淅define,只要我地用一D native javascript function指定返個this就得。例如:

1
2
3
4
5
6
app.getUsers = function( callback ){
var self = this;
$.get( '/users/', function( data ){
callback && callback.call( self, data );
});
};

callback.call( self, data ) 第一個argument就係指定左個this係做self,姐係call緊self.callback(data)
又或者可以用callback.apply( self, [data] ),姐係call緊self.callback(data[0], data[1], data[2] ...)
都可以用(callback.bind(self))(data)bind都係指定個this,但係佢唔會直接行個function,而係return一個bind左scope嘅function。

總結

  • 當implement callback pattern,要記住留意this嘅binding。
  • 當要借其他object D method用,最好用call,apply或者bind,因為我地唔知人地點implement。
  • 如果只需要借人地D method用一次,直接用callapply就得。如果唔係馬上call或者要用幾次,最好用bind create一個新function先。

參考