先來一道開胃菜:
首先JS里沒有類.
構造函數是個函數,this指向的是個對象,this蒙上眼睛指也指不到構造函數去.
構造函數的this指向創建的實例對象無疑. 要明白這一點,要先弄明白,用new操作符調用構造函數的時候都發生了什么.
正好我有個答案是講構造函數的, 我這里原樣搬來:
造函數其實和普通函數本質上並無區別,唯一的區別有兩個:
函數首字母大寫,這個區別只是約定俗成的,便於區分。你實在要小寫定義構造函數也完全沒問題,所以這個區別可以忽略。
構造函數的調用需要用new操作符,而普通函數的調用又分很多種,但是都不會用到new操作符。所以,構造函數和普通函數的區別就在這個new操作符里,現在讓我們來好好研究一下這個new操作符。
用new操作符創建對象時發生的事情:
**第一步: 創建一個Object對象實例。
第二步: 將構造函數的執行對象賦給新生成的這個實例。
第三步: 執行構造函數中的代碼
第四步: 返回新生成的對象實例**
注意:原本的構造函數是window對象的方法,如果不用new操作符而直接調用,那么構造函數的執行對象就 是window,即this指向了window。現在用new操作符后,this就指向了新生成的對象。理解這一步至關重要。
執行構造函數中的代碼,看代碼:
function Person(){
this.name = "Tiny Colder";
var age = 22;
window.age = 22;
}
var p = new Person();
alert(p.name)//Tiny Colder;
alert(p.age)//undefined;
alert(window.age)//22;
當用new操作符創建對象時,先創建了一個對象實例,然后執行代碼。所以還在糾結,什么時候構造函數定義的屬性會繼承給實例對象的,都可以這么來看:
var p = new Object();
p.name = "Tiny Colder";
這是普通的創建對象,然后給對象添加屬性的方法。如果每創建一個對象,都需要這么幾行代碼,無疑是糟糕的。這個需求就正好跟這一點對應:new操作符,自動執行構造函數里的代碼。如此我們便可以省掉添加屬性時重復冗余的代碼。那么這些屬性時如何添加到新生成的對象里的呢?
第二個步驟里已經說了:將構造函數的執行對象賦給新生成的這個實例。再結合上一段里說的,自動執行構造函數里的this.name = "Tiny Colder";時,就相當於是執行p.name = "Tiny Colder";而構造函數里的
var age = 22;語句,會執行但是對新生成的對象並無影響。window.age = 22;語句,會執行,且會給window對象添加一個屬性。alert為證。
或許到這里,你已經理解了new操作符的前三步了,重要的三步。但是這個函數是如何返回對象的呢?我們並沒有看到有任何跟return相關的語句。這就是new操作符的最后一步:返回新生成的對象。
如果被調用的函數沒有顯式的 return 表達式(僅限於返回對象),則隱式的會返回 this 對象 - 也就是新創建的對象。
現在來看一下這個代碼:
function Person(){
this.name = "Tiny Colder";
return {};
}
var p = new Person();
alert(p.name)//undefined;
一個對象就這么被創建出來了。
實際上,
var p = new Person();
和
var p = new Object();
Person.apply(p);
是一樣的效果。
簡單的總結:
顯式的返回以下值:undefined, null, boolean, number等基礎類型,並不會代替 new 式調用的默認行為。
但顯式返回以下值:{},[],RegExp, Date, Function,均會代替 new 調用的默認返回值 this.
大家都看到了,后者,全都是 對象,是復雜類型。
如果函數體是嚴格模式,則不會綁定 this 至全局對象,如:
var
a =
function
(){
'use strict'
;
this
.b =
'b'
;
return
/111/g};
a();
// 直接報錯