【前端Talkking】JS-一步一步掌握Javascript中的原型與原型鏈

0.寫在前面

若是你們想深刻學習Javascript編程語言,Javascript中的原型及原型鏈是必須掌握的。當初我在學習原型及原型鏈的時候,就遇到過很多阻礙,但願經過個人這篇文章,可以讓你真正的掌握JavaScript中的原型及原型鏈。好啦,開始咱們的原型及原型鏈的旅途吧~
在介紹Javascript原型以前,咱們先來了解一段歷史。chrome

1.Javascript繼承機制的設計思想

1994年,網景公司發佈了Navigator瀏覽器0.9版,當時這個版本的瀏覽器只能用來瀏覽,並不具備與用戶用戶進行互動的功能,好比說,要判斷用戶是否填寫了表單數據,只能經過服務器來進行判斷,這樣會帶來一個弊端:極大的浪費了帶寬以及服務器資源。在這種狀況下,就須要一種腳本語言,這種語言可以與瀏覽器進行交互,Brendan Eich負責開發這種腳本語言(也就是Javascript),當時,這位工程師認爲這種腳本語言不須要設計的太複雜,只需完成簡單操做便可,好比判斷用戶是否填寫了表單數據。此時,咱們還要了解下當時的編程語言背景,在1994年的時候,C++是最興盛的面向對象的編程語言,Java1.0也將於第二年推出,Brendan Eich也將Javascript設計爲面向對象的語言,在Javascript中一切皆對象。當時,他遇到了一個難題,到底需不須要將繼承機制引入Javascript中?最終的結果是,也許他受到了C++和Java的影響,繼承機制最終被引入到Javascript編程語言中。編程

下面咱們來看看在C++中生成一個對象的方法:瀏覽器

A *a=new A(param);

而在Java中生成一個對象的方法:服務器

Foo foo=new Foo();

咱們再來看看Javascript生成一個對象的方法:微信

function Dog(name){
    this.name=name;
}

var dogA = new Dog("旺旺");
alert(dogA.name);//旺旺
咱們再來看看Javascript中另一種寫法:編程語言

function Dog(name){
    this.name=name;
    this.species ="犬科";
}
var dogA = new Dog("旺旺");
dogA.species="貓科";
var dogB = new Dog("旺旺2");
alert(dogB.species);//犬科

在這個例子中,生成了兩個對象dogA與dogB,dogA修改了species,可是咱們訪問dogB的species,仍是原來的值。因而,咱們能夠看出經過這種方法生成的實例對象,每一個對象都有本身的屬性和方法的副本,實例對象間不能作到屬性的方法的共享,這樣帶來的一個缺點就是極大的浪費了系統的資源。有沒有改進的方法呢?有,確定有!函數

2.Javascript中原型(prototype)的引入

考慮到上面的不足,這位工程師決定給每一個構造函數添加一個prototype屬性,這個屬性指向一個對象,稱爲prototype對象。在Javascript中,一切皆對象,對象能夠分爲三類:實例對象(經過new和構造函數建立出來的對象)、函數對象(通常也稱爲函數)、原型對象(函數對象的prototype屬性所指向的對象)。咱們首先來看下實例對象中的屬性和方法,以下圖:學習

圖片描述

實例一旦建立,將自動引用prototype對象的方法和屬性,也就是說,針對實例對象而言,它的屬性和方法能夠分爲兩種:一種是本地的,另一種是引用的。this

prototype、_proto_和constructor三角關係spa

我將用下面的一副圖來描述三者的關係:

圖片描述

我將上幅圖總結爲下面幾點內容:

  • 任何函數對象都有prototype屬性,它指向對應的原型對象,表示其實例對象的原型對象;
  • 任何原型對象都有一個constructor屬性,它指向對應的函數對象;

任何對象都有一個隱藏的_proto屬性,它是對原型對象的引用;

  • _proto_屬性不是一個規範的屬性,只是部分瀏覽器實現了此屬性(如chrome和Firefox),若是想訪問對象的原型,可使用Object.getPrototype(object)訪問。

必定要牢記上面的幾點內容,它將對後面的內容很是重要。

3 原型實例分析

說明:後面的實例,若是看不懂,能夠再看看前面的內容,好好理解下前面的內容,必定能夠理解後面的例子的。同時,下面的例子運行結果我會有必定的解釋。
step1:查看對象的原型

function Person(name, age){
    this.name = name;
    this.age = age; 
    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    }
}

var will = new Person("Will", 28);
console.log(will.__proto__);
console.log(will.constructor);

運行結果:
圖片描述

解釋:will對象自己並無"constructor"這個屬性,可是經過原型鏈查找,找到了will原型(will.__proto__)的"constructor"屬性,並獲得了Person函數
對象之間的關係:

step2:查看對象will原型的原型

function Person(name, age){
    this.name = name;
    this.age = age; 
    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    }
}

console.log(will.__proto__ === Person.prototype);
console.log(Person.prototype.__proto__);
console.log(Person.prototype.constructor);
console.log(Person.prototype.constructor === Person);

運行結果:
圖片描述

對象之間的關係

圖片描述

step3:查看對象Object的原型

function Person(name, age){
    this.name = name;
    this.age = age; 
    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    }
}

console.log(Person.prototype.__proto__ === Object.prototype);
console.log(typeof Object);
console.log(Object);
console.log(Object.prototype);
console.log(Object.prototype.__proto__);
console.log(Object.prototype.constructor);

運行結果:
圖片描述
對象之間的關係:
圖片描述

step4:查看函數對象的原型

function Person(name, age){
    this.name = name;
    this.age = age; 
    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    }
}

console.log(Person.__proto__ === Function.prototype);
console.log(Person.constructor === Function)
console.log(typeof Function);
console.log(Function);
console.log(Function.prototype);
console.log(Function.prototype.__proto__);
console.log(Function.prototype.constructor);

運行結果:
圖片描述

對象之間的關係:

圖片描述

4 經過原型改進實例-實現繼承

step1:最老的方式

function Person(name, age){
    this.name = name;
    this.age = age; 
    this.getInfo = function(){
        console.log(this.name + " is " + this.age + " years old");
    }
}

var will = new Person('Will',28);
var wilber = new Person("Will", 20);

對象之間的關係:

step2:經過原型prototype實現繼承

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

對象之間的關係:
圖片描述

5 原型鏈

什麼是原型鏈?

因爲_proto_是任何對象都有的屬性,而Javascript中萬物皆對象,因此會造成一條_proto_鏈接起來的鏈條,遞歸訪問_proto_必須最終到頭,而且值爲null。
原型鏈有什麼做用?
屬性查找與隱藏:當Javascript引擎查找對象的屬性時,先查找對象自己是否存在該屬性,若是不存在,再沿着_proto_這條鏈向上查找,但不會查找自身的prototype。

var A = function(){};
var a = new A();

以上面的這幅圖爲例,查找某個屬性的時候,會沿着這條原型鏈進行查找,直到爲null。
原型鏈之屬性查找

function Person(name, age){
        this.name = name;
        this.age = age; 
    }
    Person.prototype.MaxNumber = 9999;
    Person.__proto__.MinNumber = -9999;
    var will = new Person("Will", 28);
    console.log(will.MaxNumber);// 9999
    console.log(will.MinNumber);//undefined

原型鏈之屬性隱藏

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

var will = new Person("Will", 28);
will.getInfo = function(){
    console.log("getInfo method from will instead of prototype");//
};
will.getInfo();

對象建立方式影響原型鏈的構成

var July = {
    name: "July",
    age: 28,
    getInfo: function(){
        console.log(this.name + " is " + this.age + " years old");
    },
}
console.log(July.getInfo());

hasOwnProperty

var will = new Person('Will',28);
    var wilber = new Person("Will", 20);

    for(var attr in will){
        console.log(attr);
    }    
    console.log('---------------');
    for(var attr in wilber){
        if(will.hasOwnProperty(attr)){
            console.log(attr);
        }
    }

6.總結

  • 在Javascript中,經過原型(prototype)實現了對象的繼承;
  • 在Javascript中,一切皆對象,prototype、_proto_與constructorJavascript中全部的對象關聯起來;
  • 原型鏈能夠實現對象屬性的查找和隱藏; hasOwnProterty函數能夠用來判斷屬性是對象本地屬性仍是原型鏈上的屬性;
    相信到這裏,你已經掌握了Javascript中的原型和原型鏈的知識點了。

歡迎關注個人微信公衆號。您的支持將鼓勵我繼續創做!

圖片描述

相關文章
相關標籤/搜索