1. 程式人生 > >Effective JavaScript Item 37 認識this的隱式指向

Effective JavaScript Item 37 認識this的隱式指向

讀書 筆記 rim prototype 介紹 ade ava split fun

本系列作為Effective JavaScript的讀書筆記。

CSV數據通常都會被某種分隔符進行分隔。所以在實現CSV Reader時,須要支持不同的分隔符。那麽,非常自然的一種實現就是將分隔符作為構造函數的參數。


function CSVReader(separators) {
	this.separators = separators || [","];
	this.regexp =
		new RegExp(this.separators.map(function(sep) {
			return "\\" + sep[0];
		}).join("|"));
}

對於CSV Reader而言。它的工作原理是首先將讀入的字符串依據換行符切分成為一個個的行。然後對每行依據分隔符進行切分成為一個個的單元。

所以,能夠使用map方法進行實現:


CSVReader.prototype.read = function(str) {
	var lines = str.trim().split(/\n/);
	return lines.map(function(line) {
		return line.split(this.regexp); // wrong this!
	});
};
var reader = new CSVReader();
reader.read("a,b,c\nd,e,f\n"); // [["a,b,c"], ["d,e,f"]], wrong result

但是上述代碼中有一個錯誤:傳入到map函數中的回調函數的this指向有問題。即當中的this.regexp並不能正確的引用到CSVReader實例的regexp屬性。因此,最後得到的結果也就是不對的了。

對於這個樣例。在map的回調函數中this指向的實際上是全局對象window。關於this在各種場景下的指向,在Item 18Item 25中進行了介紹。

為了克服this的指向問題,map函數提供了第二個參數用來指定在其回調函數中this的指向:


CSVReader.prototype.read = function(str) {
	var lines = str.trim().split(/\n/);
	return lines.map(function(line) {
		return line.split(this.regexp);
	}, this); // forward outer this-binding to callback
};
var reader = new CSVReader();
reader.read("a,b,c\nd,e,f\n");
// [["a","b","c"], ["d","e","f"]]

可是,並非全部的函數都如map考慮的這麽周全。假設map函數不能接受第二個參數作為this的指向。能夠使用以下的方法:


CSVReader.prototype.read = function(str) {
	var lines = str.trim().split(/\n/);
	var self = this; // save a reference to outer this-binding
	return lines.map(function(line) {
		return line.split(self.regexp); // use outer this
	});
};
var reader = new CSVReader();
reader.read("a,b,c\nd,e,f\n");
// [["a","b","c"], ["d","e","f"]]

這樣的方法將this的引用保存到了另外一個變量中,然後利用閉包的特性在map的回調函數中對它進行訪問。一般會使用變量名self來保存this的引用。當然使用諸如methat也是可行的。

ES5環境中。還能夠借助於函數的bind方法來綁定this的指向(Item 25中,對該方法進行了介紹)


CSVReader.prototype.read = function(str) {
	var lines = str.trim().split(/\n/);
	return lines.map(function(line) {
		return line.split(this.regexp);
	}.bind(this)); // bind to outer this-binding
};
var reader = new CSVReader();
reader.read("a,b,c\nd,e,f\n");
// [["a","b","c"], ["d","e","f"]]

總結

  1. 依據函數的調用方式的不同,this的指向也會不同。
  2. 使用selfmethat來保存當前this的引用供其它函數使用。


Effective JavaScript Item 37 認識this的隱式指向