原型和原型鏈

首先,咱們來看一個咱們再代碼中常用的卻沒有深刻細究的實例app

const obj = { key: "value" } 
obj.toString() // [object Object]

咱們在自定義對象時,僅僅定義 key 的屬性,並無定義 toString 的方法,可是在代碼中卻能夠調用而且得到結果,那麼如今我就來深刻的解釋一下發生在背後的緣由。函數

必備基礎知識

JavaScript 中的數據類型分爲兩大類,值類型(也叫基本數據類型) 和 引用數據類型this

值類型:String、Number、Boolean、null、undefined、Symbol(ES6 新增)
引用數據類型:Object、Function、Arrayspa

在這裏咱們僅僅來聊一聊 ObjectFunctionprototype

// 使用對象直接量建立對象 
// 優勢: 
// 一、寫法直觀,簡潔無歧義 
// 二、強調對象是一個簡單的可變的散列表,而沒必要必定派生自某個類 
// 三、當使用 Object() 建立對象時,解析器須要順着原型鏈開始查找,直到找到 Object 構造函數爲止,而直接量的寫法則不會 
const obj = { key: "value" } 
console.log(obj)
/*  控制檯輸出結果以下 
    {key: "value"} 
        key: "value" 
        __proto__: Object 
*/

在 JavaScript 中,全部的對象都有 __proto__ 的屬性,指向該對象的原型對象,並且 Function 也是對象,是一種特殊的對象code

new 一個對象到底發生了什麼

隨便百度一下,就能發現常見的,對 new 的過程有以下描述:對象

  1. 建立一個空對象,將它的引用賦給 this,繼承函數的原型;
  2. 經過 this 將屬性和方法添加到這個對象上;
  3. 最後返回這個 this 指向的新對象(若是沒有手動指定return時);
function myNew (Parent, arguments) { 
    // 建立一個新對象賦值給this,並 繼承函數的原型 
    // 這一步實際上是要拆成兩小步的 
      // 一、建立新對象,賦值給 this 
          // this = {} 
      // 二、繼承函數的原型 
          // this.__proto__ = Parent.prototype 
    // 合併寫法 
    this = Object.create(Parent.prototype) 
    // 將屬性和方法添加到 this 指向的對象上 
    this.argments = arguments 
    // 返回這個 this 指向的新對象(若是沒有手動指定 return)        return this 
}

Object.create 建立一個新對象,並使用傳入的對象做爲該對象的原型繼承

建立(構造)函數

在 JavaScript 中,每個函數都有一個 prototype 的屬性,指向一個對象,該對象便是構造函數的原型對象ip

每一個原型對象都有一個 constructor 屬性,指向該原型對象所屬的構造函數原型鏈

不管是使用下列哪一種方式建立的函數,都會自帶 prototype 屬性:

  1. const func = function () {}
  2. const func = new Function () {}
  3. function func () {}

獲取原型對象的是3種方式:

一、obj.__proto__,因爲 __proto__屬性爲內部屬性,因此通常推薦是使用
二、Parent.prototype,經過父類構造函數的 prototype 訪問
三、Object.getPrototypeOf(obj),經過頂層對象的 getPrototypeOf 方法獲取,推薦使用

對象的繼承

經過咱們手動的實現 new 運算符能夠發現,新建立的對象經過 __proto__ 屬性,引用了父類構造函數的原型對象 prototype,由此實現了繼承

注意:
一、在 JavaScript 中,每一個對象都有 __proto__ 屬性,指向該對象的父類構造函數的原型對象
二、在 JavaScript 中,每一個函數都有 prototype 屬性,即構造函數的原型對象 (因爲函數也是對象,因此函數也有 __proto__ 的屬性)
prototype.jpg

原型鏈

在 JavaScript 中,實現繼承的方式,就是經過對象的原型串聯起來的,一級一級的向上查找而造成的鏈式結構,稱爲原型鏈
proto_chain.png

繼承

可簡單的理解爲,即子類無需定義便可使用父類已定義的屬性和方法

如何實現繼承

/* 
  一、原型鏈繼承
    子類對象繼承父類構造函數的實例
  
  優勢:既能繼承父類原型上的屬性和方法,也能繼承父類上的屬性和方法
  缺點:
    一、不能動態的給父構造函數傳遞參數
    二、繼承單一
    三、全部的子類,都會共享父類實例的屬性和方法
*/
function Parent (name, age) {
  this.name = name
  this.age = age
}
const parent = new Parent("obj1", 21)
const obj1 = {}
obj1.__proto__ = parent

/*
  二、借用構造函數繼承
    經過 call / apply 在子類構造函數中動態調用父類構造函數

  優勢:
    一、能夠動態的傳遞參數
    二、能夠多繼承(多個 call/apply)
  缺點:
    一、只能繼承父類的實例屬性和方法,不能繼承父類原型的屬性和方法
    二、每一個子類構造函數中,都要手動調用父類構造函數,臃腫
*/
function Parent (name, age) {
  this.name = name
  this.age = age
}
function Children (name, age) {
  Parent.call(this, name, age)
  // ...
}
const obj2 = new Children("obj2", 22)

/*
  三、組合繼承
    組合 原型鏈繼承 和 借用構造函數繼承
  優勢:
    一、既能繼承構造函數屬性,又能繼承原型屬性
    二、能夠動態傳參
  缺點:
    一、不支持多繼承(父類原型上的屬性)
    二、子類實例原型上的構造函數指向父類構造函數
*/
function Parent (name, age) {
  this.name = name
  this.age = age
}
function Children(name, age){
  Parent.call(this, name, age)
  // ...
}
Children.prototype = new Parent()
const obj3 = new Children("obj3", 23)

/*
  四、原型式繼承
    經過一個函數封裝,建立一個新對象,將傳入的對象做爲新對象的原型對象,返回新對象
  優勢:
    一、能繼承原型上的屬性
  缺點:
    一、不能繼承實例上的屬性
    二、單一繼承
*/
function Parent (name, age) {
  this.name = name
  this.age = age
}
function content(p){
  function F(){}
  f.prototype = p
  return new F() 
}
const obj4 = content(new Parent("obj4", 24))

/*
  五、寄生式繼承
    在原型式繼承的基礎上,再套上一層函數,用於處理函數的傳參
  優勢:
    一、支持動態傳參
  缺點:
    一、沒用到subject的原型,沒法共享該構造函數原型的屬性
*/
function Parent (name, age) {
  this.name = name
  this.age = age
}
function content(p){
  function F(){}
  F.prototype = p
  return new F()
}
function subject(name, age,level){
  const cont = content(new Parent(name, age))
  cont.level = level
  return cont
}
const obj5 = subject("obj5", 25, 5)

/*
  六、寄生式組合繼承(推薦)
    經過寄生,完成原型上屬性的繼承
    經過組合,完成實例上屬性的繼承
*/
function content(obj){
  function F(){}
  F.prototype = obj
  return new F()
}
// 寄生
const cont = content(Parent.prototype)
// 組合
function Child(name, age){
  Parent.call(this, name, age)
  // ...
}
cont.constructor = Child // 惟一的不足
Child.prototype = cont
const obj6 = new Child("obj6", 26)
相關文章
相關標籤/搜索