banner
lzyoo

lzyoo

本人职业:前端开发工程师 工作年限:三年 技术栈:React、Vue、Nest、Python 一直在努力,一直在放弃,好想有一个引路人带领。

QTNND的燒腦原型鏈

😍原型鏈介紹#

原型對象#

說個簡單的開頭

JS 中萬物都基於對象

  • 函數的prototype指向原型對象
  • 原型對象的constructor指向函數
  • 原型對象上定義的屬性方法可以被所有與之關聯的實例對象共享繼承
  • prototype函數獨有的
  • __proto__屬性,它是對象所獨有的
  1. JS 創建函數的時候,都会生成一個prototype屬性,這個屬性指向了一個對象,這個對象就是該函數的原型對象,

該對象中包含一個constructor屬性,該屬性指向的是該函數。

​ 2. 聲明該函數的實例對象,該對象實現了一個__proto__屬性,指向該實例對象(ha)原型對象({constructor:f}),因此我們可以把函數的prototype和實例對象的__proto__ 進行對等比較,結果當然就是true

// 創建一個名為Ha的函數
function Ha(){
    this.name='哈喇'
}
// 調用下Ha的prototype
Ha.prototype //{constructor:f}

// 聲明一個實例對象
let ha = new Ha()
// 原型對象比較
ha.__proto__ === Ha.prototype // true
// 構造函數可直接通過ha.constructor調用
ha.__proto__.constructor === ha.constructor

原型鏈#

原型鏈很簡單 看以下代碼就懂了

ha.__proto__.__proto__.__proto__ // null
// 在此解釋下為什麼是null
ha.__proto__時指向了Ha的原型對象
ha.__proto__.__proto__時指向了Ha的原型對象Object的原型對象
ha.__proto__.__proto__.__proto__時,萬物都基於對象了,那你說Object上層的原型對象還存在嗎?當然不存在了,所以返回null
// 構造函數繼承
function Haha(name){
    this.name=name
    Ha.call(this)
}

// 先聲明一個Haha類的實例,調用實例的name屬性,打印的值是Ha中name屬性的值,,因為使用了繼承所以,當前實例構造函數中不存在的屬性,他就會自己再往上層去查找Ha的構造函數的name屬性,如果存在則打印,如果不存在,則會繼續往上找到Object的構造函數的name屬性,依然不存在就是null了
let haha = new Haha()
haha.name // 哈喇

// 先聲明一個Haha類的實例並傳參,調用實例的name屬性,打印的值就是王老五了
let haha = new Haha('王老五')
haha.name // 王老五

構造函數、實例和原型的關係#

構造函數是用來構造實例的函數。每個實例都有一個原型,原型又指向構造函數的原型

// ha的原型是Ha.prototype
// Ha.prototype的原型是  Object.prototype
function Ha(){} // 構造函數
let ha=new Ha() // 實例

Ha.prototype.constructor === Ha //true 構造函數原型的constructor指向構造函數本身

繼承#

構造函數繼承#

使用 call () 將 Parent 的 this 指向 Child 的實例,實現繼承。
優點

  1. 創建實例時可以向父類構造函數傳參。
  2. 每個子類實例都有一份自己的父類實例,修改父類實例屬性不會影響其他繼承同一父類的子類。

缺點

  1. 只繼承父類構造函數中的屬性和方法,無法繼承原型對象上的內容。
  2. 無法復用
function Parent(){
    this.name='father'
}

Parent.prototype.sayHello = function () {
    return this.name
}

function Children(){
    Parent.call(this)
}
const c = new Children()
c.name // father
c.sayHello // c.sayHello is not a function

原型鏈繼承#

重寫原型對象

優點

  1. 函數復用,子類可使用父類屬性和方法。
  2. 子類可直接訪問父類原型對象上的屬性方法。

缺點

  1. 無法向父類構造函數傳參。
  2. 父類引用屬性會共享到所有子類,一個子類修改了引用屬性,其他子類也會受影響,因為操作的是同一個內存地址。
  3. 父類私有變量會在子類中暴露。
function Parent(){
    this.name='father'
    this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function(){
    return this.name
}
function Children(){}
Children.prototype = new Parent()
const c = new Children()
const c2 = new Children()
c.name // father
c.sayHello() // father
c.hobby.push('rap')
c.hobby // ['唱', '跳', 'rap']
c2.hobby // ['唱', '跳', 'rap']

組合繼承#

構造函數 + 原型鏈

優點

  1. 繼承父類實例屬性和原型對象的所有屬性方法(私有除外)
  2. 避免了引用類型的屬性被所有實例共享問題(原型鏈繼承)。

缺點

  1. 調用兩次父類實例,性能影響
function Parent(){
    this.name='father'
}
Parent.prototype.sayHello = function(){
    return this.name
}
function Children(){
    Parent.call(this)
}
Children.prototype = new Parent()
Children.prototype.constructor = Children
const c = new Children()
c.name // father
c.sayHello() // father

寄生式繼承#

原型式繼承 + 增強對象

優點

  1. 繼承父類實例屬性和原型對象的所有屬性方法(私有除外)
  2. 避免了引用類型的屬性被所有實例共享問題(原型鏈繼承)。

缺點

  1. 調用兩次父類實例,性能影響
function Parent(){
    this.name='father'
}
Parent.prototype.sayHello = function(){
    return this.name
}
function Children(){
    Parent.call(this)
}
Children.prototype = new Parent()
Children.prototype.constructor = Children
const c = new Children()
c.name // father
c.sayHello() // father

寄生式繼承#

子類的構造函數中,增強父類實例的功能

優點

  1. 避免了引用類型的屬性被所有實例共享問題(原型鏈繼承)。

缺點

  1. 無法實現函數復用,每個子類內部都實例化了父類。
  2. 能訪問到父類私有變量,其實就是子類里塞了父類實例。
function Parent() {
    this.name = 'father'
    this.hobby = ['唱', '跳']
}

Parent.prototype.sayHello = function () {
    return this.name
}

function Children() {
    let parent = new Parent()
    parent.sayBye = function Bye() {
        return this
    }
    return parent
}
const c = new Children()
console.log(c.sayHello());

寄生組合式繼承#

子類的構造函數中,增強父類實例的功能

優點

  1. 避免了引用類型的屬性被所有實例共享問題(原型鏈繼承)。
  2. 只調用了一次 Parent 構造函數

缺點

  1. 無法實現函數復用,每個子類內部都實例化了父類。
  2. 能訪問到父類私有變量,其實就是子類里塞了父類實例。
function _extends(children, parent) {
    let parent_prototype = Object.create(parent.prototype)
    parent_prototype.constructor = children
    children.prototype = parent_prototype
}
function Parent() {
    this.name = 'father'
    this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function () {
    return this.name
}
function Children() {
    Parent.call(this)
}

_extends(Children, Parent)
const c = new Children()
console.log(c.sayHello()) // father
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。