動態

詳情 返回 返回

【js基礎複習】原型以及原型鏈 - 動態 詳情

目錄

  • 對象是什麼
  • 構造函數
  • 原型對象
  • 實現繼承以及不同繼承方式

對象

為什麼要面向對象編程

代碼邏輯遷移更加靈活、代碼複用性高、高度模塊化

構造函數

function Person(name) {
    this.name = name
    
    this.getName = function(name) {
        return name
    }
}
const person = new Person()
  • 函數體內部的this指向生成的實例對象
  • 生成對象用new關鍵詞進行實例化
  • 可以做初始化傳參

new實例的過程

  1. 創建一個空對象,作為返回的對象實例
  2. 將生成空對象的原型對象指向了構造函數的prototype屬性
  3. 將當前實例對象賦值給內部的this
  4. 執行構造函數初始化代碼
function myNew(Fn, params) {
    // 創建一個空對象,作為返回的對象實例,將實例對象的__proto__屬性指向構造函數的原型(Fn.prototype)
    const obj = Object.create(Fn.prototype)
    // 將當前實例對象賦值給內部的`this`,執行構造函數初始化代碼
    const result = Fn.apply(obj, params)
    return (result && (typeof result === 'object' || typeof result === 'function')) ? result : obj
}

如何創建沒有news實例化的構造函數(不被外部感知)

function Person() {
    // 判斷是否為new實例對象
    if(!this instanceof Person) {
         return new Person()  
    }
    this.name = "Tom";
    this.getName = function() {
        return this.name
    }
}

const person = Person();

使用構造函數的缺點

  • 構造函數中的方法,會存在每一個生成的實例對象中,重複掛載其實是會導致資源浪費。

所以要使用原型對象來解決上面的問題。

原型對象

每個函數都有一個屬性——prototype。這個prototype的屬性值是一個對象(屬性的集合),默認只有一個叫做constructor的屬性,指向這個函數本身。 如下圖所示:

這裏寫圖片描述

上圖中,SuperType是一個函數,右側的方框就是它的原型

原型既然作為對象(屬性的集合),除了constructor外,還可以自定義許多屬性,比如下面這樣的:

這裏寫圖片描述

function Person(name) {
    this.name = name;
}
// 賦值原型對象的屬性做為實例對象上的繼承屬性,避免重複掛載方法
Person.prototype.getName = function() {
    return this.name
}

“隱式原型”proto

每個對象都有一個__proto__屬性,指向創建該對象的構造函數的prototype。

注意: Object.prototype確實一個特例——它的__proto__指向的是null,切記切記!!!

這裏寫圖片描述

從上圖可以看出:自定義函數Foo.__proto__指向Function.prototypeObject.__proto__指向Function.prototype

但是,為什麼有Function.__proto__指向Function.prototype呢?
其實原因很簡單:Function也是一個函數,函數是一種對象,也有__proto__屬性。既然是函數,那麼它一定是被Function創建。所以Function是被自身創建的。所以它的__proto__指向了自身的Prototype

最後一個問題:Function.prototype指向的對象,它的__proto__是不是也指向Object.prototype
答案是肯定的。因為Function.prototype指向的對象也是一個普通的被Object創建的對象,所以也遵循基本的規則。

這裏寫圖片描述

繼承

  • 原型鏈繼承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man() {
    
}
Man.prototype = new Person();
Man.prototype.constructor = Man

本質: 重新原型對象的方式,將父對象的屬性和方法,作為子對象原型對象的屬性和方法

缺點:

  1. 父類屬性一旦賦值給子類的原型屬性,此時屬性屬於子類的共享屬性了
  2. 實例化子類時,無法向父類傳參
  • 構造函數繼承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
// 解決了共享屬性的問題 + 子向父傳參問題
  • 組合繼承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
Man.prototype = new Person();
Man.prototype.constructor = Man

缺點:無論何種場景,都會調用2次父構造函數

  1. 初始化子類原型
  2. 子類調用函數內部call父類的時候
  • 寄生組合繼承
function Person() {
    
}
Person.prototype.getName = function() {
    return this.name
}

function Man(arg) {
    Person.call(this,arg)   
}
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man

如何實現多重繼承

function Person(name) {
    this.name = name
}
Person.prototype.getName = function() {
    return this.name
}
function Worker(salary) {
    this.salary = salary
}
Worker.prototype.getSalary = function() {
    return this.salary
}

function Man(arg) {
    Person.call(this,arg) 
    Worker.call(this,arg)   
    
}
Man.prototype = Object.create(Person.prototype);
Object.assign(Man.prototype, Worker.prototype);
Man.prototype.constructor = Man

參考文章

  • new 實例化對象過程
  • 【JS】深入理解JS原型和繼承
user avatar wric 頭像
點贊 1 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.