深刻淺出面向對象和原型【概念篇2】

牆裂建議您在看這篇文章以前閱讀一遍我以前寫的文章 深刻淺出面向對象和原型【概念篇1】,由於此篇文章涉及到很多相關概念都在上篇文章裏啦

1.1 簡單複習對象

對象是老生常談的概念了,在這裏咱們來簡單複習一下segmentfault

var obj = {
        a: 1, 
        b: "hello",
        c: true,
        d: function () {
            console.log("hello")
        },
        e: {
            e_1: "對象內儲存一個對象"
        }
    }

對象是一個儲存一系列無序 key: value【鍵值對】 的集合的容器函數

注意:核心是要將對象做爲一個容器看待

使用對象做爲容器的這個特性咱們能夠進行封裝,這會產生兩個好處this

  1. 讓咱們的代碼變得優雅、易讀
  2. 規避全局變量

1.2 經過構造函數建立對象

1.2.1 關於function的額外知識

  1. function做爲構造函數(經過new操做符調用)的時候會 返回 一個類型爲function的對象
  2. function能夠接受參數,能夠根據參數來建立 相同類型不一樣值 的對象
  3. function實例做用域內有一個constructor屬性,這個屬性就能夠指示其構造器

1.2.2 學會使用 new Function

new 運算符接受一個函數 F 及其參數:new F(arguments...)spa

  1. 建立類的實例——這步是把一個空的對象的__proto__屬性設置爲 F.prototype
  2. 初始化實例——函數 F 被傳入參數並調用,關鍵字 this 被設定爲該實例
  3. 返回實例
function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        }
        // 通常在這個函數裏不要 return
        // 若是return引用類型的話,等於把return的值賦值給p1
    }

    People() // this指向全局變量,name和sayName成爲了全局變量的屬性

    var p1 = new People('sad')
    // 第一步:instance = {} 建立了一個類的實例 —— 空對象,而且將空的對象的__proto__屬性設置爲 F.prototype,也就是說
    // 第二步:執行函數People(),並傳入參數"sad",並將this指向p1
    // 第三步:return instance【實例】 , 即把instance賦值給p1

    // 請注意 People()是一個函數,new People()是構造函數

    var p2 = new People('angry')

1.2.3 instanceof

instance 的中文意思爲 實例
那麼 instanceof 天然就是用來判斷對象是否爲某個類型的實例prototype

console.log(p1 instanceof People) // true
    // 意思是判斷 對象(p1)是否爲某個類型(People)的實例
    // p1 是由 People 構建出來的,天然是People的實例,返回爲true

但須要注意的是,instanceof 運算符的工做原理是檢測 類的prototype 是否存在實例的原型鏈上3d

console.log(p1 instanceof Object) // true
    // 由於Object.prototype能夠被實例p1以原型鏈的方式訪問到

1.2.4 構造函數存在的問題

構造函數在解決了上面全部問題,同時爲實例帶來了類型
但能夠注意到在上例中,每一個的實例的方法做用是同樣的
可是每一個實例被建立的時候都要從新聲明一遍,浪費了內存code

// 咱們再來看一下這個函數
    function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        } // 每一個實例的sayName方法倒是相同的,並且每出現一個新實例,都是新聲明一個函數,大大地浪費了內存
    }

能不能給People的實例都使用同一個sayName方法呢對象

1.3 構造函數&原型&原型鏈

1.3.1 構造函數

任何函數使用new表達式就是構造函數,也就是說這個函數成爲了一個類blog

1.3.2 用原型鏈解決重複建立的問題

  1. 每一個對象都會自帶一個名稱爲prototype的屬性
  2. prototype屬性是一個對象
// 仍是這個例子
    function People(name) {
        this.name = name
        this.sayName = function () {
            console.log(name)
        } 
    }
    var p1 = new People('sad')
    var p2 = new People('angry')
    
    // People自己就有prototype屬性
    console.log(People.prototype) // {constructor: ƒ} 

    // 而每一個對象也都會帶有一個__proto__屬性,指向這個構造函數【實例】的類
    // 全部的實例都共用一個prototype
    console.log(p1.__proto__ === People.prototype) // true
    console.log(p2.__proto__ === People.prototype) // true
// 實例能夠經過__prop__訪問到其 類 的prototype屬性,這就意味着類的prototype對象能夠做爲一個公共容器,供全部實例訪問。
    People.prototype.test = "abc"
    console.log(p1.test) // abc

    // p1這個對象沒有test屬性,可是能夠經過它的__proto__屬性訪問到People的prototype屬性
上述關係咱們用一張圖來幫助你們更好地理解

clipboard.png

1.3.3 解決內存浪費問題

由上咱們能夠知道ip

  1. 全部實例都會經過原型鏈引用到其類的prototype
  2. prototype至關於全部實例均可以訪問到的一個公共容器,這個公共容器也是這些實例的類的屬性

    那麼如何解決內存浪費問題呢?

    Answer:重複的東西移動到公共容器裏就能夠了

function People(name) {
        this.name = name // 每一個對象的name屬性時不一樣的,這點無可厚非
    }

    People.prototype.sayName = function () {
        console.log(this.name)
    } // 將sayName方法放入People的原型中

    var p1 = new People('sad')
    var p2 = new People('angry')
    console.log(p1.sayName())
    console.log(p2.sayName())
相關文章
相關標籤/搜索