摘要:面向對象之四創建特定對象的語法糖個人學習筆記分享為了講清楚面向對象實際上是需要幾個前置知識的。于是之父創造了這個語法糖。
JS面向對象之四 【new】 (創建特定對象的語法糖)
個人學習筆記分享
為了講清楚面向對象,實際上是需要幾個前置知識的。
包括前面的幾篇文章【原型鏈】 【this】 和今天要說的【new】
還是先說結論: new只是一個語法糖,這個語法糖被設計出來,使用場景是批量創建對象
從場景說起: 假設這個世界沒有new,我們如何批量創建一百個士兵obj?通篇文章,我們都是在實現這個需求,不斷優化,最終實現一個new語法糖。
類似這樣的士兵ojb
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個內存空間,十分浪費。
自然地,我們就想到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了
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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92407.html
摘要:首先為了模擬類創建對象的功能搞出了構造函數。也就是名字膚色膚色這里是繼承里的自有屬性生命值這里繼承的共有屬性的方法攻擊力兵種美國大兵攻擊防御死亡膚色 JS面向對象之五 【繼承】 我們已經準備了很多前置知識,包括 原型鏈,對象和對象之間的關系 this,對象和函數之間的關系 new, 用函數批量創建特定的對象的語法糖 JS面向對象的前世今生 我們說,面向對象是一種寫代碼的套路。因為如...
摘要:一面向對象概念面向對象就是使用對象。因此在構造函數中表示剛剛創建出來的對象。在構造函數中利用對象的動態特性為其對象添加成員。 一、面向對象 1.1 概念 面向對象就是使用對象。面向對象開發就是使用對象開發。 面向過程就是用過程的方式進行開發。面向對象是對面向過程的封裝。 1.2 三大特性 抽象性所謂的抽象性就是:如果需要一個對象描述數據,需要抽取這個對象的核心數據 提出需要的核心...
摘要:如果你已經對機制已有了解,但是由于兩者對象機制的巨大本質差異,對它和構造函數,實例對象的關系仍有疑惑,本文或許可以解答你的問題。所有的原型對象都會自動獲得一個屬性,這個屬性的值是指向原型所在的構造函數的指針。 幫助面向對象開發者理解關于JavaScript對象機制 本文是以一個熟悉OO語言的開發者視角,來解釋JavaScript中的對象。 對于不了解JavaScript 語言,尤其是習...
摘要:前言我們在深入淺出面向對象和原型概念篇在這篇文章中了解到了如何使用解決重復創建浪費內存的問題,其中的關鍵就是,那么這篇文章讓我們來重新了解的前世今生一個苦逼年級主任的故事開學啦高一年級主任龔主任需要為全年級每一位理科班新生錄入學號并為每一位 前言 我們在深入淺出面向對象和原型【概念篇2】在這篇文章中了解到了如何使用new Function解決重復創建浪費內存的問題,其中的關鍵就是new...
摘要:為啥我要自己實現一個語法糖為什么要自己實現一個語法糖呢因為之前對于里的語法糖一直是理論理解但是并親自嘗試實現過。直到有一天在頭條的面試中我聊了摸著自己的良心說我可以實現一個語法糖面試官嗯那你實現一個吧。我們知道構造函數一般是不寫的。 為啥我要自己實現一個new語法糖? 為什么要自己實現一個new語法糖呢? 因為之前對于JS里的new語法糖一直是理論理解,但是并親自嘗試實現過。 直到有一...
閱讀 3601·2021-11-23 09:51
閱讀 1473·2021-11-04 16:08
閱讀 3547·2021-09-02 09:54
閱讀 3616·2019-08-30 15:55
閱讀 2595·2019-08-30 15:54
閱讀 958·2019-08-29 16:30
閱讀 2047·2019-08-29 16:15
閱讀 2317·2019-08-29 14:05