JS 基礎篇(二):理解JS原型對象與原型鏈

1、什麼是原型對象和原型鏈

JavaScript 常被描述爲一種基於原型的語言 (prototype-based language)——每一個對象對應擁有一個原型,對象以其原型爲模板、從原型繼承方法和屬性。而同時原型也是對象,它也擁有原型,並從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱爲原型鏈 (prototype chain),它解釋了爲什麼一個對象會擁有定義在其餘對象中的屬性和方法。javascript

注:由於原型同時也是對象,因此也能夠稱呼爲原型對象。html

2、爲何使用原型對象

一、Javascript 並無類繼承模型,而是使用原型對象進行原型式繼承。java

二、原型對象的用途是爲每一個實例對象存儲共享的方法和屬性,它僅僅是一個普通對象而已。而且全部的實例是共享同一個原型對象,所以有別於實例方法或屬性,原型對象僅有一份。git

3、原型對象的理解

一、對象__proto__屬性的值就是它所對應的原型對象,即「對象.__proto__ === 對象對應的原型對象」。github

二、在Javascript中,每一個函數都有一個屬性爲prototype指向函數自身的原型。即「函數.prototype === 自身對應的原型對象」。瀏覽器

案例分析:bash

code1函數

function Person(){ }  //構造函數建立對象
Person.prototype.name = "Jie"; //添加屬性值到原型對象上
console.log(Person.prototype);
複製代碼

在這裏插入圖片描述
原型對象內包含咱們以前添加到原型對象上的name屬性,還包含一個"constructor"屬性,這個屬性對應建立全部指向該原型的實例的構造函數。

code2this

function Person(){ }  //構造函數建立對象
Person.prototype.name = "Jie"; //添加屬性值到原型對象上

var person = new Person(); //建立實例對象
person.age = 23;  //建立屬性值到實例對象上
console.log(person);
複製代碼

在這裏插入圖片描述

經過上面的兩段代碼分析,咱們能夠得出實例對象person的__proto__屬性就是Person的prototype屬性。person.__proto__與Person.prototype均指向了原型對象。spa

而且經過上面的結果能夠看出,實例對象person的原型(person.__proto__)的原型(person.__proto__.__proto__)是Object對象的原型。

能夠將上面的分析進行圖解,以下所示:

在這裏插入圖片描述

  • prototype: 在函數身上,指向原型對象
  • __proto__: 在對象身上(包括函數建立的對象, 函數自己和原型對象),指向自身的原型
  • constructor: 在原型對象上,指向構造函數, 在多級繼承的時候,指明構造函數方便在對象上擴展原型屬性
  • Object.__proto__爲null: 原型的頂端

在Javascript中,每一個函數都有一個原型屬性prototype指向函數自身的原型,而由這個函數建立的對象也有一個__proto__屬性指向這個原型。(即person.__proto__ === Person.prototype; //true)

而函數的原型是一個對象,因此這個對象也會有一個__proto__指向本身的原型,這樣逐層深刻直到Object對象的原型(null),這樣就造成了原型鏈。

函數的原型對象的constructor默認指向函數自己,原型對象除了有原型屬性外,爲了實現繼承,還有一個原型鏈指針__proto__,該指針指向上一層的原型對象,而上一層的原型對象的結構依然相似,這樣利用__proto__一直指向Object的原型對象上,而Object的原型對象用Object.prototype.__proto__ = null表示原型鏈的最頂端,如此變造成了javascript的原型鏈繼承,同時也解釋了爲何全部的javascript對象都具備Object的基本方法。

4、"prototype"和"__proto__"區別

對於全部的對象,都有__proto__屬性,這個屬性對應該對象的原型。 對於函數對象,除了__proto__屬性以外,還有prototype屬性,當一個函數被用做構造函數來建立實例時,該函數的prototype屬性值將被做爲原型賦值給全部對象實例(也就是被設置爲全部實例的__proto__屬性值)

5、查找屬性

當訪問一個對象的屬性時,Javascript 會從對象自己開始往上遍歷整個原型鏈,直到找到對應屬性爲止。若是此時到達了原型鏈的頂部,也就是 Object.prototype,仍然未發現須要查找的屬性,那麼 Javascript 就會返回 undefined 值。

例如:當你訪問 person 的一個屬性, 瀏覽器首先查找 person 是否有這個屬性. 若是 person 沒有這個屬性, 而後瀏覽器就會在 person 的 __proto__ 中查找這個屬性(也就是 Person.prototype). 若是 person 的 __proto__ 有這個屬性, 那麼 person 的 __proto__ 上的這個屬性就會被使用. 不然, 若是 person 的 __proto__ 沒有這個屬性, 瀏覽器就會去查找 person 的 __proto__ 的 __proto__ ,看它是否有這個屬性. 默認狀況下, 全部函數的原型屬性的 __proto__ 就是 Object.prototype. 因此 person 的 __proto__ 的 __proto__ (也就是 Person.prototype 的 __proto__ (也就是 Object.prototype)) 會被查找是否有這個屬性. 若是沒有在它裏面找到這個屬性, 而後就會在 person 的 __proto__ 的 __proto__ 的 __proto__ 裏面查找. 然而這有一個問題: person 的 __proto__ 的 __proto__ 的 __proto__ 不存在. 最後, 原型鏈上面的全部的 __proto__ 都被找完了, 瀏覽器全部已經聲明瞭的 __proto__ 上都不存在這個屬性,而後就得出結論,這個屬性是 undefined.

在這裏插入圖片描述

6、原型對象操做

觀察如下代碼:

function Person(){
    this.name = "Jie";
}
var person = new Person();

Person.prototype.say = function(){
    console.log("success");
}
複製代碼

咱們的代碼中定義了構造器,而後用這個構造器建立了一個對象實例,此後向構造器的 prototype 添加了一個新的方法。

say() 方法仍然可用於 person 對象實例——舊有對象實例的可用功能被自動更新了。這證實了先前描述的原型鏈模型。這種繼承模型下,上游對象(父類)的方法不會複製到下游的對象(子類)實例中;下游對象自己雖然沒有定義這些方法,但瀏覽器會經過上溯原型鏈、從上游對象中找到它們。這種繼承模型提供了一個強大而可擴展的功能系統。

所以,咱們能夠將一些通用的,公用的屬性或方法定義在prototype中。

例如:

一、"getInfo"方法是構造函數Person的一個成員,當經過Person構造兩個實例的時候,每一個實例都會包含一個"getInfo"方法。

在這裏插入圖片描述

二、原型就是爲了方便實現屬性的繼承,因此能夠將"getInfo"方法看成Person原型(Person.prototype)的一個屬性,這樣全部的實例均可以經過原型繼承的方式來使用"getInfo"這個方法了。而且改變getInfo方法所有的實例都會同步。

function Person(name, age){
    this.name = name;
    this.age = age;
}

Person.prototype.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
};
複製代碼

在這裏插入圖片描述

參考資料:

MDN對象原型

原型對象

瞭解JS原型對象

__proto__和prototype

相關文章
相關標籤/搜索