首先聲明:javascript中每個物件都有一個constructor屬性和一個prototype屬性。 constructor指向物件的建構函數,prototype指向使用建構函式建立的物件實例的原型物件。
function Person(){ } var person = new Person(); Person.prototype = { constructor : Person, name : 'zxs', age : 24, sayName : function(){alert(this.name)} } person.sayName();
在這段程式碼會報錯,sayName() is not defined。根據javascript高階程式設計第二版的解釋,是因為重寫的原型切斷了建構子與最初原型之間的連結。但是我們調整一下上面語句的順序。如下:
function Person(){ } //var person = new Person(); Person.prototype = { constructor : Person, name : 'zxs', age : 24, sayName : function(){alert(this.name)} } /*===========================================================*/ var person = new Person(); /*===========================================================*/ person.sayName(); // zxs alert(person.constructor) //function Object() { [native code]} or function Person() {} 取决与蓝色的语句是否有效
注意上面兩段程式碼等號中間的語句。依照第二段的順序寫程式碼,將會輸出 ”zxs“,這個結果說明在第一種情況下報錯並不能說明是因為切斷了構造函數與原想之間的聯繫引起的。
Person.prototype = {}
本來就是一種定義物件的方法,而且在javascript中每個物件的constructor屬性都預設的指向Object建構函數,這也不難說明重寫原型物件確實切斷了建構子與最初原型之間的聯繫,但並不能說明這種聯繫被切斷之後person就不能訪問到sayName()函數。
現在有這樣的假設:函數的prototype屬性所指向的原型對象,與我們顯示新建的原型對象並不是完全等同的。當我們呼叫函數的時候會創建一個原型對象,此時會先查找當前環境中是否存在其原型對象,如果程式中不存在,就創建一個,如果環境中存在,側查找他們的屬性和方法,最後根據尋找的結果傳回一個原型對象,這個物件中的屬性和方法總是優先使用預設原型中的屬性和方法,也就是建構函式中定義的屬性和方法。當當呼叫的方法或屬性不存在於預設的原型中時,才使用定義在Person.prototype = {} 的屬性和方法。
javascript是解釋性的語言,語句都是順序執行的,在第一段程式碼中,當我們使用new 關鍵字建立新物件的時候,Person.prototype = {} 並沒有執行,也就是說在在目前的執行環境中找不到其中定義的方法和屬性,而建構函數中沒有該方法,所以出錯。就像一個變量,給他賦值的時候程式沒有執行將不能使用。在第二段中環境中已經存在該呼叫的方法,而建構函數的原型物件已經創建完畢,所以可以得到結果。
再看下面的一段程式:
////////////////////////////////////////////////////////////////////////// function Person(){} /*===========================================================*/ var person = new Person(); Person.prototype.name = 'song'; /*===========================================================*/ //Person.prototype.sayName = function(){alert(this.name)}; Person.prototype = { constructor : Person, name : 'zxs', age : 24, sayName : function(){alert(this.name)} } person.sayName(); // error ////////////////////////////////////////////////////////////////////////// function Person(){ } /*var person = new Person();*/ Person.prototype.name = 'song'; /*Person.prototype.sayName = function(){alert(this.name)};*/ Person.prototype = { constructor : Person, name : 'zxs', age : 24, sayName : function(){alert(this.name)} } /*===========================================================*/ var person = new Person(); /*===========================================================*/ person.sayName(); // zxs
從這裡可以看出使用Person.prototype.name = '',的方式不論在什麼地方創建對像都能被訪問,如果同時存在對象字面量和這種方法定義原型對象,將使用後定義的作為最終值。並且對原型物件使用物件字面量定義之後,該定義必須出現在建立物件的語句之前才能存取。
實例不能存取到原型物件中的屬性和方法,不僅是因為重寫原型物件切斷了建構子與最初原型之間的聯繫。
function Person(){ } var person = new Person(); Person.prototype = { //constructor : Person, name : 'zxs', age : 24, sayName : function(){alert(this.name)} } person.sayName();
以上程式碼在實例化物件時建構函式的原型為空,它沒有任何預設屬性以外的屬性。重寫構造函數的原型確實切斷了構造函數與最初原型之間的聯繫。
在使用 new 運算子以後建構函式的原型物件中的屬性和方法已經加入 person物件中。因為以上方法為函數原型添加新屬性和方法不具有動態性,所以person不能存取到新添加的屬性和方法。
重寫原型物件之後,就如同下列程式碼:
var o = { name : 'zxs' } var obj = o; o = {} console.log(o.name);
此時輸出的值是undefined,因為,物件是一個引用類型,「=」是賦值操作符,其運算順序是從右往左。 o={}就是說o的指向已經改變,就是一個空物件。
Person.prototype.mothed = function() {}與Person.prototype={mothed:function(){}}的差異就如同arr = []和arr.push()一樣,前者都是修改自身,後者是完全改變自身。