深究JavaScript——原型:prototype、constructor、__proto__

prototype、constructor、__proto__

  prototype是站在構造函數的角度討論原型對象的,用來實現基於原型的繼承與屬性的共享。__proto__是站在實例對象的角度討論原型對象,構成原型鏈,一樣用於實現基於原型的繼承。css

function Person(){}
var p = new Person();
Person.prototype

Person.prototype

Person.constructor

Person.constructor

p.prototype

p.prototype

p.constructor

p.constructor

p.__proto__

p.__proto__

  • 經過new構造函數實例化獲得對象。
  • prototype是構造函數的屬性,而不是實例對象的屬性,指向Person.prototype原型對象。
  • 實例對象含有一個Constructor屬性指向該對象的構造器。
  • p對象有個__proto__內部屬性指向Person.prototype。
  • p.__proto__、p.constructor.prototype、Person.prototype指向Person的原型對象

  每一個構造函數都有一個phototype屬性(經過Function.prototype.bind方法構造出來的函數以及Object.create(null)例外,沒有prototype屬性),這個屬性是一個指針,指向一個包含特定類型的全部實例共享屬性和方法的對象。經過prototype對象能夠返回對象的原型對象的引用。html

  每一個實例對象都有一個constructor屬性指向prototype屬性所在函數的指針。經過這個constructor(構造函數),還能夠繼續爲原型對象添加其餘屬性和方法。 html5

  每一個實例對象都有一個內部屬性[[prototype]],在ES5以前沒有標準的方法訪問這個內置屬性,可是大多數瀏覽器都支持經過__proto__指針來訪問。__proto__指向實例該對象的構造器的原型對象(Object.prototype 這個對象是個例外,它的__proto__值爲null)。這個鏈接存在於實例與構造函數的原型對象(而不是構造函數)之間,也就是說實例對象與構造函數沒有直接關係。而且,實例並不包含屬性與方法,實例之因此可以調用原型上的方法,是依賴於查找對象屬性的過程來實現的。雖然沒法訪問到__proto__,能夠經過isPrototypeOf()方法來肯定對象之間是否存在這種關係,若是__proto__指向調用該方法的對象的prototype,返回true。css3

console.log(Person.prototype.isPrototypeOf(p)); //true;

原型解構圖

  每當代碼讀取某個對象屬性時,首先從對象實例開始,若是找到給定名字屬性,返回該值;若是沒有找到,繼續搜索指針指向的原型對象。雖然能經過實例訪問原型中的值,但不能經過對象實例重寫原型中的值。git

  當爲對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性;換句話說,添加這個屬性只會阻止咱們訪問原型中的那個屬性,但不會修改那個屬性。即便將這個屬性設置爲 null,也只會在實例中設置這個屬性,而不會恢復其指向原型的鏈接。不過,使用 delete 操做符則能夠徹底刪除實例屬性,從而讓咱們可以從新訪問原型中的屬性。github

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){
    console.log("My name is "+this.name);
}
var p = new Person();
p.name = "赫連小妖"
p.say();  //My name is 赫連小妖
delete p.name;
p.say();  //My name is staven

  使用 hasOwnProperty()方法只在給定屬性存在於對象實例中時,纔會返回 true;能夠檢測一個屬性是存在於實例中,仍是存在於原型中。segmentfault

  for(x in xxx)既能夠檢測實例屬性,也能夠檢測自定義屬性。數組

  hasOwnProperty()爲true,屬性存在在對象實例中;hasOwnProperty()爲false,(x in xxx)爲true,屬性存在在對象的構造器原型中。瀏覽器

function Person(){}
Person.prototype.name = "staven";
var p = new Person();
console.log(p.hasOwnProperty('name'));    //false
console.log(('name' in p));                //true
p.name = "赫連小妖";
console.log(p.hasOwnProperty('name'));    //true
console.log(('name' in p));                //true

  ECMAScript 5 的 Object.keys()方法接收一個對象做爲參數,返回一個包含全部可枚舉屬性的字符串數組。函數

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){};
console.log(Object.keys(Person.prototype));        //["name", "say"]
var p = new Person();
p.sex = 'man';
p.character = "handsome";
console.log(Object.keys(p));    //["sex", "character"]

  不管它是否可枚舉,均可以使用 Object.getOwnPropertyNames()方法。

function Person(){}
Person.prototype.name = "staven";
Person.prototype.say = function(){};
console.log(Object.getOwnPropertyNames(Person.prototype));        //["constructor", "name", "say"]
var p = new Person();
p.sex = 'man';
p.character = "handsome";
console.log(Object.getOwnPropertyNames(p));    //["sex", "character"]

使用字面量賦值原型對象

Person.prototype = {
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};

  此時constructor屬性再也不指向Person。儘管 instanceof操做符還能返回正確的結果,但經過 constructor 已經沒法肯定對象的類型了。

function Person(){}
Person.prototype = {
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};
var p = new Person();
console.log(p instanceof Object);        //true
console.log(p instanceof Person);        //true
console.log(p.constructor == Person);    //false
console.log(p.constructor == Object);    //true

  不過能夠從新設置constructor屬性,確保經過該屬性的原有性。

function Person(){}
Person.prototype = {
    constructor : Person,
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};

原型的動態性

  隨時能夠爲原型添加屬性和方法,而且可以當即在全部對象實例中反映出來,可是若是重寫了整個原型對象,狀況就不一樣了。調用構造函數時會爲實例添加一個[[Prototype]]指針,把原型修改成另外一個對象,就至關於切斷了構造函數與最初原型之間的聯繫。

function Person(){}
var p = new Person();
Person.prototype = {
    constructor : Person,
    name : "staven",
    say : function(){
        console.log("My name is "+this.name);
    }
};
p.say();    //Uncaught TypeError: p.say is not a function

原型對象的缺點

  省略了爲構造函數傳遞初始化參數,結果全部實例在默認狀況下都將取得相同的屬性值。
  因爲原型的屬性共享,若在實例上添加一個同名的包含引用類型值得的屬性,其餘實例該屬性也會被改變。

function Person(){}
Person.prototype = {
    books:["html5","css3","js"]
}
var p1 = new Person();
var p2 = new Person();
p1.books.pop();
console.log(p1.books);        //["html5", "css3"]
console.log(p2.books);        //["html5", "css3"]

☞☞☞深究JavaScript系列☜☜☜

相關文章
相關標籤/搜索