前言
俗話說“在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中文網其他相關文章!

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

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

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

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

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境