JS面向對象之四 【new】 (建立特定對象的語法糖)

JS面向對象之四 【new】 (建立特定對象的語法糖)

我的學習筆記分享函數

爲了講清楚面向對象,其實是須要幾個前置知識的。
包括前面的幾篇文章【原型鏈】 【this】 和今天要說的【new】性能

仍是先說結論: new只是一個語法糖,這個語法糖被設計出來,使用場景是批量建立對象學習

從場景提及: 假設這個世界沒有new,咱們如何批量建立一百個士兵obj?

通篇文章,咱們都是在實現這個需求,不斷優化,最終實現一個new語法糖。優化

相似這樣的士兵ojbthis

var 士兵 = {
    ID: 1,
    兵種: '美國大兵',
    攻擊力: 5,
    生命值: 42,
    攻擊: function(){console.log('攻擊')},
    防護: function(){console.log('防護')},
    死亡: function(){console.log('死亡')},
}

版本v1.0: 最直覺的方法固然是循環100次

var 士兵
var 士兵們 = []
for (var i = 0 ; i < 100 ; i++){
     士兵 = {
        ID: 1,
        兵種: '美國大兵',
        攻擊力: 5,
        生命值: 42,
        攻擊: function(){console.log('攻擊')},
        防護: function(){console.log('防護')},
        死亡: function(){console.log('死亡')},
    }
    士兵們.push(士兵)
}

最終咱們獲得了array士兵們

根據第二篇文章 JS面向對象之二【原型鏈】(對象和對象的關係).md),咱們知道這裏的攻擊、防護、死亡都是匿名函數,在內存空間都會佔據空間。
這些函數的內容徹底相同,300個匿名函數卻要佔用300個內存空間,十分浪費。prototype

天然地,咱們就想到JS的原型鏈就是專門解決這個問題的,咱們把共同的屬性放到__proto__裏,全部士兵的__proto__都指向相同的內存空間。設計

版本v1.1: 優化內存空間,使用__proto__保存共同的屬性

var 士兵
var 士兵們 = []

for (var i=0 ; i<100 ; i++){
    士兵 = {
        ID: 1,
        生命值: 42
    }
   士兵.__proto__ = 士兵共
   士兵們.push(士兵)
}

var 士兵共 = {
    兵種: '美國大兵',     // 若是兵種和攻擊力也是同樣的話,那麼也須要放到__proto__裏去共享
    攻擊力: 5,
    攻擊: function(){console.log('攻擊')},
    防護: function(){console.log('防護')},
    死亡: function(){console.log('死亡')},
}

最終依舊獲得了array士兵們

(以上代碼可粘貼運行)
如今咱們省掉了以前297個匿名函數所佔用的空間。讓他們的__proto__都指向'士兵共'這個內容空間就OK了code

版本v1.2: 變量名稱優化成英文

var soldier
var soldiers= []

for (var i=0 ; i<100 ; i++){
    soldier = {
        ID: 1,
        生命值: 42
    }
   soldier.__proto__ = soldierCommon
   soldiers.push(soldier)
}

var soldierCommon = {
    兵種: '美國大兵',     // 若是兵種和攻擊力也是同樣的話,那麼也須要放到__proto__裏去共享
    攻擊力: 5,
    攻擊: function(){console.log('攻擊')},
    防護: function(){console.log('防護')},
    死亡: function(){console.log('死亡')},
}

版本v2.0: 封裝成createSoldier函數,避免意大利式麪條,讓代碼更加集中。

var soldierCommon = {
        兵種: '美國大兵',     // 若是兵種和攻擊力也是同樣的話,那麼也須要放到__proto__裏去共享
        攻擊力: 5,
        攻擊: function(){console.log('攻擊')},
        防護: function(){console.log('防護')},
        死亡: function(){console.log('死亡')},
    }
    
    function createSoldier(){
        var obj = {
            ID: 1,
            生命值: 42
        }
        obj.__proto__ = soldierCommon
        return obj
    }
    
------分割線以上是建立構造函數, 分割線如下,是使用構造函數------

    var soldiers = []
    for(var i = 0 ;i<100 ; i++){
        soldiers.push(createSoldier()) 
    }

版本v2.1: 讓開發者一眼就知道soldierCommon和createSoldier是有關係的,讓代碼進一步集中

// 讓soldierCommon成爲構造函數的屬性(由於函數也是obj,固然能夠有屬性)
    // soldierCommon能夠改叫xxx,只是一個名字,只是爲了讓這個對象和createSoldier產生聯繫
    
    createSoldier.xxx = {
        兵種: '美國大兵',     
        攻擊力: 5,
        攻擊: function(){console.log('攻擊')},
        防護: function(){console.log('防護')},
        死亡: function(){console.log('死亡')},
    }
    
    function createSoldier(){
        var obj = {
            ID: 1,
            生命值: 42
        }
        obj.__proto__ = createSoldier.xxx
        return obj
    }
    
------分割線以上是建立構造函數, 分割線如下,是使用構造函數------

    var soldiers = []
    for(var i = 0 ;i<100 ; i++){
        soldiers.push(createSoldier()) 
    }

那麼如今還能夠優化嗎? 上面這段代碼,惟一的缺點就是xxx不太容易理解。對象

因此,JS之父爲了容易理解將這個xxx,也就是共有屬性的引用,統一叫作prototype,雖然prototype對於中國人來講依舊很差理解。內存

版本v2.2: 完美的代碼,將xxx改成prototype (只是一個名字,可是JS之父將它命名成了prototype)

createSoldier.prototype = {
        兵種: '美國大兵',     
        攻擊力: 5,
        攻擊: function(){console.log('攻擊')},
        防護: function(){console.log('防護')},
        死亡: function(){console.log('死亡')},
    }
    
    function createSoldier(){
        var obj = {
            ID: 1,
            生命值: 42
        }
        obj.__proto__ = createSoldier.prototype
        return obj
    }
    
------分割線以上是建立構造函數, 分割線如下,是使用構造函數------

    var soldiers = []
    for(var i = 0 ;i<100 ; i++){
        soldiers.push(createSoldier()) 
    }

好了,仔細看看上面的代碼,還有什麼可優化的地方嗎?

內存、變量名、代碼集中都作了優化,也就是範例式的代碼。

那麼,JS之父也認爲這個就是最好的代碼,他但願,當JS開發者們須要批量建立特定對象時,都這樣寫。

因而,JS之父創造了new這個語法糖。

版本v3.0 讓咱們看看JS之父創造的new語法糖

function Soldier(){
     this.ID  = 1
     this.生命值 = 42
 }
 
 Soldier.prototype.兵種 = '美國大兵'
 Soldier.prototype.攻擊力 = '65
 Soldier.prototype.攻擊 = function(){console.log('攻擊')}
 Soldier.prototype.防護 = function(){console.log('防護')}
 Soldier.prototype.死亡 = function(){console.log('死亡')}
 
 ------分割線以上是建立構造函數, 分割線如下,是使用構造函數------
 
 var soldiers = []
 for(var i = 0 ;i<100 ; i++){
     soldiers.push( new Soldier()) 
 }

對比v2.2 和 v3.0 這兩個版本,就是new幫咱們作的事情。

v2.2 沒有使用new語法的函數createSoldier
function createSoldier(){
        var obj = {
            ID: 1,
            生命值: 42
        }
        obj.__proto__ = createSoldier.prototype
        return obj
    }
v3.0 使用了new語法的函數Soldier
function Soldier(){
     //   this = {}                                          JS偷偷作的第1個事情                               
     //   this.__proto__ = Soldier.prototype                 JS偷偷作的第2個事情 
     
        this.ID  = 1
        this.生命值 = 42
         
     // return this                                          JS偷偷作的第3個事情 
         
     }
     
     Soldier.prototype = {                                  JS偷偷給Soldier.prototype加了個constructor屬性
        constructor : Soldier
     }
對比一下,當你使用new語法,JS之父幫你作了什麼事情
  • 第一 , JS幫你在函數裏建立了一個this空對象,而且這個this指向是構造函數的實例
  • 第二 , JS幫你讓this的__proto__ 指向 構造函數的原型,也就是prototype
  • 第三 , JS幫你偷偷return了 this
  • 第四 , JS偷偷給Soldier.prototype加了個constructor屬性
其餘注意事項
  • 構造函數首字母大寫,習慣寫法
  • __proto__不能在生產環境出現,由於會嚴重影響性能
  • 用new調用的時候,也無法用call了,由於JS之父幫你call了,this的指向就像你見到的指向實例

因此使用了new,就可讓代碼沒有廢話。

你只須要設置對象的自有屬性和共有屬性。JS幫你建立空對象,幫你搞定this指向,幫你改變this的__proto__,幫你return this

相關文章
相關標籤/搜索