原型對象隨筆

prototype、proto和constructor 三者之間的關係

首先來看三個的各自含義函數

  1. prototype
    構造函數有一個prototype屬性,指向實例對象的原型對象。經過同一個構造函數實例化的多個對象具備相同的原型對象
  2. constructor
    原型對象有一個constructor屬性,指向該原型對象對應的構造函數
  3. proto
    實例對象有一個proto屬性,指向該實例對象對應的原型對象,參照構造函數prototype屬性來的。

原型對象

基本理解

不管何時,只要建立函數,就會根據規則爲該函數建立一個 prototype屬性,這個屬性指向函數的原型對象。在默認狀況下,全部的原型對象,都會自動得到一個constructor屬性。這個屬性包含一個指向 prototype 屬性所在函數的指針。
在建立了自定義構造函數以後,其原型對象默認只會取得 constructor 屬性和從Object繼承一些方法。
當調用構造函數建立一個新實例後,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象
例如:prototype

// 定義函數.  會默認爲該函數建立prototype屬性
function Person(){}
// 建立實例對象, 實例對象會有默認的proto 屬性 指向原型對象
var p = new Person()

圖示:
指針

實際狀況

  1. 原型對象也是實例對象,實際上,任何對象均可以看作是經過Object()構造函數的new操做實例化的對象。
  2. 函數也是對象,只不過是具備特殊功能的對象而已。任何函數均可以看作是經過Function()構造函數的new操做實例化的結果。
  3. Object.prototype的原型對象是null (注意不是Object的原型對象)
  4. 特殊就是Fuction函數了, 全部的函數均可以當作是構造函數Function()的new操做的實例化對象。那麼,Function能夠當作是調用其自身的new操做的實例化的結果
function foo(){}
f1 = new foo;

圖示:
code

注意
原型對象Function.prototype的constructor屬性指向構造函數Function();實例對象Object和Foo自己沒有constructor屬性,須要繼承原型對象Function.prototype的constructor屬性。對象

function Foo(){};
var f1 = new Foo;
console.log(Function.prototype.constructor === Function);//true
console.log(Foo.constructor === Function);//true
console.log(Foo.hasOwnProperty('constructor'));//false
console.log(Object.constructor === Function);//true
console.log(Object.hasOwnProperty('constructor'));//false

原型鏈

其實,在對象去查找一個屬性的時候,會如今對象自己上查找,若是不存在,則在其原型對象上查找,一直查找到null爲止。這種經過原型層層鏈接起來的就稱爲成了原型鏈

注意: 原型對象能夠被修改,能夠被重寫。注意修改和重寫的區別:blog

修改原型對象

function Foo(){}
foo.prototype.name = 'fun'
var f1 = new Foo;
alert(f1.name); // fun  能夠訪問到原型對象上的屬性

字面量改變

function Foo(){}
Foo.prototype = {
    name:"foo"
}
var f1 = new Foo;
alert(f1.name); // fun  能夠訪問到原型對象上的屬性

注意當以這種字面量來建立,雖然結果是正確的可是 constructor 並不在指向 Foo 函數了。 而如今咱們徹底重寫了prototype 對象了。(就是改變了Foo.prototype的指向一個新建立的對象)所以,constructor屬性指向新對象的constructor屬性(新對象由Objecct建立出來的,所以指向了Object構造函數)繼承

console.log(f1 instanceof Objecct) // true
console.log(f1 instanceof Foo) // true
console.log(f1.constructor == Foo) // false
console.log(f1.constructor == Object) // true

因此一般會在改變prototype時指定constructor屬相原型鏈

unction Foo(){}
Foo.prototype = {
    constructor: Foo,
    name:"foo"
}
var f1 = new Foo;
alert(f1.name); // fun  能夠訪問到原型對象上的屬性

動態改變

其實原型對象和實例對象之間鏈接就一個指針,而非副本,這樣鬆散的鏈接關係, 就可以讓咱們在原型對象作的任何操做,在實例上當即反應出來。原型

function Foo(){}
f1 = new Foo;
// 這裏改變對象自己, 此時 實例對象和構造函數的原型對象是同一地址
Foo.prototype.sayHello = function(){
    console.log("hello")
}
f1.sayHello()  // 'hello'

請記住,實例中的prototype指向原型對象,在建立時就構造函數已經決定了。 不會隨着構造函數的原型對象指針的改變而發生改變。io

function Foo(){}
f1 = new Foo;
// 這裏改變了指向。 此時 實例對象和構造函數的原型對象不在指向同一地址了
Foo.prototype = {
    constructor: Foo,
    sayHello : function(){
        console.log("hello")
    }
}
f1.sayHello()  // erro

重寫原型對象切斷了現有原型與任何以前已經存在的對象實例之間的聯繫,以前任何實例對象它們的引用仍然是最初的原型。而現有構造函數,以及以後建立的實例對象會指向重寫以後的原型對象。

相關文章
相關標籤/搜索