Javascript之創建對象

来源:https://www.cnblogs.com/laixiangran/archive/2018/04/06/8727776.html
-Advertisement-
Play Games

Object構造函數 創建自定義對象最簡單的方式就是創建一個 Object 的實例,然後再為它添加屬性和方法: 缺點 代碼冗餘,會產生大量重覆代碼 無法識別對象(無法知道對象的類型) 對象字面量 對象字面量相比較於 Object 構造函數,代碼會比較直觀一些: 缺點 代碼冗餘,會產生大量重覆代碼 無 ...


Object構造函數

創建自定義對象最簡單的方式就是創建一個 Object 的實例,然後再為它添加屬性和方法:

// 創建對象
var person = new Object();

// 定義屬性
person.name = 'laixiangran';
person.age = 28;
person.job = 'Front End Software Engineer';

// 定義方法
person.sayName = function() {
    console.log(this.name);
};

person.sayName(); // 'laixiangran'

缺點

  • 代碼冗餘,會產生大量重覆代碼
  • 無法識別對象(無法知道對象的類型)

對象字面量

對象字面量相比較於 Object 構造函數,代碼會比較直觀一些:

var person = {
    name: 'laixiangran',
    age: 28,
    job: 'Front End Software Engineer',
    sayName:  function() {
        console.log(this.name);
    }
};

person.sayName(); // 'laixiangran'

缺點

  • 代碼冗餘,會產生大量重覆代碼
  • 無法識別對象(無法知道對象的類型)

工廠模式

Object 構造函數或對象字面量這兩種方法的缺點就是:使用同一個介面創建很多對象時,會產生大量的重覆代碼。為解決這個問題,我們將利用工廠模式來創建一個函數,這個函數將創建具體對象的過程進行封裝:

function creatPerson(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        console.log(this.name);
    };
    return o;
}

// 使用函數creatPerson創建對象
var person1 = creatPerson('laixiangran', 28, 'Front End Software Engineer');
person1.sayName(); // 'laixiangran'

var person2 = creatPerson('lai', 29, 'Back End Software Engineer');
person2.sayName(); // 'laixiangran'

通過creatPerson()能夠根據參數無數次地創建不同的對象,這樣就達到復用的目的,而且創建對象的細節是透明的。

工廠模式雖然解決了創建多個相似對象的問題,但是沒有解決對象識別的問題(即怎樣知道一個對象的類型)。

缺點

  • 無法識別對象(無法知道對象的類型)

構造函數模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        console.log(this.name);
    };
}

var person1 = new Person('laixiangran', 28, 'Front End Software Engineer');
person1.sayName(); // 'laixiangran'

var person2 = new Person('lai', 29, 'Back End Software Engineer');
person2.sayName(); // 'laixiangran'

在這個例子中,Person() 函數取代了 creatPerson() 函數,不同之處在於:

  • 沒有顯式地創建對象
  • 直接將屬性和方法賦給了 this 對象
  • 沒有return語句

因此,要創建 Person 的新實例,則必須使用 new 操作符。以這種方式調用構造函數實際上會經歷以下4個步驟:

  1. 創建一個新對象
  2. 將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象)
  3. 執行構造函數中的代碼(為這個新對象添加屬性和方法)
  4. 返回新對象

所以,當我們使用 new 操作符來調用構造函數時,實際就是隱式地完成 creatPerson() 函數要完成的那些工作。

當然,這種模式 解決了對象識別的問題 。在前面的例子中,person1 和 person2 分別保存著 Person 的一個不同的實例。這兩個對象都有一個 constructor(構造函數)屬性,該屬性指向 Person,這樣就達到對象識別了(能知道對象的類型)。還有,檢測對象類型,我們一般使用 instanceof 操作符。最後,我們知道所有的對象都是繼承自 Object 的,因此下麵的代碼都返回 true:

console.log(person1.constructor === Person); // true
console.log(instanceof person1 Person); // true
console.log(instanceof person1 Object); // true

console.log(person2.constructor === Person); // true
console.log(instanceof person2 Person); // true
console.log(instanceof person2 Object); // true

缺點

  • 每個方法都要在每個實例上重新創建一遍
  • 如果不想重新創建一遍,函數只能先在全局作用域中定義,但是這樣對於自定義對象來說就沒有封裝性可言了

原型模式

我們創建的每個函數都有一個 prototype(原型) 屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共用的屬性和方法。如果按照字面量意思來理解,那麼 prototype 就是通過 調用構造函數而創建 的那個 對象實例原型對象

不清楚 原型對象 可以先自行瞭解下,本文不展開介紹 原型對象,後面會寫文章單獨介紹。

使用原型對象的好處是可以讓所有的對象實例共用它所包含的屬性和方法。換句話說,不必在構造函數中定義對象實例的信息,而是可以將這些信息直接添加到原型對象中:

function Person() {
}

Person.prototype.name = 'laixiangran';
Person.prototype.age = 28;
Person.prototype.job = 'Front End Software Engineer';
Person.prototype.friends = ['xu', 'song'];
Person.prototype.sayName = function() {
    console.log(this.name);
};

var person1 = new Person();
person1.sayName(); // 'laixiangran'
person1.friends.push('chen');
console.log(person1.friends); // 'xu', 'song', 'chen'

var person2 = new Person();
person2.sayName(); // 'laixiangran'
console.log(person2.friends); // 'xu', 'song', 'chen'

console.log(person1.sayName === person2.sayName); // true
console.log(person1.friends === person2.friends); // true

缺點

  • 由於是所有實例共用屬性和方法,如果修改引用類型值的屬性(如對象、數組),那麼就會影響所有的對象實例(往往我們都希望每個實例都有自己的屬性)

組合使用構造函數模式和原型模式

創建自定義類型的最常見方式,就是組合使用構造函數模式與原型模式。構造函數模式用於定義實例屬性,而原型模式用於定義方法和共用屬性。這樣,每個實例都會有自己的一份實例屬性的副本,但同時又共用著對方法的引用,最大限度地節省了記憶體。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['xu', 'song'];
}

Person.prototype.sayName = function() {
    console.log(this.name);
};

var person1 = new Person('laixiangran', 28, 'Front End Software Engineer');
var person2 = new Person('lai', 29, 'Back End Software Engineer');

person1.friends.push('chen');
console.log(person1.friends); // 'xu', 'song', 'chen'
console.log(person2.friends); // 'xu', 'song'
console.log(person1.sayName === person2.sayName); // true
console.log(person1.friends === person2.friends); // false

這種混成模式集構造函數模式和原型模式這兩種模式之長。

缺點

  • 構造函數和原型分別獨立,代碼封裝型不強

動態原型模式

這種模式是對 組合使用構造函數模式和原型模式 方法的改進,它將所有信息都封裝在了構造函數中,而通過在構造函數中初始化原型(可僅在必要的情況下),又保持了 組合使用構造函數模式和原型模式 方法的優點。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    
    // 檢查是否存在sayName方法來決定是否初始化原型
    if (typeof this.sayName !== 'function') {
        Person.prototype.sayName = function() {
            console.log(this.name);
        };
    }
}

var person1 = new Person('laixiangran', 28, 'Front End Software Engineer');
person1.sayName(); // 'laixiangran'

其中,if 語句檢查可以是初始化之後應該存在的任何屬性和方法,不必對每個方法和屬性都判斷,只需要判斷其中一個即可。

寄生構造函數模式

function Person(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function() {
        console.log(this.name);
    };
    return o;
}

var person1 = new Person('laixiangran', 28, 'Front End Software Engineer');
person1.sayName(); // 'laixiangran'

var person2 = new Person('lai', 29, 'Back End Software Engineer');
person2.sayName(); // 'laixiangran'

除了使用 new 操作符並把使用的包裝函數叫做構造函數之外,這種模式其實和 工廠模式 是一模一樣的。構造函數中的 return 語句重寫了通過 new 操作符調用構造函數預設返回的新對象實例。

這種模式可以在特殊情況下用來為對象創建構造函數。假設我們想創建一個具有額外方法的特殊數組,但又不能直接修改 Array 構造函數:

function SpecialArray() {
    
    // 創建數組
    var values = new Array();
    
    // 添加值
    values.push.apply(values, arguments);
    
    // 添加方法
    values.toPipedString = function() {
        return this.join('|');
    };
    
    // 返回數組
    return values;
}

var colors = new SpecialArray('red', 'blue', 'green');
console.log(colors.toPipedString()); // 'red|blue|green'

缺點

  • 由於該模式返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係,因此,這種模式並不能通過 instanceof 操作符來確定對象類型。

穩妥構造函數模式

所謂穩妥對象,指的是沒有公共屬性,而且其方法也不引用 this 的對象。穩妥對象最適合在一些安全環境中(這些環境中會禁止使用 this 和 new),或者在防止數據被其他應用程式(如 Mashup 程式)改動時使用。

寄生構造函數模式 有兩點不同:

  1. 新創建對象的實例方法不引用 this
  2. 不使用 new 操作符調用構造函數
function Person(name, age, job) {

    // 創建要返回的對象
    var o = new Object();
    
    // 可以在這裡定義私有變數和方法

    // 添加方法
    o.sayName = function() {
        console.log(name);
    };
    
    // 返回對象
    return o;
}

var person1 = Person('laixiangran', 28, 'Front End Software Engineer');
person1.sayName(); // 'laixiangran'

變數 person1 中保存的是一個穩妥對象,除了調用 sayName() 方法外,沒有別的方式可以訪問其數據成員。

缺點

  • 寄生構造函數模式 一樣,由於該模式返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係,因此,這種模式並不能通過 instanceof 操作符來確定對象類型。

參考資料:《JavaScript高級程式設計(第3版)》第6.2節 創建對象


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

-Advertisement-
Play Games
更多相關文章
  • 只要調用函數進行執行,都必須加括弧。 函數是一個對象,函數名是指向這個對象的指針。 函數名後面加上括弧就表示立即調用執行這個函數裡面的代碼。 使用不帶圓括弧的函數是訪問函數的指針,而非調用函數。 函數名後面加括弧,就直接執行函數返回值。 函數名後面不加括弧,表示函數名賦值給click等的事件,觸發c ...
  • 最近做小程式開發,出於練手,也是工作需要,就做了個微信小程式的類似於酒店預訂的日曆插件。先上圖; 這個插件分為上下兩部分,上邊是tab欄,會根據當前的日期自動定位到當前,並展示以後7天的日期,下邊為內容展示,隨tab欄變化而變化。思路:首先用`new Data()`時間對象初始化時間,獲取當前的日期 ...
  • 原文地址: "A cartoon guide to Flux by Lin Clark" Flux在目前web開發中最受歡迎也較不被人理解,本文會以簡單易懂的方式解釋它。 出現問題 首先,我要聲明Flux所解決的基本問題。Flux是一種幫助你處理數據的模式。Flux和React都由Facebook開 ...
  • webpack優化方案 1. 優化開發體驗 1-1. 加快構建速度 ① 縮小文件搜索範圍 由於 Loader 對文件的轉換操作很耗時,需要讓儘可能少的文件被 Loader 處理,用include和exclude去縮小; resolve.modules用於配置 Webpack 去哪些目錄下尋找第三方模 ...
  • "— Java攻城獅學習路線 —" 一. JavaScript基礎 輸出 使用 window.alert() 彈出警告框。 使用 document.write() 方法將內容寫到 HTML 文檔中。 使用 innerHTML 寫入到 HTML 元素。 使用 console.log() 寫入到瀏覽器的 ...
  • 常用的一些快捷鍵: Windows + e 我的電腦Ctrl + Tab 網頁間不同頁面切換F2 重命名Ctrl+Shift+S 另存為 前端的一些常識:前端意義:將效果圖生成網頁網頁組成:文字、圖片、輸入框、視頻、音頻、超鏈接Web標準:Html 結構標準;Css 表現標準;Js 行為標準 瀏覽器 ...
  • 最近想用LayaBox做個小游戲,然而Laya本身不自帶構建工具。然後覺得寫模塊化的東西還是用webpack好使,用es6的語法也比較清晰。 於是就裝了webpack,只用babel-loader來編譯用es6寫的代碼。配置文件如下: 一開始我沒有設定mode,雖然我在babelrc裡面寫了comp ...
  • 一、對象冒充 其原理如下:構造函數使用 this 關鍵字給所有屬性和方法賦值(即採用類聲明的構造函數方式)。因為構造函數只是一個函數,所以可使 Parent 構造函數 成為 Children 的方法,然後調用它。Children 就會收到 Parent 的構造函數中定義的屬性和方法。原理:就是把 ... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...