JS構造函數new的過程


造函數其實和普通函數本質上並無區別,唯一的區別有兩個:

函數首字母大寫,這個區別只是約定俗成的,便於區分。你實在要小寫定義構造函數也完全沒問題,所以這個區別可以忽略。
構造函數的調用需要用new操作符,而普通函數的調用又分很多種,但是都不會用到new操作符。所以,構造函數和普通函數的區別就在這個new操作符里,現在讓我們來好好研究一下這個new操作符。

用new操作符創建對象時發生的事情:

(1)創建一個新對象;

(2)將構造函數的作用域賦給新對象(因此this就指向了這個新對象);

(3)執行構造函數中的代碼(為這個新對象添加屬性和方法);

(4)返回新對象;

通過new操作符后跟構造函數的方式創建的對象(實例),這個對象有一個constructor(構造函數)屬性,該屬性指向構造函數Person。

創建的對象,毫無疑問是Person的實例,同時也是Object的實例;所有對象皆繼承自Object。

構造函數是定義在Global對象中的,在瀏覽器中,即為window對象。

注意:原本的構造函數是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);
是一樣的效果。

構造函數也是函數

任何函數,只要通過new操作符來調用,那么它就可以作為構造函數;任何函數,如果不通過new操作符來調用,那它與普通函數並無區別。

(1)當做構造函數調用

var person = new Person("CC",23);

(2)當做普通函數使用

Person("CC",23);    //添加到window對象
console.log(window.name);    //"CC"
console.log(window.age);    //23

(3)在另一個對象的作用域中調用

var person = new Object();
Person.call(person,"CC",23);
console.log(person.name);    //"CC"
console.log(person.age);    //23

構造函數的問題

使用構造函數的主要問題,就是每個方法都要在每個實例上重新創建一次。

var person1 = new Person("CC",23);
var person2 = new Person("VV",32);

兩個實例都有sayName()方法,但是兩個方法不是同一Function的實例,也就是說,兩個實例上的同名函數是不相等的。

console.log(person1.sayName == person2.sayName);    //false

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM