第一個差異是 this 是誰?
function WhoIsThis() {
console.log(window == this);
console.log(this instanceof WhoIsThis);
}
如果是用 function 方式呼叫,會得到 true 與 false,表示以 function 方式呼叫時,「主體」是 window,這當然是以瀏覽器為執行環境。WhoIsThis(); // true, false但是改用 new 方式呼叫,會得到相反地結果,false 與 true,表示以 new 方式呼叫時,「主體」是 function 本身建立的物件,也就是 WhoIsThis 的 instance。
new WhoIsThis(); // false, true
第二個差異是 return 什麼?
function ReturnWhat() {
}
console.log(ReturnWhat()); // undefined
console.log(new ReturnWhat()); //ReturnWhat {}
以 function 方式呼叫時,由於沒有定義 return,所以回傳的當然是 undefined,但是以 new 方式呼叫時,即使沒有定義 return,卻還是回傳了 ReturnWhat 的 instance。這是由於 Javascript 在 new 方式呼叫時會「自動」加上 return this。
疑?自動?那麼來搗蛋一下,如果有定義 return 但是用 new 方式呼叫會發生什麼事?
function ReturnMe() {
return new Date();
}
console.log(ReturnMe());
console.log(new ReturnMe());
以 function 方式呼叫時得到當下時間,但是以 new 方式呼叫時,也是得到當下時間,疑?回傳的到底是 ReturnMe 還是 Date 的 instance?console.log((new ReturnMe()) instanceof ReturnMe); console.log((new ReturnMe()) instanceof Date);結果是 false 與 true,得到的是 Date 的 instance,該怎麼解釋呢?其實很簡單,用 new 呼叫沒有定義 return 的 function 時,Javascript 會自動加上 return this,但是用 new 呼叫有定義 return 的 function 時,Javascript 什麼都不做,就照實 return,所以上面的例子會得到 Date 的 instance。
這裡要注意的是,用 new 呼叫得到回傳的 instance 可能和所呼叫的 function 完全沒關係,完全視 return 而定,這裡和 Java 很不一樣。
以上兩個差異是我目前所知的差別,接下來就專心來看看 new 吧。
如果以 new 的思維來定義 function(也就是在 function 裡使用 this 儲存變數),但是卻被用 function 的方式呼叫了,會發生什麼事?
function Tree(title) {
this.title = title; // 將傳進來的 title 存在 Tree instance 裡
}
var t = new Tree('Book'); // 正確的使用 new 方式呼叫
console.log(t.title); // Book
var t = Tree('Video'); // 使用錯誤的 function 方式呼叫
console.log(t.title); // error,因為 Tree 沒有定義 return,用 function 方式呼叫不會自動加上 return this,所以 t 是 undefined。
那 Video 跑去哪兒呢?console.log(this.title); // Video console.log(window.title); // Video在 window 上,原因是前面提過的第一點差異,function 的主體是 window,這可能是很危險的情況,因為如果 window 剛好有 title 變數的話,就會被覆蓋掉,title 死的不明不白,還很難除錯。
自動校正錯誤的 new function 呼叫
那要如何避免誤用呢?如果是自己寫得自己用,還誤用就自己該死,但是如果是要公開的 API 呢?有兩種方法可以避免 new function 被誤用,兩種的概念都是用 instanceof 去判斷「主體」是誰?然後用不同的方式去修正錯誤的使用情形。
第一種是轉呼叫正確的 new function。
function Tree(title) {
if (!(this instanceof Tree)) {
return new Tree(title); // 發現主體不對,就轉呼叫正確的方式
}
this.title = title;
}
var t = new Tree('Book');
console.log(t.title); // Book
var t = Tree('Video');
console.log(t.title); // Video
第二種是利用 ES5 的 Object.create() 來建立正確的 instance。
function Tree(title) {
var that;
if (this instanceof Tree) {
that = this; // 正確的呼叫方式
}
else {
that = Object.create(Tree.prototype); // 使用 ES5 的 Object.create() 來建立正確的 instance。
}
that.title = title;
return that; // 記得最後要回傳自訂的 instance
}
var t = new Tree('Book');
console.log(t.title); // Book
var t = Tree('Video');
console.log(t.title); // Video
------
---
沒有留言:
張貼留言