javascript原型Prototype

javaScript建立對象一文中提到過:用構造函數建立對象存在一個問題即同一構造函數的不一樣實例的相同方法是不同的,因此咱們用原型把構造函數中公共的屬性和方法提取出來進行封裝,達到讓全部實例共享的目的。javascript

接下來進一步介紹javaScript原型。html

1、javaScript原型機制

一、函數與原型的關係

js中建立一個函數,就會自動建立一個prototype屬性,這個屬性指向函數的原型對象,而且原型對象會自動得到一個constructor(構造函數)屬性,指向該函數。java

舉例:之前面的原型模式建立對象爲例說明數組

<script type="text/javascript">
function Person(){

}
Person.prototype.name="lxy";
Person.prototype.age=22;
Person.prototype.job="Software Engineer";
Person.prototype.sayName=function(){
    alert(this.name);
}
     
     var lxy=new Person();
     lxy.sayName();
     var personA=new Person();
     personA.sayName();
     alert(lxy.sayName()==personA.sayName());//true
</script>
View Code

Person對象和Person對象的原型之間的關係以下圖1。瀏覽器

圖1函數、實例與原型的關係(圖來着JS高程)ide

簡單一句話就是:Person.prototype.constructor指向Person,能夠在瀏覽器中測試一下。函數

二、實例與原型的關係

經過構造函數建立一個實例,該實例內部將包含一個屬性(指針),指向構造函數的原型對象。學習

舉例:Person構造函數的實例Person1和Person2的[[Prototype]]屬性都指向Person的原型。如圖1所示。測試

Note:[[Prototype]]鏈接是存在於實例和構造函數的原型之間,而不是存在實例與構造函數之間。this

關於這個指針,ECMA-262中叫[[Prototype]],沒有標準的方式訪問[[Prototype]],但Firefox、Safari和Chrome在每一個對象上都支持一個屬性__protp__,而在其餘實現中,這個屬性對腳本不可見。

三、原型鏈

實例有本身的屬性和方法,而原型封裝了全部實例共享的屬性和方法,那這種共享是經過什麼方式實現的呢?答案是原型鏈。

當要訪問實例的屬性時,解析器會執行一次搜索。首先從實例對象開始,若是在實例中找到了這個屬性,則返回該屬性的值;重點是若是沒有找到的話,則繼續搜索[[Prototype]]指針指向的原型對象,在原型對象中查找該屬性,若是找到,則返回該屬性的值。因此經過這種方式多個實例就能共享保存在原型中的屬性和方法。這也是js的原型鏈。

Note:理解了原型鏈,很天然就能明白幾個問題。

a、給實例添加一個與原型中同名的屬性,就會將其屏蔽。由於搜索原型鏈時在實例中就能找到而後就返回了,根本到不了原型。

b、能夠經過實例爬原型鏈訪問原型中的值,但卻不能經過實例重寫原型中的值。同理。

c、原型的動態性,在原型上新增屬性或方法能當即從實例反應出來。

2、原型相關的方法介紹

一、isPrototypeOf()方法

雖然實例的[[Prototype]]屬性沒法訪問的,咱們能夠經過isPrototypeOf()方法來確認原型和實例之間的關係。這個方法呢是站在原型的角度來檢測原型是否是某個實例的原型。A.isPrototypeOf(B),若是A的B的原型返回true,不然返回false。

舉例:由於Person的兩個實例lxy和personA內部都有一個[[Prototype]]屬性指向Person.prototype。因此isPrototypeOf方法會返回true。

<script type="text/javascript">
    function Person(){

    }
    Person.prototype.name="lxy";
    Person.prototype.age=22;
    Person.prototype.job="Software Engineer";
    Person.prototype.sayName=function(){
        alert(this.name);
    }

    var lxy=new Person();
    var personA=new Person();
    console.log(Person.prototype.isPrototypeOf(lxy)); //true
    console.log(Person.prototype.isPrototypeOf(personA)); //true
</script>

二、Object.getPrototypeOf()方法

這個方法是ECMAScript 5中新增的,返回實例的[[Prototype]]值。

這個方法是很是有用的,由於它是在Object上實現的。因此把任何實例扔給Object,它都能得到實例的原型。

舉例

<script type="text/javascript">
    function Person(){

    }
    Person.prototype.name="lxy";
    Person.prototype.age=22;
    Person.prototype.job="Software Engineer";
    Person.prototype.sayName=function(){
        alert(this.name);
    }

    var lxy=new Person();
    console.log(Object.getPrototypeOf(lxy));
    console.log(Object.getPrototypeOf(lxy)==Person.prototype); //true
    console.log(Object.getPrototypeOf(lxy).age);//22
</script>

結果:

三、hasOwnPrototype()方法

這個方法用於檢測某個屬性是否真正存在於實例中。是返回ture,不然返回false。

就像咱們本身有一些資源和技能,可是也能夠從爹媽那裏獲得一些資源和技能,好比看起來你有套別墅,可是你要知道哪些是你真正屬於你本身的,哪些是爹媽給你的。

舉例:好比我一出生父母就給我個名字lxy,這時候我用hasOwnPrototype()方法檢測這個"name"屬性是不真是個人,就會返回false。

後來我本身改了個名字starof,再用hasOwnPrototype()方法檢測,這時就會返回true。

再後來我不想用這個名字了,我就把它delete掉了,用回了我父母給的名字。這時候再用hasOwnPrototype()方法檢測這個"name"屬性是否是我本身的,就會返回false。

這個一波三折的故事代碼以下:

<script type="text/javascript">
    function Person(){

    }
    Person.prototype.name="lxy";

    var lxy=new Person();
    console.log(lxy.name);
    console.log(lxy.hasOwnProperty("name"));  //false 
    lxy.name="starof";//經過重寫屏蔽原型屬性name,因此這個name就變成了實例屬性
    console.log(lxy.name);
    console.log(lxy.hasOwnProperty("name"));  //true
    delete lxy.name;
    console.log(lxy.name);
    console.log(lxy.hasOwnProperty("name"));  //false
</script>

四、in操做符,for-in循環,Object的keys()和getOwnPropertyNames()

in操做符在經過對象可以訪問屬性時返回true,不管該屬性是實例屬性仍是原型屬性。

for-in循環,返回全部可以經過對象訪問的,可枚舉的屬性。即包括實例屬性也包括原型屬性。

屏蔽了原型中不可枚舉的屬性的實例屬性也會在for-in循環中返回,由於根據規定,全部開發人員定義的屬性都是可枚舉的——只有在IE8及更早版本中例外。

Object.keys()方法,取得實例上全部可枚舉的實例屬性。該方法接收一個實例做爲參數,返回一個包含全部可枚舉屬性的字符串數組。

Object.getOwnPropertyNames()方法,得到全部實例屬性,不管它是否可枚舉。

<script type="text/javascript">
    function Person(){

    }
    Person.prototype.name="lxy";
    Person.prototype.age=22;
    Person.prototype.job="Software Engineer";
    Person.prototype.sayName=function(){
        alert(this.name);
    }

    var lxy=new Person();
    //in
    console.log("name" in lxy);//in實例可訪問的屬性
    //for-in
    for(var prop in lxy){
        console.log(prop);
    }//for-in列出全部可訪問的,可枚舉的屬性
    //Object.keys
    var keys=Object.keys(Person.prototype);
    console.log(keys);//Person的原型的全部可枚舉屬性
    var keys=Object.keys(lxy);
    console.log(keys);//lxy如今沒有實例屬性,因此keys爲空
    lxy.name="lxy";
    var keys=Object.keys(lxy);
    console.log(keys);
    //Object.getOwnPrototypeNames
    console.log(Object.getOwnPropertyNames(lxy));//獲得全部實例屬性
</script>
View Code

3、原型語法

第一種:每添加一個屬性和方法,都直接在原型上加。

<script type="text/javascript">
    function Person(){
    }
    Person.prototype.name="lxy";
    Person.prototype.age=22;
    Person.prototype.job="Software Engineer";
    Person.prototype.sayName=function(){
        alert(this.name);
    }

    var lxy=new Person();
</script>

第二種:對象字面量的方法

<script>
    function Person(){
    }
    Person.prototype={
        name:"lxy",
        age: 22,
        job:"Software Engineer",
        sayName:function(){
            alert(this.name);
        }
    };
    var lxy=new Person();
</script>

第二種語法比較簡單,少寫幾行代碼,可是有一點要注意,字面量形式,徹底重寫了prototype屬性,因此constructor再也不指向Person,而是Object了。

<script>
    function Person(){
    }
    Person.prototype={
        name:"lxy",
        age: 22,
        job:"Software Engineer",
        sayName:function(){
            alert(this.name);
        }
    };
    var lxy=new Person();

    console.log(lxy.constructor==Person);//false
    console.log(lxy.constructor==Object);//true

</script>
View Code

若是constructor很重要,可手動設置爲Person,以下。

<script>
    function Person(){
    }
    Person.prototype={
        constructor:Person,
        name:"lxy",
        age: 22,
        job:"Software Engineer",
        sayName:function(){
            alert(this.name);
        }
    };
    var lxy=new Person();

    console.log(lxy.constructor==Person);//true
    console.log(lxy.constructor==Object);//false
</script>

 可是這樣寫會致使constructor的[[Enumerable]]特性被置爲true。由於開發人員定義的屬性都是可枚舉的。

若是是兼容ECMAScript5的JS引擎可以使用Object.definePrototype。

<script>
    function Person(){
    }
    Person.prototype={
        name:"lxy",
        age: 22,
        job:"Software Engineer",
        sayName:function(){
            alert(this.name);
        }
    };
    //重設構造函數,只適用於ECMAScript5兼容的瀏覽器
    Object.defineProperty(Person.prototype,"constructor",{
        enumerable:false,
    value:Person
    })

    var lxy=new Person();

    console.log(lxy.constructor==Person);//true
    console.log(lxy.constructor==Object);//false
</script>
View Code

 

本文做者starof,因知識自己在變化,做者也在不斷學習成長,文章內容也不定時更新,爲避免誤導讀者,方便追根溯源,請諸位轉載註明出處:http://www.cnblogs.com/starof/p/4904929.html有問題歡迎與我討論,共同進步。

相關文章
相關標籤/搜索