<font id="zqva1"></font>
<rt id="zqva1"></rt>
  • <tt id="zqva1"></tt>
    <cite id="zqva1"></cite>

    <cite id="zqva1"><noscript id="zqva1"></noscript></cite>
      <rp id="zqva1"><meter id="zqva1"></meter></rp>

        <cite id="zqva1"></cite>
          <b id="zqva1"></b>
          <rp id="zqva1"></rp>
          <cite id="zqva1"></cite>

          <rt id="zqva1"></rt>

        1. <rp id="zqva1"></rp>

          JavaScript中創建對象的7種模式

          時間:?2017-11-09閱讀:?928標簽:?對象

          ECMA-262把對象定義為:”無需屬性的集合,其屬性可以包含基本值、對象或者函數。”嚴格來講,這就相當于說明對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值。正因為這樣,我們可以把ECMAScript的對象想象成散列表:無非就是一組名對值,其中值可以是數據或函數。 


          創建自定義對象最簡單的方式就是創建一個Object的實例,然后再為他添加屬性和方法,如下所示:

          var person = new Object();

          person.name = "liubei";

          person.age = 29;

          person.job = "shayemuyou";


          person.sayName = function(){

              alert(this.name);

          }

          上面的例子創建了一個名為person的對象,并為他添加了三個屬性和一個方法。其中sayName()方法用于顯示name屬性,this.name將被解析為person.name,早期的開發人員經常使用這個模式來創建對象,后來對象字面量的方法成了創建對象的首選模式,上面的例子用對象字面量的語法可以寫成如下這樣:

          var person = {

              name:"liubei",

              age:29,

              job:"shayemuyou",

              sayName:function(){

                  alert(this.name);

              }

          }

          這個例子中的person對象和前面的對象是一樣的,都有相同的屬性和方法。 


          雖然Object構造函數或者對象字面量的方法都可以用來創建單個對象,但是這些方法有個明顯的缺點:使用同一個接口創建很多對象,會產生大量的重復代碼。為了解決這個方法,人們開始使用工廠模式的一種變體。


           1   工廠模式


          工廠模式是軟件工程領域一種廣為人知的設計模式,這種模式抽象了創建具體對象的過程。考慮到ECMAScript中無法創建類,開發人員就發明了一種函數,用函數來封裝以特定接口創建對象的細節,如下所示:

          function createPerson(name, age, job){

              var o = new Object();

              o.name = name;

              o.age = age;

              o.job = job;

              o.sayName = function(){

                  alert(this.name);

              }

              return o;

          }

          var person1 = createPerson("wei",25,"software");

          var person2 = createPerson("bu",25,"software");

          函數createPerson()能夠根據接受的參數來構建一個包含所有必要信息的Person對象。可以多次調用這個函數,每次都會返回一個包含三個屬性一個方法的對象。工廠模式雖然解決了創建多個相似對象的問題,但卻沒有解決對象識別的問題,即怎么樣知道這是哪個對象類型。


           2   構造函數模式


          像Array、Object這樣的原生構造函數,在運行時會自動出現在執行環境中。此外,我們可以創建自定義個構造函數,從而定義自定義類型的屬性和方法。例如,我們可以使用構造函數重寫上個例子:

          function Person(name, age, job){

              this.name = name;

              this.age = age;

              this.job = job;

              this.sayName = function(){

                  alert(this.name);

              }

          }

          var person1 = new Person("wei",25,"software");

          var person2 = new Person("bu",25,"software");

          在這個例子中,Person()函數取代了createPerson()函數,我們注意到Person()與createPerson()的不同之處在于:

          • 沒有顯式的創建對象

          • 直接將屬性和方法賦值給this對象

          • 沒有return語句 

                 

          此外,還應該注意到函數名Person使用的是大寫字母P。按照慣例,構造函數始終都應該以一個大寫字母開頭,而非構造函數則應該以一個小寫字母開頭。這個做法借鑒了其他OO語言,主要是為了區別于ECMAScript中的其他函數。因為構造函數本身也是函數,只不過可以創建對象而已。 

                 

          要創建一個Person實例,必須使用new操作符。以上這種方式會經過以下四個步驟: 

              1.創建一個新對象 

              2.將構造函數的作用域賦給新對象(因此this指向這個新對象) 

              3.執行構造函數中的代碼 

              4.返回新對象 

                 

          在前面例子的最后,person1和person2分別保存著Person的一個不同的實例。這兩個對象都有一個constructor(構造函數)屬性,該屬性指向Person。如下:

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

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

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

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


          對象的constructor屬性最初是用來標識對象類型的。但是,提到檢測對象類型,還是instanceof操作符比較可靠一些。我們在這個例子中創建的對象都是Object對象的實例,也是Person對象的實例,這一點通過instanceof操作符可以驗證。

          console.log(person1 instanceof Object);     //true

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

          console.log(person2 instanceof Object);     //true

          console.log(person2 instanceof Person);     //true

          創建自定義的構造函數意味著將來可以將他的實例標識為一種特定的類型;而這正是構造函數模式勝過工廠模式的地方。在這個例子中,person1和person2之所以同是Object的實例,是因為所有的對象都繼承自Object。 


          構造函數的主要問題,就是每個方法都要在實例上重新創建一遍,造成內存浪費。在前面的例子中,person1和person2都有一個名為sayName()的方法,但是兩個方法不是同一Function的實例。不要忘了ECMAScript中的函數也是對象,因此每定義一個函數,也就是實例化了一個對象,從邏輯角度講,此時的構造函數可以這樣定義:

          function Person(name, age, job){

              this.name = name;

              this.age = age;

              this.job = job;

              this.sayName = new Function("alert(this.name);")   //與聲明函數在邏輯上是等價的

          }

          從這個角度來看構造函數,更容易看明白每個Person實例都會包含一個不同的Function實例的本質。說明白些,會導致不同的作用域鏈和標識符解析,但是創建Function新實例的機制仍然是相同的。因此,不同實例上的同名函數是不相等的,以下代碼可以證實這一點。

          alert(person1.sayName == person2.sayName);  //false

          然而,創建兩個完成同樣任務的Function實例的確沒有必要;況且有this對象在,根本不用在執行代碼前就把函數綁定到特定的對象上。因此,可以像下面這樣,通過把函數定義轉移到構造函數外部來解決這個問題。

          function Person(name, age, job){

              this.name = name;

              this.age = age;

              this.job = job;

              this.sayName = sayName;

          }

          function sayName(){

              alert(this.name);

          這樣做解決了多個函數解決相同問題的問題,但是有產生了新的問題,在全局作用域中實際上只被某個對象調用,這讓全局對象有點名不副實。更讓人無法接受的是:如果對象需要定義很多方法,那么就要定義很多全局函數,于是我們這個自定義的引用類型就絲毫沒有封裝性可言了。好在這些問題可以使用原型模式來解決。


           3   原型模式


          我們創建的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。使用原型對象的實例就是讓所有實例共享它所包含的屬性和方法。換句話說,不必在構造函數中定義對象的實例信息,而是可以將這些信息直接添加到原型對象中,如下所示:

          function Person(){

          }

          Person.prototype.name = "wei";

          Person.prototype.age = 27;

          Person.prototype.job = "Software";

          Person.prototype.sayName = function(){

              alert(this.name);

          }


          var person1 = new Person();

          person1.sayName();      //"wei"


          var person2 = new Person();

          person2.sayName();      //"wei"


          alert(person1.sayName == person2.sayName);

          在此,我們將sayName()方法和所有的屬性直接添加在了Person的prototype屬性中,構造函數變成了空函數。即便如此,我們仍然可以通過構造函數來創建新對象,而且新對象還會具有相同的屬性和方法。但是與構造函數不同的是,新對象的這些屬性和方法是由所有實例共享的。換句話說,person1和person2訪問的都是同一組屬性和同一個sayName()函數。要理解原型模式的工作原理,就必須先理解ECMAScript中原型對象的性質。 

                 

          原型對象的本性由于篇幅太長將會在下一章節詳細分析。上面我們說了原型模式的好處,接下來我們來看一下原型模式的缺點。原型模式省略了為構造函數傳遞參數的這一環節,結果所有實例在默認情況下都具有相同的屬性值。這會在某些程度上帶來一種不便,這并不是原型模式最大的問題,因為如果我們想為一個通過原型模式創建的對象添加屬性時,添加的這個屬性就會屏蔽原型對象的保存的同名屬性。換句話說,就是添加的這個屬性會阻止我們去訪問原型中的屬性,但并不會改變原型中的屬性。 

                 

          原型模式最大的問題是由其共享的本質所導致的。原型中所有的屬性被很多實例共享,這種共享對函數非常合適,對包含基本值的屬性也說的過去,但是對引用類型的屬性值來說問題就比較突出了,下面我們來看一個例子:

          function Person(){

          }

          Person.prototype = {

              constructor:Person,

              name:"wei",

              age:29,

              friends:["乾隆","康熙"],

              sayName:function(){

                  alert(this.name);

              }

          }

          var person1 = new Person();

          var person2 = new Person();

          person1.friends.push("嬴政");

          console.log(person1.friends);   //["乾隆","康熙","嬴政"]

          console.log(person2.friends);   //["乾隆","康熙","嬴政"]

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

          上面的例子中,Person.prototype對象有一個名為friends的屬性,該屬性包含一個字符串數組。然后創建了兩個Person的實例,接著修改person1.friends引用的數組,向數組中添加一個字符串,由于數組存在于Person.prototype中而不是person1中,所以person2.friends也會被修改。但是一般每個對象都是要有屬于自己的屬性的,所以我們很少看到有人單獨使用原型模式來創建對象。


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


          創建自定義類型最常見的方式就是組合使用構造函數模式與原型模式。構造函數模式用于定義實例屬性,原型模式用于定義方法和共享的屬性。結果,每個實例都會有自己的一份實例屬性的副本,但同時又共享著對方法的引用,最大限度的節省了內存。另外,這種混成模式還支持向構造函數傳遞參數;可謂是集兩種模式之長。下面的代碼重寫了前面的例子:

          function Person(name, age){

              this.name = name;

              this.age = age;

              this.friends = ["乾隆","康熙"];

          }

          Person.prototype = {

              constructor:Person,

              sayName:function(){

                  alert(this.name);

              }

          }

          var person1 = new Person("wei",29);

          var person2 = new Person("bu",25);

          person1.friends.push("嬴政");

          console.log(person1.friends);   //["乾隆", "康熙", "嬴政"]

          console.log(person2.friends);   //["乾隆", "康熙"]

          console.log(person1.friends === person2.friends);   //false

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

          在這個例子中,實例屬性都是在構造函數中定義的,而由所有實例共享的屬性constructor和方法sayName()則是在原型中定義的。所以修改了person1.friends并不會改變person2.friends,因為他們分別引用了不同的數組。 

                 

          這種構造函數與原型模式混成的模式,是目前在ECMAScript中使用最廣泛、認同度最高的一種創建自定義類型的方法。可以說,這是用來定義引用的一種默認形式。


           5   動態原型模式


          有其他OO語言經驗的開發人員在看到獨立的構造函數和原型時,很可能會感到非常的困惑。動態原型模式就是用來解決這個問題的一個方案,它把所有的信息都封裝在了構造函數中,而通過構造函數中初始化原型(僅在必要的情況下),又保持了同時使用構造函數和原型的優點。換句話說,可以通過檢查某個應該存在的方法是否有效,來決定是否要初始化原型。來看一個例子:

          function Person(name, age){

              this.name = name;

              this.age = age;

              this.friends = ["乾隆","康熙"];

              //注意if語句

              if(typeof this.sayName!="function"){

                  Person.prototype.sayName = function(){

                      alert(this.name);

                  }

              }

          }

          var person1 = new Person("wei",29);

          person1.friends.push("嬴政");

          person1.sayName();

          注意構造函數代碼中的if語句,這里只在sayName()方法不存在的情況下才會將它添加到原型中。這斷代碼只有在第一次調用構造函數的時候才會被執行。此后,原型已經被初始化,不需要再做什么修改。不過要記住,這里所做的修改能立即在所有實例中得到反映。因此,這種方法可以說確實非常完美。其中if語句檢查的是初始化之后應該存在的任何方法和屬性–不必再用一大堆if來檢查每個屬性和方法,只檢查其中一個即可。對于采用這樣模式創建的對象,還可以使用instanceof操作符來確定他的類型。 

                 

          注意:使用動態原型模式時,不能使用對象字面量重寫原型。如果在已經創建了實例的情況下重寫原型,那么就會切斷現有的實例與新原型之間的聯系。


           6   寄生構造函數模式


          通常,在上述幾種模式都不適合的情況下可以使用寄生構造函數模式。這種模式的基本思想是創建一個函數,該函數的作用僅僅是封裝創建對象的代碼,然后再返回新創建的對象,但從表面看,這個函數又很像典型的構造函數。來看一個例子:

          function Person(name, age, job){

              var o = new Object();

              o.name = name;

              o.age = age;

              o.job = job;

              o.sayName = function(){

                  alert(this.name);

              }

              return o;

          }

          var person = new Person("wei",29,"banzhuan");

          person.sayName();   //"wei"

          在這個例子中,Person函數創建了一個對象,并以相應的屬性和方法初始化該對象,然后返回了這個對象。除了使用new操作符把使用的包裝函數叫做構造函數之外,這個模式和工廠模式并沒有多大的區別。構造函數在不返回值的情況下,會默認返回新對象的實例。而通過在構造函數的末尾添加一個return語句,可以重寫調用構造函數時返回的值。 

                 

          這個模式可以在特殊的情況下來為對象創建構造函數。假設我們想創建一個具有額外方法的特殊數組。由于不能直接修改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

          在這個例子中,我們創建了一個名為SpecialArray的構造函數。在這個函數的內部,首先創建了一個數組,然后push()方法初始化了數組的值。隨后又給數組實例添加了toPipedString()方法,用來返回以豎線分隔的數組值。最后將數組以函數的形式返回。接著,我們調用了SpecialArray構造函數,傳入了初始化的值,并調用了toPipedString()方法。 


          關于寄生構造函數模式,有一點需要聲明:首先,返回的對象與構造函數或者構造函數的原型沒有任何關系;也就是說,構造函數返回的對象與在構造函數外部創建的對象沒有什么不同。為此,不能依賴instanceof操作符來確定對象的類型。由于存在這一的問題,我們建議在可以使用其他模式的情況下不要使用這種模式。


           7   穩妥構造函數模式


          道格拉斯·克拉克福德發明了JavaScript中的穩妥對象這個概念。所謂穩妥對象,是指沒有公共屬性,而且其方法也不引用this對象。穩妥對象最適合在一些安全環境中(這些環境會禁止使用this和new),或者在防止數據被其他應用程序改動時使用。穩妥構造函數遵循的與寄生構造函數類似的模式,但又兩點不同:一是新創建對象的實例方法不引用this;二是不使用new操作符調用構造函數。按照穩妥構造函數的要求,可以將前面的Person構造函數重寫如下:

          function Person(name, age, job){

              //創建要返回的新對象

              var o = new Object();


              //可以在這里定義私有變量和函數


              //添加方法

              o.sayName = function(){

                  alert(this.name);

              };


              //返回對象

              return o;

          }

          注意,在以這種模式創建的對象中,除了使用sayName()方法之外,沒有其他辦法訪問name的值。可以像下面使用穩妥的Person構造函數:

          var person =Person("weiqi",22,"banzhuan");

          person.sayName();   //weiqi

          這樣,變量person中保存的是一個穩妥對象,而除了sayName()方法外,沒有別的方式可以訪問其他數據成員。即使有其他代碼會給這個對象添加方法或數據成員,但也不可能有別的辦法訪問傳入到構造函數中的原始數據。穩妥構造函數模式提供的這種安全性,使得他非常適合在某些安全執行環境–例如,ADsafe(www.adsafe.org)提供的環境下使用。 

                 

          注意:與寄生構造函數模式類似,使用穩妥構造函數模式創建的對象與構造函數之間沒有什么關系,因此instanceof操作符對這種對象也沒有意義。

          站長推薦

          1.阿里云: 本站目前使用的是阿里云主機,安全/可靠/穩定。點擊領取2000元代金券、了解最新阿里云產品的各種優惠活動點擊進入

          2.騰訊云: 提供云服務器、云數據庫、云存儲、視頻與CDN、域名等服務。騰訊云各類產品的最新活動,優惠券領取點擊進入

          3.廣告聯盟: 整理了目前主流的廣告聯盟平臺,如果你有流量,可以作為參考選擇適合你的平臺點擊進入

          鏈接: http://www.modern-decoration.com.cn/article/detial/75

          在原生JavaScript中創建不可變對象

          Javascript是一種靈活的語言,你可以重新定義任何東西,但是當項目變得復雜時,我們會發現可變數據結構的問題。隨著JavaScript的最新版本的發布這種情況發生了改變。現在可以創建不可變的對象了。本文介紹如何用三種不同的方法來做。

          淺談JS包裝對象

          對象是 JavaScript 語言最主要的數據類型,三種原始類型的值——數值、字符串、布爾值——在一定條件下,也會自動轉為對象,也就是原始類型的“包裝對象(wrapper)。

          vue中如何監聽一個對象內部的變化?

          方法1:對整個obj深層監聽,默認第一次綁定的時候不會觸發watch監聽,值為true時可以在最初綁定的時候執行;方法2 :指定key;方法3:computed

          Reflect對象

          將Object 對象上的屬于語言內部的方法放到 Reflect 對象上,從 Reflect 上獲得語言內部的方法 ;修改某些 Object 方法的返回結果,讓其變得更合理。讓Object的操作都變成函數行為。Reflect 對象的方法與 Proxy 對象的方法一一對應。

          利用 WeakMap 對 Vue 新建數組中的對象賦予 :key

          在 Vue 中,對組件進行循環都需要加入key以便“就地復用”,可是在某些情況下,我們需要新建多個對象,而這些對象不是從后端獲取到的,而是前端生成的,沒有唯一值,且 Vue 目前版本只允許字符串,數字作為組件的 key

          javascript中document是什么?

          javascript中document是window對象的屬性,表示對Document對象的只讀引用。Document對象是Window對象的一部分,可通過window.document屬性對其進行訪問。

          javascript如何判斷對象是否相等?

          JavaScript作為一個基于對象(沒有類的概念)的語言,從入門到精通到放棄一直會被對象這個問題圍繞。下面我們就來看一下如何判斷對象是否相等。

          如何將JSON反序列化為JavaScript對象?

          JSON(JavaScript Object Notation)用于與Web服務器或RESTFull API交換數據,從Web服務器接收的數據始終是字符串。為了使用這些數據,您需要使用JSON.parse()解析數據,它將返回一個JavaScript對象或對象數組。

          File FileList 和 FileReader 對象詳解

          File 對象、FileList 對象與 FileReader 對象大家或許不太陌生,常見于文件上傳下載操作處理(如處理圖片上傳預覽,讀取文件內容,監控文件上傳進度等問題)

          js內置對象

          在js里,一切皆為或者皆可以被用作對象。可通過new一個對象或者直接以字面量形式創建變量,所有變量都有對象的性質。JS中常用的內置對象:Array對象、Date對象、正則表達式對象、string對象、Global對象

          內容以共享、參考、研究為目的,不存在任何商業目的。其版權屬原作者所有,如有侵權或違規,請與小編聯系!情況屬實本人將予以刪除!

          文章投稿關于web前端網站點搜索站長推薦網站地圖站長QQ:522607023

          小程序專欄: 土味情話心理測試腦筋急轉彎幽默笑話段子句子語錄成語大全運營推廣

          国产精品高清视频免费 - 视频 - 在线观看 - 影视资讯 - 唯爱网