JavaScript最后的秘密——使用原型創建對象

      網友投稿 675 2025-03-31

      目的

      在對象之間建立關系和共享代碼的方法,擴展和改進既有的對象的方法。

      JavaScript最后的秘密——使用原型創建對象

      概念

      javaScript不是基于類的面向對象系統(即用類來產生對象,javaScript根本沒有類),而是基于原型模型,對象可繼承和擴展其他對象(即原型對象)的屬性和行為,這種方式我們稱之為原型式繼承或基于原型的繼承,其中其行為和屬性被繼承的對象稱為原型。這樣做的目的在于繼承既有的屬性和方法,同時在對象中添加屬性和方法。

      對象字面量適合創建少量對象的情況。

      對象構造函數適合創建大量一致的對象,在代碼上來看,實現了代碼重用,但在運行效率上看,創建出來的對象都會產生一個包含屬性和方法的副本,而方法的副本是完全沒有必要存在這么多的,因此會占用大量內存,影響程序性能。

      將對象們共用的方法和屬性放到原型中,然后所有對象都基于此原型來創建,就能實現代碼共享。此時原型對象只有一個,不會產生不必要的對象副本。既節省了大量計算機資源,又提高了程序性能。

      使用原型創建對象

      在開始前,我們要思考好哪些方法是需要放到原型中去共享,哪些方法和屬性需要放在對象實例中。

      一般,我們將所有對象都需要的方法放到原型中,把對象實例自身特有的屬性和方法放到實例對象中。

      我們舉個利用汽車對象原型創建汽車對象實例的例子。經分析,所有汽車對象都會有牌子(brand)、控制啟動參數(started),還有啟動車子(start)、停車(stop)、行駛(drive)等方法,它包含了每個汽車對象都需要的屬性和方法。

      實際上,每個對象的屬性都可能會變化,不太應該放在原型中,但我們暫時這樣,順便可以講點別的知識。分析后,汽車原型應該是這樣的:

      接下來,基于這個原型創建貨車對象,而貨車對象一般都會有weight(載重),height(高),goods(貨物)這些屬性和卸貨(unload)這個方法。所以我們的貨車對象看起來是這樣的:

      分析完畢。

      一般來說我們應該先建原型,再建對象。但在JavaScript中,要**(1)先創建貨車對象的構造函數**,然后**(2)通過函數的屬性prototype獲得原型對象,然后往原型對象里添加屬性和方法**。步驟如下:

      第一步,定義貨車對象構造函數:

      function CarModel(weight,height,goods){ this.weight = weight; this.height = height; this.goods = goods; this.unload = function(){ alert("開始卸貨"); }; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      第二步,創建構造函數后,獲取汽車原型對象,并設置原型:

      CarModel.prototype.brand = "BMW"; CarModel.prototype.started = false; CarModel.prototype.drive = function(){ //if(CarModel.prototype.started){ if(this.started){//如果實例中沒有此屬性,就會到原型中找 alert("start start start"); }else{ alert("no!"); } }; CarModel.prototype.stop = function(){ //CarModel.prototype.started = false; this.started = false;//這將在對象實例中創建屬性started }; CarModel.prototype.start = function(){ //CarModel.prototype.started = true; this.started = true;//這將在對象實例中創建屬性started };

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      上述例子中之所以不用CarModel.prototype.started而用this.started是因為修改原型中的started以影響到所有對象( 再次說明, 原型中不太應該放屬性)。執行this.started = true后,會在對象實例中添加started屬性:

      小知識:

      在JavaScript中,函數也是對象,也有屬性。對象構造函數里包含屬性prototype,這是一個指向原型對象的引用。但是這個原型對象默認包含的屬性與方法不多,所以我們要給原型對象添加屬性和方法,這通常是在使用構造函數前進行的。

      可以通過 CarModel.prototype訪問原型對象, 并通過其向原型對象中添加屬性和方法 。向原型添加的方法和屬性將被所有對象所共用。對象自己特有的方法與屬性,則在對象構造函數中添加(這其實也是在對象實例上添加),或者直接在對象實例上添加,如:

      var c1 = new CarModel(11,11,"apple1"); c1.seats = 6; c1.flash = function(){ alert("turn on the flash light"); };

      1

      2

      3

      4

      5

      如上面的seats屬性與flash方法只屬于對象cm。其他用對象構造函數創建出的對象是沒有的。

      第三步,測試:

      //c1 c2 c3將會共用原型中的代碼 var c1 = new CarModel(11,11,"apple1"); var c2 = new CarModel(22,22,"apple2"); var c3 = new CarModel(33,33,"apple3"); c1.start(); c1.drive();//start start start c1.stop(); c1.drive();//no! c1.brand = "MMMM"; //這里的brand已經不是原型中那個brand了,這是我們在c1對象實例創建的brand屬性。此語句就是正在創建對象實例變量brand。 alert(c1.hasOwnProperty("brand")); //true,證實c1.brand = "MMMM"賦值語句讓brand變成c1對象實例的屬性。 alert(c1.brand);//MMMM alert(c2.brand);//BMW alert(c2.hasOwnProperty("brand"));//false,說明brand屬性來自原型,因為上一條測試語句能訪問brand,且此測試語句又說明brand不是c2對象實例的屬性,那它只能是來自c2對象的原型。

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      到這里為止,我們展示了搭建原型和通過原型創建對象的過程。

      注意:原型中的屬性與方法都是共用的,沒有副本。

      繼承原型并不意味著必須與它完全相同。在任何情況下,都可以重寫原型的屬性和方法,為此只需在對象實例中提供它們即可,重寫原型中的start方法:

      c1.start = function(){ alert("hello Earth"); };

      1

      2

      3

      4

      最后給出上述的完整代碼:

      hello

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      以上的貨車對象是在汽車原型的基礎上創建的。

      建立原型鏈

      對像不僅可能繼承一個原型,還可以繼承一個原型鏈。就像B原型繼承A原型,C原型繼承B原型,D原型又繼承C原型,這樣形成的一條鏈條,例如D就同時擁有A、B、C等原型的屬性與方法。

      我們通過一個基于噴水車原型創建噴水車對象實例的例子來說明。提醒一下,前面我們已經有了一個汽車原型了。

      分析:(1)我們決不能通過修改上面貨車構造函數CarModel來適應我們的變化,因為這一改就會影響到其他貨車對象,這硬生生給貨車加上噴水車的屬性和方法,顯然也不合理。(2)單獨再創建一個噴水車構造函數的話,那么汽車原型的代碼就要在噴水車原型中重新設置。

      所以最好的做法是建個噴水車原型,然后再讓其繼承汽車原型,這樣汽車原型這部分代碼就不用重新在噴水車原型中設置。

      **我們在創建汽車原型時,只需直接通過構造函數CarModel的屬性prototype獲取原型對象,然后在其中添加要讓每個汽車對象都繼承的屬性和方法即可。**在這里我們需要的是一個繼承汽車原型的噴水車原型對象。為此,我們必須創建一個繼承汽車原型的汽車對象,因為假如不創建的話,那么汽車原型對象就不會存在,再親自動手建立關聯。

      噴水車原型如下:

      創建原型鏈的步驟如下:

      第一步創建繼承了汽車原型的對象:

      對對象實例的唯一要求就是它必須繼承了汽車原型。

      var car = new CarModel();

      1

      第二步創建噴水車對象構造函數:

      function SprayCarModel(weight,height,goods,name,handler){ CarModel.call(this,weight,height,goods); this.name = name; this.handler = handler; }

      1

      2

      3

      4

      5

      說明:創建繼承另一個原型的構造函數時,都不應該重復既有的代碼,下面就重復了貨車對象構造函數的代碼:

      function SprayCarModel(weight,height,goods,name,handler){ this.weight = weight; this.height = height; this.goods = goods; this.name = name; this.handler = handler; };

      1

      2

      3

      4

      5

      6

      7

      解決辦法:

      CarModel.call(this,weight,height,goods);

      1

      它其實是調用CarModel對象構造函數,給當前new SprayCarModel出來的對象this賦值。傳當前對象的引用this過去,再調用CarModel對象構造函數對this進行賦值。

      為什么要這樣做?

      通過第三步我們可知,貨車對象實例將變成噴水車原型,而原型對象是共用的,所以將weight、height、goods放在噴水車對象實例中會更好,如果使用原型中的話,那么對象之間就會互相影響。共用原型中的方法就不會有這種問題,因為大家都是相同的,但是數據就不是了,各有各的不同。

      所以這一條call語句相當于做了以下事情:

      this.weight = weight; this.height = height; this.goods = goods;

      1

      2

      3

      調用對象構造函數是不會產生新對象的,和調用普通函數一樣,調用對象構造函數一般都是給對象屬性賦值。

      只有用運算符new,才會產生新對象,它會先創建一個空對象并將引用賦給this,返回this,然后再調用對象構造函數對對象this進行賦值。由此可見new才會產生新對象,而調用對象構造函數是不會產生新對象的。

      所以call一番操作后,噴水車原型中的屬性,就根本沒有給它們賦值過,因此它們都是未定義的undefined

      第三步將新建的繼承了汽車原型的對象變成噴水車原型:

      SprayCarModel.prototype = car;//將實例car變成SprayCarModel的原型

      1

      注意:另忘了,噴水車原型依然是一個貨車對象實例。其實,還可以通過創建一個對象字面量,然后用作原型,如

      var d = {

      start:function(){},

      };

      SprayCarModel.prototype = d;//SprayCarModel就將繼承d中的屬性和方法。

      第四步向噴水車原型中添加屬性和方法:

      SprayCarModel.prototype.volume = 11;//設置原型的屬性 //設置原型的方法 SprayCarModel.prototype.sprayWater = function(){ alert("spray spray spray"); };

      1

      2

      3

      4

      5

      第五步,測試:

      //創建一個噴水車對象實例 var sprayCar = new SprayCarModel(29999,3,"applepie","AAP",15); alert(sprayCar.hasOwnProperty("weight"));//false alert(sprayCar.weight);//29999,上面說明weight不是實例的屬性,本語句說明能訪問weight屬性,說明weight是在原型中的。 var sprayCar1 = new SprayCarModel(999,3,"applepie","AAP",15); alert(sprayCar1.weight);//999 alert(sprayCar.weight);//29999 alert("weight belongs to sprayCar:"+sprayCar.hasOwnProperty("weight"));//true,說明通過CarModel.call(this,weight,height,goods);已將weight變成了sprayCar對象實例的屬性了。height、goods也是如此。 sprayCar.sprayWater();//訪問噴水車原型中的sprayWater方法 alert(sprayCar.volumn);//訪問噴水車原型中的屬性 alert(sprayCar.hasOwnProperty("volumn")); //false,結合上一條語句,說明volumn是在原型中的 alert(sprayCar.brand);//BMW,能訪問汽車原型中的屬性 sprayCar.drive();//能訪問汽車原型中的方法 alert(sprayCar.hasOwnProperty("name"));//噴水車對象實例的屬性

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      至此,原型鏈也講完了。其實就是原型對象換成了基于上一個原型對象創建的對象實例。

      下面給出完整的代碼:

      hello

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      原型鏈中的繼承原理

      對象調用方法或訪問屬性時,首先在對象實例里查找,如果查找不到,就會沿繼承鏈上移,在其原型中接著查找。

      意外!意外!意外!

      console.log("SprayCarModel constructor is:"+sprayCar.constructor);

      1

      輸出的結果是:

      SprayCarModel constructor is:function CarModel(weight,height,goods){ this.weight = weight; this.height = height; this.goods = goods; } ```

      1

      2

      3

      4

      5

      6

      不可能呀,SprayCarModel才是sprayCar對象實例的構造器呀,怎么成了CarModel。原來,我們要顯式地把對象構造函數的constructor屬性設置為SprayCarModel對象構造器函數。雖然不設置也不會有什么影響,但是最佳實踐建議還是設置的好。

      顯式設置對象構造函數的constructor屬性設置為SprayCarModel

      SprayCarModel.prototype.constructor = SprayCarModel;

      1

      再看看結果:

      console.log("SprayCarModel constructor is:"+sprayCar.constructor);

      1

      輸出的結果是:

      SprayCarModel constructor is:function SprayCarModel(name,handler){ this.name = name; this.handler = handler; }

      1

      2

      3

      4

      這下終于正確了。

      總結:

      我們創建的每個原型鏈的終點都是Object。我們創建的任何對象,默認原型都是Object,除非你對其進行了修改。噴水車原型從汽車原型派生出來,汽車原型是從Object派生出來。所有對象都是從Object派生出來的,所以我們創建的每個對象都有原型,該原型默認是Object。當然,你可以將對象的原型設置為其他對象,如噴水車原型是汽車對象實例,無論怎樣,所有原型鏈的終點都是Object。

      原型是動態的,只要在原型上作任何修改,就會馬上反映到各個對象上去。可以對象實例中重寫原型中的方法和屬性。

      Object實現了很多重要的方法,如hasOwnProperty、toString,它們是javaScript對象系統的核心部分。

      我們常常會重寫Object原型中的toString方法,如:

      TruckModel.prototype.toString = function(){ alert("HDDDDDDDD"); }; var truck = new TruckModel("baobao",true,5000,1.5,"apple"); truck.toString();

      1

      2

      3

      4

      5

      但不是每個方法都能重寫,如以下這些就是不能重寫的:

      constructor 表示與原型相關聯的構造函數

      hasOwnProperty判斷實例是否有此屬性,每個對象都有此方法。如果屬性不是在對象實例中定義的,但能夠訪問它,就可以認為它肯定是在原型中定義的。

      isPrototypeOf判斷一個對象是否是另一個對象的原型

      如car.isPrototypeOf(truck) //true

      propertyIsEnumerable用于判斷通過迭代對象的所有屬性是否可訪問指定的屬性。

      而下面這些方法是可重寫:

      toString

      toLocaleString

      valueOf

      給一個運行實例:

      heloo world ~

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      21

      22

      23

      24

      25

      26

      27

      28

      29

      30

      31

      32

      33

      34

      35

      36

      37

      38

      39

      40

      41

      42

      43

      44

      45

      46

      47

      48

      49

      50

      51

      52

      53

      54

      55

      56

      57

      58

      59

      60

      61

      62

      63

      64

      65

      66

      67

      68

      69

      70

      71

      72

      73

      74

      75

      擴展內置對象

      其實與上面的一樣,通過在原型中添加方法和屬性,如擴展String內置對象:

      String.prototype.clickme = function(){ alert("you clicked me"); };

      1

      2

      3

      其他的依次類推。

      最后回顧一下:

      JavaScript對象系統使用原型式繼承

      使用構造函數創建對象實例時,實例包含自己的自定義屬性,還有構造函數中方法的副本。

      給構造函數的原型添加屬性后,使用這個構造函數創建的實例都將繼承這些屬性。

      通過在原型是中定義屬性,可減少對象包含的重復代碼。

      要重寫原型中的屬性,只需在實例中添加該屬性即可。

      構造函數有默認的原型,可通過函數的屬性prototype來訪問它。

      可將你自己創建的對象賦給構造函數的屬性prototype

      使用自定義的原型對象時,務必將原型的屬性constructor設置為相應的對象構造函數,以保持一致。

      給原型添加屬性后,繼承該原型的所有實例都將立即繼承這些屬性,即便是以前創建的實例也不例外。

      歸根結底,所有原型和對象都是從Object派生而來的。

      Object包含所有對象都將繼承的屬性和方法,如toString和hasOwnProperty

      可給內置對象(如Object和String等)添加屬性,也可重寫它們的既有屬性,但要小心。

      在JavaScript中,一切幾乎皆是對象,包括函數、數組和眾多的內置對象和自己創建的自定義對象。

      謝謝閱讀。

      AI JavaScript 交通智能體

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:最早的在線文檔叫什么名稱(最早的在線文檔叫什么名稱來著)
      下一篇:表格外觀怎么更改
      相關文章
      国产亚洲欧洲Aⅴ综合一区 | 亚洲久本草在线中文字幕| 久久久久亚洲国产| 亚洲精品一区二区三区四区乱码| 国产亚洲一区二区三区在线观看| 亚洲女人被黑人巨大进入| 日韩国产精品亚洲а∨天堂免| 亚洲最大的视频网站| 亚洲日产2021三区在线| 亚洲永久中文字幕在线| 亚洲妇女水蜜桃av网网站| 亚洲一卡2卡3卡4卡国产网站| 亚洲av永久无码精品三区在线4| 亚洲一区无码中文字幕乱码| 亚洲香蕉在线观看| 亚洲综合无码一区二区痴汉| 亚洲av永久无码| 国产成人综合久久精品亚洲| 亚洲国产成人久久精品99 | 久久亚洲精品无码aⅴ大香| 亚洲制服中文字幕第一区| 亚洲宅男永久在线| 亚洲卡一卡2卡三卡4麻豆| 日本亚洲色大成网站www久久| 亚洲日韩精品无码AV海量| 国产精品自拍亚洲| 亚洲麻豆精品国偷自产在线91| 久久久久亚洲av毛片大| 亚洲精品午夜国产VA久久成人| 亚洲AV无码一区二区二三区入口 | 国产亚洲综合一区柠檬导航| 亚洲AV无码乱码国产麻豆穿越| 亚洲无删减国产精品一区| 亚洲欧洲国产精品久久| 亚洲熟妇AV一区二区三区浪潮| 处破女第一次亚洲18分钟| 亚洲午夜精品第一区二区8050| 国产亚洲精品a在线无码| 亚洲情a成黄在线观看动漫尤物| 亚洲精品视频观看| 亚洲 日韩经典 中文字幕|