JavaScript學習總結——原型

什麼是原型

首先,原型是一個對象。並且全部的對象都有一個原型(有一種例外:當把對象的原型設爲null時),而且任何對象均可以成爲一個原型。javascript

當咱們定義一個對象時 var a = new Object(); 默認的原型在原型鏈的頂端。java

原型有什麼好處

原型最大的好處體如今它的 共享 的特性。全部原型對象的實例對象共享它所包含的屬性和方法。因此咱們經常使用利用原型來建立對象,也就是 原型模式數組

原型模式

原型模式 是一種用來建立多個實例對象的方法,咱們經常把它和 構造函數結合起來用來建立特定類型的對象。函數

咱們建立的每個函數都有一個 prototype 屬性,這個屬性是一個指針,指向一個對象。這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。這個對象就是實際上經過調用構造函數而建立的 實例對象 的原型對象。看代碼:this

// 構造函數
function Person(){};

Person.prototype.name = "darko";
Person.prototype.age = 21;
Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
person1.sayName();  // "darko"

var person2 = new Person(); 
person2.sayName();  // "darko"

咱們將全部的屬性和sayName()方法添加到了構造函數Personprototype屬性中,構造函數成了空函數。可是即使如此,咱們也能夠經過調用構造函數來建立新對象,並且新對象還會具備相同的屬性和方法。spa

構造函數,實例對象和原型對象的關係

實例對象就是經過構造函數創造的,默認擁有一個constructor屬性指向其構造函數。prototype

原型對象就是構造函數的屬性prototype指向的那個對象,同時也是基於構造函數生成的實例對象的原型對象。在默認狀況下,全部的原型對象都會自動得到一個constructor屬性,這個屬性是一個指針,指向其構造函數。指針

實例對象能夠訪問原型對象上的屬性和方法。在實例對象的內部有一個屬性(內部屬性)[[Prototype]]指向其原型對象。有一種非標準方法__proto__訪問[[Prototype]]code

在上面的例子中person1person2就是實例對象,構造函數爲Person,原型對象爲Person.prototype對象

來,看個栗子(仍是上面那段代碼):

alert(person1.constructor === Person);  // true

alert(Person.prototype.constructor === Person);  // true

alerta(person1.__proto__ === Person.prototype); // true

來看個圖你就什麼都懂了:
圖片描述

理解prototype,getPrototypeOf和 proto 之間的不一樣

prototype是函數的一個默認屬性,只有函數對象纔有

Object.getPrototypeOf()方法用來返回實例對象內部屬性[[prototype]]的值。這是ES5中定義的用來獲取原型對象的標準方法。

__proto__屬性是獲取原型對象的非標準方法(IE不支持)
看個栗子(仍是上面那段代碼):

alert(Object.getPrototypeOf(person1) === Person.prototype); // true
alert(Object.getPrototypeOf(person1).name); // "darko"

alert(person1.__proto__ === Person.prototype);    // true
alert(person1.__proto__.name);  // "darko"

原型模式下的對象

每次查找對象的每一個屬性,都是一次搜索。搜索從實例對象自己開始,若是在實例對象中找到,中止查找,返回值。若是沒有則繼續搜索實例對象指向的原型對象。

若實例對象中屬性和其指向的原型對象的屬性重名,實例對象中的屬性屏蔽原型對象中的那個屬性。
舉個栗子:

function Person(){};

Person.prototype.name = "darko";
Person.prototype.age = 21;
Person.prototype.sayName = function(){
    alert(this.name);
}

var person1 = new Person();
var person2 = new Person();

person1.name = "leon";
person1.sayName();   // "leon",來自實例
person2.sayName()   // "darko",來自原型

delete person1.name;
person1.sayName();  // "darko",來自原型

能夠利用hasOwnProperty()方法判斷一個屬性是位於實例中,仍是原型中。只有在屬性來自實例中時,纔會返回true。一般和in操做符配合使用。

// 接上
alert("name" in person1);   // true
alert(person1.hasOwnProperty("name"));  // false

原生對象的原型

全部的原生引用類型都在其原構造函數的原型上定義了方法,例如,Array.prototype.sort()方法,正是因爲原型的共享特性,咱們定義的數組纔可使用sort()方法等一系列的方法。
舉個栗子:

var num = [1, 5, 3, 7, 9];
num.sort(); // 1,3,5,7,9
alert(num.constructor === Array);   // true
alert(num.__proto__ === Array.prototype);    // true
alert(num.__proto__.__proto__ === Object.prototype);    //true

數組對象num自己就是構造器Array的實例對象,而Arrayprototype屬性指向的對象上定義了sort()方法,因此新定義了num對象通過搜索找到了sort()方法,並調用了方法。

原型的動態性

因爲在原型中查找值的過程是一次搜索,因此對原型對象的任何修改都能當即從實例上反應出來。
舉個栗子:

function Person(){};
var firend = new Person();
// 修改原型
Person.prototype.sayHi = function(){
    alert("Hi");
}

firend.sayHi(); // "Hi"

可是若將原型重寫,來看看有什麼不一樣:

function Person(){};
Person.prototype.name = "darko";
var firend = new Person();
// 重寫了原型對象
Person.prototype = {
    constructor: Person,  // 注意:重寫原型對象,因此此時的constructor屬性變成了新對象的構造函數,默認爲Object構造函數,應該將其設置回適當的值
    sayHi: function(){
        alert("Hi");
    }
}

alert(friend.name); // "darko"
firend.sayHi(); // error

這說明,重寫原型對象切斷了現有原型和任何以前已經存在的實例對象之間的聯繫,它們引用的還是最初的原型。

若是你以爲我寫的還能夠,點一下推薦吧。

相關文章
相關標籤/搜索