JS中的new操作符與Object.create()
new操作符用於例項化一個物件: var obj = new Base();
,具體過程可解釋如下:
(1) 建立一個新物件,__proto__屬性指向建構函式的prototype,其中constructor指向原建構函式;
(2) 將建構函式的作用域賦給新物件(因此 this 就指向了這個新物件);
(3) 執行建構函式中的程式碼(為這個新物件新增屬性);
(4) 返回新物件。
用程式碼簡單解釋就是:
var obj = {};//建立一個空物件 obj.__proto__ = Base.prototype;//將物件的__proto__物件指向Base的prototype Base.call(obj);//將Base中的this指向obj並執行函式,將屬性新增到obj中
接下來再通過例子來看看new操作符在實際使用中的問題:
var Base = function(){
this.name = 'in';
}
Base.name = 'out';
var obj1 = new Base();
var obj2 = new Base;
以上兩個obj其實返回的是一樣的,呼叫name屬性都會是’in’,但是下面的呢:
var obj3 = new Base.name;//報錯 var obj4 = Base().name;//報錯 var obj5 = new Base().name;//'in'
分別執行上面三個語句:
obj3那行會報錯,原因是Base.name不是一個建構函式,這說明了new操作符的運算優先順序小於.
的優先順序,而且new確實是需要呼叫建構函式,這裡的這個建構函式(constructor)就是Base函式。
obj4那行也會報錯原因是Base()函式執行後未返回任何值,被預設成undefined,在呼叫name屬性自然報錯。
obj5那行不會報錯,對照obj4來看,new Base().name
不報錯應該是先運算new Base()在訪問name屬性,此時new Base()的形式運算優先順序就要高於.
運算。
再來看看下面程式碼:
var Base = function(name){ this.name = name; console.log(name); return {name:''} } var base = new Base('myName')//myName console.log(base.name);//''
上面的建構函式會列印傳入時的name值,最後返回一個name為空的物件,可以看到,在執行var base = new Base('myName')
這條語句時Base函式被執行,列印了‘myName’,但是最後訪問base的name屬性時成了空,這說明base最終指向了{name:’’}物件,可以看它的constructor,指向的是Object,並非Base,這與直接執行Base('myName')
得到的結果相同,不同的是,直接呼叫Base('myName')
,this指向window(瀏覽器中,嚴格模式下會報錯),可以訪問到,window.name為’myName’,而new Base(‘myName’)函式執行后里面的name等屬性就訪問不到了,最終被銷燬。
Object物件提供了一個建立物件例項的方法Object.create(p1,p2),接受兩個引數,p1必選,為要繼承的原型,p2為可選,為包含一個或多個屬性描述符的JavaScript物件。具體過程簡化一下,p2實現為建立例項後可列舉的物件,於是可用以下程式碼簡單展現:
Object._create = function (p1, p2) {
var F = function () {
for(var prop in p2){
this[prop] = p2[prop];
}
};
F.prototype = p1;
return new F();
};
其中p1可為null,終斷原型鏈。