JS物件創建常用方式有哪些?
前言
俗話說“在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中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

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

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

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

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

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

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

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

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