第一個差異是 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---
---
---
沒有留言:
張貼留言