JavaScript進階系列-原型繼承與原型鏈

先上圖bash

原型繼承

原型繼承實現

function Animal(name){
    // 屬性
    this.name = name || 'Animal';
    // 實例方法
    this.sleep = function(){
        console.log(this.name + " 正在睡覺。");
    }
}
    // 原型方法
Animal.prototype.eat = function(food){
    console.log(this.name + " 正在吃 "+food);
}

// 子類
function Tiger(){}
Tiger.prototype = new Animal();
Tiger.prototype.name = "Tiger";

var tiger = new Tiger();
console.log(tiger.name);
console.log(tiger.eat('sleep'));
console.log(tiger.sleep());
console.log(tiger instanceof Animal); //true 
console.log(tiger instanceof Tiger); //true
複製代碼

原型鏈

什麼是原型鏈? 要理解原型鏈,須要從函數對象constructor、new、Prototype、proto 這五個概念入手。函數

什麼是原型鏈

談到繼承的時候 JavaScript 只有一種結構:對象,每一個對象都有一個 proto 的屬性指向他的構造函數的原型對象(是一個對象實例)。該原型對象也有本身的一個原型對象(proto 指向)。ui

函數經過 prototype 屬性查找this

組合繼承

function Animal(name){
    // 屬性
    this.name = name || 'Animal';
    // 實例方法
    this.sleep = function(){
        console.log(this.name + " 正在睡覺。");
    }
}
    // 原型方法
Animal.prototype.eat = function(food){
    console.log(this.name + " 正在吃 "+food);
}

// 子類
function Tiger(value){
    Animal.call(this, value)
}
Tiger.prototype = new Animal();
Tiger.prototype.name = "Tiger";

var tiger = new Tiger();
console.log(tiger.name);

複製代碼

class 關鍵字實現

class Parent {
  constructor(value) {
    this.val = value
  }
  getValue() {
    console.log(this.val)
  }
}
class Child extends Parent {
  constructor(value) {
    super(value)
  }
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
複製代碼

函數對象

在 JavaScript 當中 函數即對象。(函數是一等公民) 能夠把函數賦值給變量,能夠做爲參數傳遞給其餘函數,添加屬性和調用方法。 凡是由關鍵字 function 定義的函數都是函數對象,並且,只有函數對象擁有 prototype 屬性。指向該函數的原型對象。spa

constructor 構造函數

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        console.log(this.name);
    };
}
複製代碼

咱們建立了一個自定義函數 Person() ,構造函數一般使用大寫字母開頭。prototype

new

要建立 Person() 的新實例,必須使用 new 關鍵字。 new 調用構造函數實際上會經歷如下四個步驟。指針

  • 建立一個新對象
  • 將構造函數的做用域賦值給新對象 (this 指向新的對象)
  • 執行構造函數中的代碼
  • 返回新的對象

構造函數和其餘普通函數沒有什麼區別,使用 new 操做符號均可以做爲構造函數來調用。code

注意:構造函數若是返回一個對象的話,會覆蓋掉新建的對象。cdn

##構造函數的問題對象

建立實例時構造函數每次都要執行

const Tom = new Person('Tom', 12, 'cxy');
const lucy = new Person('lucy', 14, 'dd');
Tom.sayName === lucy.sayName // false
複製代碼

這樣每一個實例都有一個獨立的 sayName 方法。 一般的解決辦法是把方法添加到原型鏈上。

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

Person.prototype.sayName = function (){
    console.log(this.name);
}
複製代碼

prototype 原型

就像咱們上面作的那樣,使用原型的好處就是可讓全部的對象實例共享他所包含的屬性和方法。

原型對象

在默認狀況下,全部原型對象都會自動得到一個 constructor 屬性,這個屬性包含一個指向 prototype 屬性所在函數的指針。 上面咱們建立的 Person.prototype.constructor 指向 Person .經過這個構造函數咱們還能夠給原型對象添加其餘的屬性和方法。

雖然能夠經過對象實例訪問保存在原型中的值,但卻不能經過對象實例重寫原型中的值。 ?? 若是咱們添加的屬性和方法和原型對象的屬性和方法同樣會怎麼樣

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

Person.prototype.sayName = function (){
    console.log(this.name);
}
Person.prototype.tag = '大V';
const Tom = new Person('Tom', 12, '碼農');
Tom.tag = 'dd';
Tom.sayName = function(){console.log(1)};
複製代碼

能夠看到獲取的是咱們後添加的屬性和方法,這是讀取對象屬性和方法的機制決定的。 首先會在原型上查找,原型上沒有的話再去原型鏈查找,一層一層向上查找。

=v= 當咱們修改原型對象會怎麼樣 嘿嘿

function Person(){}

const Tom = new Person();

Person.prototype = {
    constructor: Person,
    name : "Stone",
    age : 28,
    job : "Software Engineer",
    sayName : function () {
        console.log(this.name);
    }
};

Tom.sayName();   // Uncaught TypeError: Tom.sayName is not a function
複製代碼

Tom 出生的時候尚未這些東西,機智如我。

原生對象的原型

原生對象也是經過這種模型建立的。

全部原生引用類型,都在其構造函數的原型上定義了方法。經過上面的例子咱們也能夠直接修改原生對象的原型的方法,可是不推薦這種方式,有可能會重寫原生方法。

原型鏈的問題

它省略了爲構造函數傳遞初始化參數這一環節,結果全部實例在默認狀況下都將取得相同的屬性值。

對於引用類型的共享屬性

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["ZhangSan", "LiSi"];
}

Person.prototype = {
    constructor : Person,
    sayName : function(){
        console.log(this.name);
    }
}

const person1 = new Person("dd", 28, "Engineer");
const person2 = new Person("tt", 29, "Teacher");
person2.friends=[]
person1.friends  //  ["ZhangSan", "LiSi"]
複製代碼

能夠看到共享屬性都是備份的。

相關文章
相關標籤/搜索