Effective JavaScript Item 37 認識this的隱式指向,effectiveitem
本系列作為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 18和Item 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-bindingreturn 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的引用,當然使用諸如me,that也是可行的。
在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"]]
總結
- 根據函數的調用方式的不同,this的指向也會不同。
- 使用self,me,that來儲存當前this的引用供其他函數使用。