JavaScript創建對象—從es5到es6

来源:https://www.cnblogs.com/ImmortalWang/archive/2019/03/12/10517091.html
-Advertisement-
Play Games

原文地址 本文主要講述了使用JavaScript創建對象的幾種方式,分別是傳統的Object構造函數、對象字面量、工廠模式、構造函數模式、原型模式、組合模式,以及es6的class定義類。然後從babel的角度探究es5與es6創建對象的區別。 1.創建對象的幾種方式 (1).Object構造函數和 ...


原文地址

本文主要講述了使用JavaScript創建對象的幾種方式,分別是傳統的Object構造函數、對象字面量、工廠模式、構造函數模式、原型模式、組合模式,以及es6的class定義類。然後從babel的角度探究es5與es6創建對象的區別。

1.創建對象的幾種方式

(1).Object構造函數和對象字面量

在早期js開發中,很多開發者會使用Object構造函數的方式來創建一個對象,通過調用Object構造函數new一個Object對象,然後再給這個對象的每一個屬性和方法進行賦值

1 var person = new Object();
2 person.age = 22;
3 person.name = 'Dolanf';
4 person.code = function() {
5   console.log(‘hello world!’);
6 };

後來出現了對象字面量的寫法,由於使用對象字面量創建對象的寫法簡單直觀,所以Object構造函數寫法漸漸被對象字面量的寫法所取代,對象字面量是通過在一個大括弧裡面使用鍵值對的方式表示每一個屬性和方法,每一個鍵值對之間使用逗號隔開

1 var person = {
2     age: 22,
3     name: 'Dolanf',
4     code: function() {
5          console.log('hello world!');
6     }
7 }

雖然對象字面量簡單直觀,但是上面兩種方法都存在一個共同的問題:當需要創建很多很多個Person對象的時候,只能一個一個去創建,每一個對象的方法和屬性都需要單獨寫,這使得代碼沒有絲毫復用性可言,違背了對象封裝的特性。於是乎,工廠模式就隨之出現了

(2)工廠模式

工廠模式通過將對象的創建封裝到一個方法中,再通過在調用該方法時傳入參數而實現對象的實例化,解決了以上提到的產生大量重覆代碼的問題

 1 function createPerson(age, name) { 
 2     var o = new Object();
 3     o.age = age;
 4     o.name = name;
 5     o.code = function() {
 6          console.log('hello world!');
 7     };
 8 
 9     return o;
10 }
11 
12 var person1 = createPerson(11, '小白');
13 var person2 = createPerson(12, '小黑');

但是工廠模式也存在一個不足,就是通過該方法創建的對象的構造函數全都是Object,沒有辨識度。沒有辦法通過構造函數辨別一個對象到底是Person還是Dog,亦或是Cat。於是乎,為瞭解決這個問題,就引入了構造函數模式。

(3)構造函數模式

構造函數模式就是通過定義一個function函數,然後通過this給對象的屬性和方法進行賦值。當我們實例化對象時,只需在該函數前面加一個new關鍵字就可以了。

 1 function Person(age, name) { 
 2     this.age = age;
 3     this.name = name;
 4     this.code = function() {
 5          console.log('hello world!');
 6     };
 7 }
 8 
 9 var person1 = new Person(11, '小白');
10 var person2 = new Person(12, '小黑');

構造函數模式解決了工廠模式中的對象識別問題,通過:

1 console.log(person1 instanceof Person);  // true

可以看出person1能成功被識別為一個Person對象。
但是,構造函數模式也同樣存在一個缺點,就是構造函數里的屬性和方法在每個對象上都要實例化一遍,包括對象共用的屬性和方法,這樣就造成了代碼的復用性差的問題。所以大多數人會考慮將構造函數模式和原型模式組合起來使用。在這裡先介紹一下原型模式。

(4)原型模式

 原型模式是通過將所有的屬性和方法都定義在其prototype屬性上,達到這些屬性和方法能被所有的實例所共用的目的。代碼如下所示:

 1 function Person(age, name) { 
 2     Person.prototype.age = age;
 3     Person.prototype.name = name;
 4     Person.prototype.code = function() {
 5          console.log('hello world!');
 6     };
 7 }
 8 
 9 var person1 = new Person();
10 var person2 = new Person();

當然,這種方法在項目開發中是沒有人會使用的,因為當一個對象上的屬性改變時,所有對象上的屬性也會隨之改變,這是非常不切實際的。在這裡提及原型模式是為了介紹以下的構造函數+原型組合模式.

(5)構造函數+原型組合模式

組合模式是將構造函數模式和原型模式結合在一起,繼承了它們優點的同時又避免了各自的缺點。它將具有各自特點的屬性和方法定義在構造函數中,將實例間共用的屬性和方法定義在prototype上,成為了在es6出現之前使用最普遍的一種創建對象模式。

 1 function  Person(age, name) { 
 2     this.age = age;
 3     this.name = name;
 4     this.cry = function() {
 5          console.log(name + 'is crying!!! T^T');
 6     }
 7 }
 8 Person.prototype = {
 9     constructor: Person,
10     sayName: function() {
11         console.log(this.name);
12     }
13 }
14 var person1 = new Person(11, '小白');
15 var person2 = new Person(12, '小黑');

(6)class定義類

當然,前面講的都是浮雲,現在大家都用class定義類啦,class的出現就是為了讓定義類能更加簡單。回到上面的Person構造函數上,我們現在將其改造成使用class定義的方式:

 1 class Person{ 
 2     constructor(age, name) {
 3         this.age = age;
 4         this.name = name;
 5         this.cry = function() {
 6          console.log(name + 'is crying!!! T^T');
 7         }
 8     }
 9     sayName() {
10         console.log(this.name);
11     }
12 }
13 var person1 = new Person(11, '小白');
14 var person2 = new Person(12, '小黑');

使用class定義類跟上面的構造函數+原型組合模式有一些相似之處,但又有所區別。
class定義的類上有個constructor方法,這就是構造方法,該方法會返回一個實例對象,this代表的就是實例對象,這跟上邊的構造函數模式很類似。
此外,class上的方法都是定義在prototype上的,這又跟原型模式有一些相似之處,這個class里的sayName等價於

1 Person.protorype.sayName = function() {
2     console.log(this.name);
3 }

雖然class定義的類跟es5中的構造函數+原型組合模式很相似,但是他們還是存在不少區別的,下麵對比如下:

2.es5與es6定義對象的區別

1)class的構造函數必須使用new進行調用,普通構造函數不用new也可執行。
2)class不存在變數提升,es5中的function存在變數提升。
3)class內部定義的方法不可枚舉,es5在prototype上定義的方法可以枚舉。

為什麼會存在以上這些區別呢?下麵使用babel將es6轉化成es5看看它的實現過程就知道了。

3.es6中class轉化為es5

下麵將講述使用babel將以上的class定義Person類轉換成使用es5實現:

es6代碼:

 1 class Person{ 
 2     constructor(age, name) {
 3         this.age = age;
 4         this.name = name;
 5         this.cry = function() {
 6          console.log(name + 'is crying!!! T^T');
 7         }
 8     }
 9     sayName() {
10         console.log(this.name);
11     }
12 }
13 var person1 = new Person(11, '小白');
14 var person2 = new Person(12, '小黑');

使用babel轉化成的es5後的代碼:

 1 'use strict'; // es6中class使用的是嚴格模式
 2 
 3 // 處理class中的方法
 4 var _createClass = function () { 
 5    function defineProperties(target, props) { 
 6       for (var i = 0; i < props.length; i++) { 
 7          var descriptor = props[i]; 
 8          // 預設不可枚舉
 9          descriptor.enumerable = descriptor.enumerable || false; 
10          descriptor.configurable = true; 
11          if ("value" in descriptor) descriptor.writable = true; 
12          Object.defineProperty(target, descriptor.key, descriptor);
13       } 
14    } 
15    return function (Constructor, protoProps, staticProps) { 
16       if (protoProps) defineProperties(Constructor.prototype, protoProps); 
17       if (staticProps) defineProperties(Constructor, staticProps); 
18       return Constructor; 
19    }; 
20 }();
21 
22 // 對構造函數進行判定
23 function _classCallCheck(instance, Constructor) { 
24    if (!(instance instanceof Constructor)) { 
25       throw new TypeError("Cannot call a class as a function"); 
26    }
27 }
28 
29 // class Person轉換為 es5的function
30 var Person = function () {
31     function Person(age, name) {
32         // 調用了_classCallCheck檢查Person是否為構造函數
33         _classCallCheck(this, Person); 
34 
35         this.age = age;
36         this.name = name;
37         this.cry = function () {
38             console.log(name + 'is crying!!! T^T');
39         };
40     }
41 
42     // 調用_createClass處理定義在class中的方法。
43     _createClass(Person, [{
44         key: 'sayName',
45         value: function sayName() {
46             console.log(this.name);
47         }
48     }]);
49 
50     return Person;
51 }();
52 
53 var person1 = new Person(11, '小白');
54 var person2 = new Person(12, '小黑');

這裡我將轉換後的代碼格式化並加上了一些註釋。
從以上代碼可以看出,class主要是通過兩個函數實現:_createClass和_classCallCheck。
所以為什麼會存在上述區別呢:
1)class的構造函數必須使用new進行調用,普通構造函數不用new也可執行。
class中的constructor會直接轉化為function構造函數,然後在function中通過 _classCallCheck的檢查該function是否是一個Constructor。因為有_classCallCheck檢查必須是instanceof Constructor,所以class必須使用new進行調用。

2)class不存在變數提升,es5中的function存在變數提升。 
class轉變成了函數表達式進行聲明,因為是函數表達式聲明的,所以class不存在變數提升。

3)class內部定義的方法不可枚舉,es5在prototype上定義的方法可以枚舉。 
class中定義的方法會傳入 _createClass中,然後 Object.defineProperty將其定義在Constructor.prototype上。所以class中的方法都是定義在Constructor.prototype上的。
由於defineProperties中的

1 descriptor.enumerable = descriptor.enumerable || false;

將屬性的 enumerable預設為false,所以class中定義的方法不可枚舉。

第一次寫博客,內容也是copy原作者,所有代碼都有手擼驗證過一遍,加深印象。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.px 和 em 和r em 的區別? px像素,相對長度單位; em相對長度單位,會繼承父元素的字體大小; rem相對長度單位,會根據節點html定義,不會受父元素的影響。 2.如何理解css的盒子模型? 盒子模型包含內容的大小,padding,border,margin css盒子模型分為IE ...
  • 一、面試題 問:有一個長度為 100 的數組,如何從中隨機挑選 50 個元素,組成一個新的數組? 答:這個...那個...emmmmmm 問:那先不挑 50 個,就挑一個數,知道怎麼做嗎? 答:這個我知道!隨機生成一個 0 ~ 99 的數,然後去原數組取對應位置的元素就可以了~ 問:好,回到最初的 ...
  • cors方式 之前在chrome控制台看到金蟬同一個請求有的時候會發送兩個(多出一個OPTION的情況)的情況,有點奇怪,經過一頓研究發現原來這個是 實現跨域 的處理方式。具體內容參見阮大神的文檔 "跨域資源共用 CORS 詳解" 。 jsonp方式 原理 利用` ...
  • Tarball 提供了一種在 Linux 系統上備份和管理一組文件的通用方法。請按照以下提示瞭解如何創建它們,以及從中提取和刪除單個文件。 “tarball” (LCTT 譯註:國內也常稱為“tar 包”)一詞通常用於描述備份一組選擇的文件並將它們打包在一個文件中的一種文件格式。該名稱來自 .tar ...
  • 問題描述 後端使用如下方式接收前端傳入參數: 由於使用了 @RequestBody 註解,所以,前端需要傳遞 JSON 數據。那麼,如何將表單數據快速轉換為 JSON 字元串呢? 定義如下通用方法: 以上方法會返回一個 Object,然後再通過 JSON.stringify(obj) 將 JSON ...
  • 一、Promise 的含義 Promise 是非同步編程的一種解決方案,所謂Promise,簡單來說就是一個容器,裡面保存著一個非同步操作的結果。 Promise對象有以下兩個特點: 1、對象的狀態不受外界的影響。Promise對象代表一個非同步操作,有三種狀態:pending(進行中)、fulfille ...
  • 前沿 項目地址 "vue admin" 歡迎 star 近幾個月,接手了一個老項目的重構規劃,有多老呢?就是前端青銅時代的項目,一個前後端都在同一個鍋里的項目、完全沒有使用任何的打包工具。 後臺 + 渲染頁面 前端 、`css js jQuery` 複製粘貼就是乾。 前端不夠後端來湊。如果前端有一些 ...
  • 插槽用於內容分發,存在於子組件之中。 插槽作用域 父級組件作用域為父級,子級組件作用域為子級,在哪定義的作用域就在哪。 子組件之間的內容是在父級作用域的,無法直接訪問子組件裡面的數據。 插槽元素 <slot></slot> 或 <slot name="名稱">預設值</slot> 1:如果定義了sl ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...