老生常談的 JavaScript 原型鏈

原文連接:https://ssshooter.com/2021-01...javascript

用了這麼多年的 JavaScript,對於原型鏈這東西自覺是理解了,可是平常工做中不多使用的「繼承」部分最近忽然想起來竟以爲有點陌生,因此在這裏稍微理一下思路。java

本文90%不能讓不懂原型鏈的人看懂原型鏈,可是可能能夠給懂一點原型鏈的人一點提示,不過若是本文讓你更混亂的話,請在評論區提出疑問 😂面試

prototype

只有函數有 prototype,對象沒有。ssh

// 函數
function A(){}
A.prototype
// 會輸出相似下面的東西
{
    constructor: A,
    __proto__: Object
}
// 對象
a = {}
a.prototype // 輸出 undefined
// 而若是你給一個對象賦予 prototype,效果不過是多了一個 prototype 屬性

proto

__proto__ 能夠當作一種「鏈接」,所到之處的屬性(property)均可以訪問。剛進坑的時候據說 __proto__ 不是標準的屬性,可能會有不一樣的實現,但這麼多年過來了彷佛也就只用 __proto__函數

a = { c: 1 }
a.hasOwnProperty('c') // 輸出 true

a 顯然沒有 hasOwnProperty 這個屬性,因而他會往 __proto__ 找,在 Object.prototype 下找到 hasOwnProperty 並使用。this

通常,一個 __proto__ 會被鏈接到一個 prototype,但你仍能夠直接定義 __proto__prototype

a.__proto__ = { b: 'b' }
a.b // 輸出 b

所謂的原型鏈就是由 __proto__ 串成。code

例如一隻貓
let mona = new Feliscatus()

     __proto__                   __proto__                     __proto__
mona -----------> Cat.prototype -----------> Felis.prototype -----------> Felinae.prototype
|                  |                           |                             |
| 實例本身的屬性      | constructor Cat           | constructor Felis           | constructor Felinae
                    | 其餘綁定在 prototype 的屬性  | 其餘綁定在 prototype 的屬性   | 其餘綁定在 prototype 的屬性

上一層找不到的東西就沿着 __proto__ 往下找,直到盡頭,找不到就是 undefined 了

__proto__ 也能夠直接鏈接到一個對象(換句話就是,繼承一個對象的屬性):對象

const person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`)
  },
}
let me1 = {}
me1.__proto__ = person
// 這樣 me1 就能使用 printIntroduction
// 不過還有更方便一點的方法,使用 Object.create,這樣就不用本身鏈接
let me2 = Object.create(person)

實例化

new 一個對象時,實際上作了什麼,如何本身寫一個 new(聽說面試會考,不過我沒遇到過):繼承

function Person(name) {
  this.name = name
}
Person.prototype.say = words => words
// 這是一我的類
let person1 = new Person('Lilas')
// 正經常使用 new 的結果是
// 1. 返回一個對象,
// 2. 這個對象運行了構造函數,
// 3. 這個對象可使用 Person 的 prototype 的屬性

// 知道了這三步,下面寫一個本身的 new
function createInstance(klass, ...arg) {
  // 返回一個對象
  let obj = {}
  // 以對象爲 this 運行構造函數 klass
  klass.call(obj, ...arg)
  // 爲了讓 obj 可使用 Person.prototype 使用 __proto__ 鏈接
  obj.__proto__ = klass.prototype
  // 返回這個對象
  return obj
}

繼承

上面說過用 Object.create 繼承一個對象的屬性,這裏的繼承說的是「類」繼承。

JavaScript 在 ES6 以前,其實沒有類,所謂的繼承就是沿着原型鏈訪問父類(準確來講是 constructor)的變量和方法。

(順帶一提,想要了解 ES6 以後的類能夠看看 Class 繼承與 super,從這篇文章還能夠看出,ES6 的類不是單純的語法糖)

在 proto 部分說明了原型鏈的結構,下面講講實際上怎麼構造原型鏈。

// 上面已經有一個 Person 了,下面繼承 Person 寫一個 Boxer
function Boxer(name, id) {
  // 在成爲格鬥者前他得是我的,因此先調用 Person 構造函數,至關於 super()
  Person.call(this, name)
  //   而後爲這個格鬥者加上本身的格鬥者 id
  this.id = id
}
// 添加 Boxer 本身的方法
Boxer.prototype.punch = () => 'punch!'
// 接通到 Person.prototype
Boxer.prototype.__proto__ = Person.prototype

不管接着還要繼承多少次,總之,記住 __proto__ 串通了父子的 prototype 就行了。

相關文章
相關標籤/搜索