目錄
最簡單的方式
物件字面量形式
使用Object建構函式
工廠模式
構造函數模式
原型模式
组合使用构造函数模式和原型模式
动态原型模式
寄生构造函数模式
稳妥构造函数模式
结语
首頁 web前端 js教程 JS物件創建常用方式有哪些?

JS物件創建常用方式有哪些?

Jun 28, 2017 am 09:55 AM
javascript 原理 方式

前言

俗話說“在js語言中,一切都物件”,而且創建物件的方式也有很多種,所以今天我們做一下梳理

最簡單的方式

JavaScript建立物件最簡單的方式是:物件字面量形式或使用Object建構子

物件字面量形式

1 var person = new Object();2   person.name = "jack";3   person.sayName = function () {4   alert(this.name)5 }
登入後複製

使用Object建構函式

1 var person = {2   name: "jack";3   sayName: function () {4     alert(this.name)5   }6 }
登入後複製

明顯缺點:建立多個物件時,會出現程式碼重複,於是乎,'工廠模式'應運而生

工廠模式

通俗一點來理解工廠模式,工廠:「我創建一個對象,創建的過程全由我來負責,但任務完成後,就沒我什麼事兒了啊O(∩_ ∩)O哈哈~」

 1 function createPerson (name) { 2   var o = new Object(); 3   o.name = name; 4   o.sayName = function () { 5     alert(this.name) 6   } 7   return o 8 } 9 10 var p1 = new createPerson("jack");
登入後複製

#明顯缺點:所有的物件實例都是`Object`類型,幾乎類型區分可言啊!你說無法分辨類型,就無法區分啊,我偏不信!那咱們就來看程式碼吧:

1 var p1 = new createPerson("jack");2 var p2 = new createPerson("lucy");3 4 console.log(p1 instanceof Object); //true5 console.log(p2 instanceof Object); //true
登入後複製

你看,是不是這個理兒;所以為了解決這個問題,我們採用'建構子模式'

構造函數模式

建構函數模式,就是這個函數我只管創建某個類型的物件實例,其他的我一概不管(注意到沒有,這裡已經有點類型的概念了,感覺就像是在搞小團體嘛)

 1 function Person (name) { 2   this.name = name; 3   this.sayName = function () { 4     alert(this.name) 5   } 6 } 7  8 function Animal (name) { 9   this.name = name;10   this.sayName = function () {11     alert(this.name)12   }13 }14 15 var p1 = new Person("jack")16 p1.sayName() //"jack"17 18 var a1 = new Animal("doudou")19 a1.sayName() //"doudou"20 21 console.log(p1 instanceof Person) //true22 console.log(a1 instanceof Animal) //true23 console.log(p1 instanceof Animal) //false(p1显然不是Animal类型,所以是false)24 console.log(a1 instanceof Person) //false(a1也显然不是Person类型,所以同样是false)
登入後複製

上面這段程式碼證明:建構函式模式的確可以做到物件類型的區分。那麼該模式是不是已經完美了呢,然而並不是,我們來一起看看下面的程式碼:

1 //接着上面的代码2 console.log(p1.sayName === a1.sayName) //false
登入後複製

發現問題了嗎? `p1`的`sayName`竟然和`a1`的`sayName`不是同一個,這說明什麼?說明'建構函式模式'根本就沒有'公用'的概念,創建的每個物件實例都有自己的一套屬性和方法,'屬性是私有的',這個我們可以理解,但方法你都要自己搞一套,這就有點沒必要了
明顯缺點:上面已經描述了,為了解決這個問題,又出現了一種新模式'原型模式',該模式簡直就是一個階段性的跳躍,下面我們來看分一下'原型模式'

原型模式

這裡要記住一句話:建構子中的屬性和方法在每個物件實例之間都不是共享的,都是各自搞一套;而要實現共享,就要將屬性和方法存到建構函數的原型中。這句話什麼意思呢?下面我們來詳細解釋
當建立一個建構函式時(普通函式亦然),會自動產生一個`prototype`(原型),建構函式與`prototype`是一對一的關係,並且此時`prototype `中只有一個`constructor`屬性(哪有,明明還有一個`__proto__`呢,這個我們先不在此討論,後面會有解釋)

這個` constructor`是什麼?它是一個類似於指標的引用,指向該`prototype`的建構函數,並且該指標在預設的情況下是一定存在的

1 console.log(Person.prototype.constructor === Person) //true
登入後複製

剛才說過`prototype`是`自動產生`的,其實還有另一種手動方式來產生`prototype`:

1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   //constructor: Person,6   age: 307 }8 console.log(Person.prototype) //Object {age: 30}9 console.log(Person.prototype.constructor === Person) //false
登入後複製

Tips:為了證明的確可以為構造函數手動建立`prototype`,這裡給`prototype`加了`name`屬性。
可能你已經注意到了一個問題,這行程式碼:

1 console.log(Person.prototype.constructor === Person) //false
登入後複製

結果為什麼是`false`啊?大哥,剛才的`prototype`是預設產生的,然後我們又用了另一種方式:手動設定。具體分析一下手動設定的原理:
1.建構函數的`prototype`其實也是一個物件

2.當我們這樣設定`prototype`時,其實已經將原先`Person.prototype`給切斷了,然後又重新引用了另外一個物件

3.此時建構子可以找到`prototype`,但`prototype `找不到建構子了

1 Person.prototype = {2   //constructor: Person, // 因为constructor属性,我没声明啊,prototype就是利用它来找到构造函数的,你竟然忘了声明3   age: 304 }
登入後複製

4.所以,要想顯示手動設定建構函式的原型,又不失去它們之間的聯繫,我們就要這樣:

1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   constructor: Person, //constructor一定不要忘了!!6   age: 307 }
登入後複製

画外音:“说到这里,你还没有讲原型模式是如何实现属性与方法的共享啊”,不要急,马上开始:

对象实例-构造函数-原型,三者是什么样的关系呢?

看明白这张图的意思吗?
1.当对象实例访问一个属性时(方法依然),如果它自身没有该属性,那么它就会通过`__proto__`这条链去构造函数的`prototype`上寻找
2.构造函数与原型是一对一的关系,与对象实例是一对多的关系,而并不是每创建一个对象实例,就相应的生成一个`prototype`
这就是原型模式的核心所在,结论:在原型上声明属性或方法,可以让对象实例之间共用它们

然后原型模式就是完美的吗?并不是,它有以下两个主要问题:
问题1:如果对象实例有与原型上重名的属性或方法,那么,当访问该属性或方法时,实例上的会屏蔽原型上的

1 function Person (name) {2   this.name = name3 }4 Person.prototype = {5   constructor: Person,6   name: 'lucy'7 }8 var p1 = new Person('jack');9 console.log(p1.name); //jack
登入後複製

问题2:由于实例间是共享原型上的属性和方法的,所以当其中一个对象实例修改原型上的属性(基本值,非引用类型值或方法时,其他实例也会受到影响


原因就是,当实例自身的基本值属性与原型上的重名时,实例就会创建该属性,留着今后自己使用,而原型上的属性不会被修改;但如果属性是引用类型值,如:`Array`、`Object`,当发生重名时,实例是不会拷贝一份新的留给自己使用的,还是坚持实例间共享,所以就会出现上图中的情况

以上两个问题就是原型模式的明显缺点,为了改掉这些缺点,我们一般会采用一种组合模式“组合使用构造函数模式和原型模式”,其实在原型模式这一节,该模式已经有所应用了

组合使用构造函数模式和原型模式

这种模式可谓是集构造函数模式和原型模式之所长,用构造函数模式来定义对象实例的属性或方法,而共享的属性或方法就交给原型模式

 1 function Person (name) { 2   this.name = name //实例的属性,在构造函数中声明 3 } 4  5 Person.prototype = { 6   constructor: Person, 7   sayName: function () { //共享的方法存在原型中 8     alert(this.name) 9   }10 }
登入後複製

注:此模式目前是ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法

-----------------

下面要介绍的几个模式是针对不同场景的,而不是说`组合使用构造函数模式和原型模式`有什么缺点,又用这几个模式来弥补,不是这样的

动态原型模式

特点:共享的方法是在构造函数中检测并声明的,原型并没有被显示创建

 1 function Person (name) { 2   this.name = name; 3   if (typeof this.sayName !== 'function') { //检查方法是否存在 4     console.log('sayName方法不存在') 5     Person.prototype.sayName = function () { 6       alert(this.name) 7     } 8   } else { 9     console.log('sayName方法已存在')10   }11 }12 13 var p1 = new Person('jack'); //'sayName方法不存在'14 p1.sayName(); //因为sayName不存在,我们来创建它,所以这里输出'jack'15 var p2 = new Person('lucy'); //'sayName方法已存在'16 p2.sayName(); //这时sayName已存在,所以输出'lucy'
登入後複製

当`Person`构造函数第一次被调用时,`Person.prototype`上就会被添加`sayName`方法;《Javascript高级程序设计》一书说到:使用动态原型模式时,不能使用对象字面量重写原型。我们来理解一下:

 

分析:
1.`p1`实例创建,此时原型没有`sayName`方法,那我们就为原型添加一个
2.随后,我们以字面量的形式重写了原型,这时旧的原型并没有被销毁,而且它和`p1`还保持着联系
3.之后的实例,也就是这里的`p2`,都是与新原型保持联系;所以`p1`、`p2`有各自的构造器原型,即使它们的构造器是同一个

所以切记:当我们采用动态原型模式时,千万不要以字面量的形式重写原型

寄生构造函数模式

了解此模式之前,我们先来想一个问题:构造函数为什么要用`new`关键字调用?代码说话:

我们发现什么?如果不是`new`方法调用构造函数,那么就要显式的`return`,否则构造函数就不会有返回值;但如果使用`new`,那就没有这个问题了

下面我们再来看寄生构造函数模式:

 1 function Person (name) { 2   var o = new Object(); 3   o.name = name; 4   o.sayName = function () { 5     alert(this.name) 6   }; 7   return o 8 } 9 10 var p1 = new Person('jack'); //与工厂模式唯一不同之处:使用new调用11 p1.sayName(); //jack
登入後複製

其实new不new都无所谓,因为我们已经显式的return o

那么寄生构造函数模式到底有什么应用场景呢?据《javascript高级程序设计》一书记载,举例:如果我们想创建一个具有额外方法的特殊数组,那么我们可以这样做:

 1 function SpecialArray () { 2   var values = new Array(); 3   Array.prototype.push.apply(values,arguments); 4     values.toPipedString = function () { 5     return this.join('|') 6   } 7   return values 8 } 9 10 var colors = new SpecialArray('red','blue','green');11 alert(colors.toPipedString()) //'red|blue|green'
登入後複製

最后重要的一点:该模式和构造函数和原型无缘,也就是不能区分实例类型,因为该模式生成的实例,它的构造函数都是Object,原型都是Object.prototype

稳妥构造函数模式

该模式与寄生构造函数相比,主要有两点不同:
1.创建对象实例的方法不引用this
2.不使用new操作符调用构造函数
按照稳妥构造函数的要求,可以将前面的Person构造函数重写如下:

1 function Person (name) {2   var o = new Object();3   o.sayName = function () {4     alert(name) //这里其实涉及到了闭包的知识,因此产生了私有属性的概念5   }6   return o7 }
登入後複製

此模式最适合在一些安全的环境中(这些环境中会禁止使用this和new),同理,此模式与构造函数和原型也无缘

结语

以上就是对js中创建对象的方式的总结,希望对大家有所帮助

以上是JS物件創建常用方式有哪些?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

nohup的作用及原理解析 nohup的作用及原理解析 Mar 25, 2024 pm 03:24 PM

nohup的作用及原理解析在Unix和類Unix作業系統中,nohup是一個常用的命令,用於在後台運行命令,即便用戶退出當前會話或關閉終端窗口,命令仍然能夠繼續執行。在本文中,我們將詳細解析nohup指令的作用和原理。一、nohup的作用後台運行命令:透過nohup命令,我們可以讓需要長時間運行的命令在後台持續執行,而不受用戶退出終端會話的影響。這在需要運行

深入探討Struts框架的原理與實踐 深入探討Struts框架的原理與實踐 Feb 18, 2024 pm 06:10 PM

Struts框架的原理解析與實務探索Struts框架作為JavaWeb開發中常用的MVC框架,具有良好的設計模式和可擴展性,廣泛應用於企業級應用程式開發中。本文將對Struts框架的原理進行解析,並結合實際程式碼範例進行探索,幫助讀者更好地理解和應用該框架。一、Struts框架的原理解析1.MVC架構Struts框架是基於MVC(Model-View-Con

深入理解MyBatis中的批次Insert實作原理 深入理解MyBatis中的批次Insert實作原理 Feb 21, 2024 pm 04:42 PM

MyBatis是一款流行的Java持久層框架,廣泛應用於各種Java專案。其中,批次插入是常見的操作,可以有效提升資料庫操作的效能。本文將深入探討MyBatis中批量的Insert實作原理,並結合具體的程式碼範例進行詳細解析。 MyBatis中的批次Insert在MyBatis中,批量Insert操作通常使用動態SQL來實作。透過建構一條包含多個插入值的S

深入探討Linux RPM工具的功能與原理 深入探討Linux RPM工具的功能與原理 Feb 23, 2024 pm 03:00 PM

Linux系統中的RPM(RedHatPackageManager)工具是安裝、升級、解除安裝和管理系統軟體套件的強大工具。它是RedHatLinux系統中常用的軟體包管理工具,也被許多其他Linux發行版採用。 RPM工具的角色非常重要,它使得系統管理員和使用者能夠方便地管理系統上的軟體包。透過RPM,使用者可以輕鬆安裝新的軟體包,升級現有的軟體

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

MyBatis分頁插件原理詳解 MyBatis分頁插件原理詳解 Feb 22, 2024 pm 03:42 PM

MyBatis是一個優秀的持久層框架,它支援基於XML和註解的方式操作資料庫,簡單易用,同時也提供了豐富的插件機制。其中,分頁插件是使用頻率較高的插件之一。本文將深入探討MyBatis分頁外掛的原理,並結合具體的程式碼範例進行說明。一、分頁外掛原理MyBatis本身並沒有提供原生的分頁功能,但可以藉助外掛程式來實現分頁查詢。分頁插件的原理主要是透過攔截MyBatis

深度解析Linux chage指令的功能與工作原理 深度解析Linux chage指令的功能與工作原理 Feb 24, 2024 pm 03:48 PM

Linux系統中的chage指令是用來修改使用者帳號的密碼失效日期的指令,也可以用來修改帳號最長的可用日期等。此指令在管理使用者帳號安全性上扮演著非常重要的作用,可以有效控制使用者密碼的使用期限,並增強系統的安全性。 chage指令的使用方法:chage指令的基本語法為:chage[選項]使用者名稱例如,要修改使用者「testuser」的密碼失效日期,可以使用下列命

Astar質押原理、收益拆解、空投項目及策略 & 操作保姆級攻略 Astar質押原理、收益拆解、空投項目及策略 & 操作保姆級攻略 Jun 25, 2024 pm 07:09 PM

目錄Astar Dapp 質押原理質押收益 拆解潛在空投項目:AlgemNeurolancheHealthreeAstar Degens DAOVeryLongSwap 質押策略 & 操作“AstarDapp質押”今年初已升級至V3版本,對質押收益規則做了不少調整。目前首個質押週期已結束,第二質押週期的「投票」子週期剛開始。若要獲得「額外獎勵」收益,需掌握此關鍵階段(預計持續至6月26日,現餘不到5天)。我將細緻拆解Astar質押收益,

See all articles