What's "new" in JavaScript?

老調重彈,在JavaScript中,遇到new操作符時執行了什麼操作?
也就是說new到底創造了什麼,過程是怎樣的?
能否爲Function對象增加一個原型方法,模擬new的操作?

Function.prototype.__new__ = function(){
var emptyFunction = function(){};
emptyFunction.prototype = this.prototype;
var newObject = new emptyFunction();
newObject.__proto__ = this.prototype;
newObject.constructor = this.prototype.constructor;
var returnedObject = this.apply(newObject, arguments);
return (typeof returnedObject === 'object' && returnedObject) || newObject;
};

多數代碼來自老道的[url=http://book.douban.com/subject/2994925/]<<JavaScript: the Good Parts>>[/url]
第5,6行是額外的一點補充:
第5行:所有JS對象都有__proto__屬性,指向 -- 構造器的prototype. (很多瀏覽器是可以訪問到__proto__的,ie不行.)

第6行:對象的constructor指向不一定是構造器,而是構造器的prototype對象的構造器,用一段代碼說明:

function Person(name){
this.name = name;
}
Person.prototype = {
getName:function(){
return this.name;
}
}
var p = new Person("a");
alert(p.constructor === Person); //false
alert(p.constructor === Object); //true 從第4行看到Person.prototype.constructor === Object 即Person.prototype = new Object(); Person.prototype.getName = function(){..}

這樣的constructor指向與constructor(構造器)的字面意義不搭,並不是我們期待的,往往會造成困擾.所以常見OO支持(YUI Kissy)通過顯示的重寫constructor維持對象的constructor的指向其構造器.
但在對new的模擬中,需要保持與瀏覽器中new的做法一致:newObject.__proto__ = this.prototype;

第2~4行:首先獲取一個空的Function實例,獲取構造器的prototype的引用,這就濾掉了構造器內定義的特有屬性和特權方法.首要保障prototype的純正血統.再用一段代碼說明

function Person(name,age){
this.name = name;
this.age = age;
this.getAge = function(){//特權方法(privileged methods) 避免其被子類繼承
return this.age
}
}
Person.prototype.getName = function(){ //原型方法 需要被其子類集成
return this.name;
}//BTW: 通過這種方式爲Person擴充原型方法,沒有通過"="操作符爲Person.prototype賦予新的對象(= new Objecct) 所以Person.prototype.constructor沒有出現混亂.


第7行:在保證了新創建對象的prototype的無增無損後,這一行代碼保障特有屬性和特權方法是新創建對象中依然是特有的,而且特有屬性和特權方法和傳入構造器的參數相關,通過apply方法將構造函數功能和傳入參數作用於新構建的對象之上.將構造函數的返回值放入returnedObject暫存.

第8行:新的對象已經創建好,如果returnedObject是非空對象則返回returnedObject,否則返回新創建的對象.returnedObject的這個判斷也不難理解,我們可以利用這個特性創建單件(Singleton).比如這樣寫:

function Singleton(){
if(Singleton.inst){
return Singleton.inst;
}
Singleton.inst = this;
}
var s1 = new Singleton();
var s2 = new Singleton();
alert(s1===s2);//true


還有一個困擾,一旦Singleton前面沒有new,Singleton.inst將指向window.
其實所有類都需要判斷是否是通過new進入的函數體.判斷的方法不復雜,可以參照YUI使用instanceof判斷:

function Singleton(){
if(this instanceof arguments.callee){ //如果沒有通過new進入函數體,這裏的this會指向window
return new arguments.callee;
}
if(Singleton.inst){
return Singleton.inst;
}
Singleton.inst = this;
}
var s1 = new Singleton();
var s2 = Singleton();
alert(s1===s2);//true

這就是爲Function增加原型方法__new__來做對new操作符的模擬,可能還不完善,但對理解一些JS內部機制有幫助.要是有錯誤,請一定幫忙指出~
最後,推薦看一下[url=http://dmitrysoshnikov.com/ecmascript/javascript-the-core/]ECMA-262-3 in Detail[/url]系列文章.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章