亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

JS中創建自定義類型的常用模式總結【工廠模式,構造函數模式,原型模式,動態原型模式等】

2024-05-06 16:47:47
字體:
來源:轉載
供稿:網友

本文實例講述了JS中創建自定義類型的常用模式。分享給大家供大家參考,具體如下:

雖然在 ES6 中,已經出了 class 的語法,貌似好像不用了解 ES5 中的這些老東西了,但是越深入學習,你會發現理解這些模式的重要性。

在本文中,我會描述 7 種常用的創建自定義類型的模式:工廠模式、構造函數模式、原型模式、組合使用構造函數模式、動態原型模式、寄生構造函數模式、穩妥構造函數模式。分別給出他們的示例代碼,并分析他們的利弊,方便讀者選擇具體的方式來構建自己的自定義類型。

最后,我會指出 ES6 中的 class 語法,本質上其實還是利用了組合使用構造函數模式進行創建自定義類型。

1. 工廠模式

廢話不多說,先上工廠模式的實例代碼:

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("Nicholas", 29, "Software Engineer");var person2 = createPerson("Greg", 27, "Doctor");

優點:解決了創建多個相似對象的問題;

缺點:沒有解決對象識別的問題(即不知道這個對象是什么類型),對于對象的方法沒有做到復用。

2. 構造函數模式

function Person(name, age, job){  this.name = name;    // 對象的所有細節全部掛載在 this 對象下面  this.age = age;  this.job = job;  this.sayName = function(){    alert(this.name);  };}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");

說到構造函數模式就不得不提到 new 操作符了。我們來看看 new 這個操作符到底做了什么:

① 創建一個對象;
② 將構造函數內的 this 指向這個新創建的對象,同時將該函數的 prototype 的引用掛載在新對象的原型下;
③ 執行函數內的細節,也就是將屬性和方法掛載在新對象下;
④ 隱式的返回新創建的對象。

優點:解決了對象識別的問題;

缺點:對于自定義類型的方法每次都要新創建一個方法函數實例,沒有做到函數復用。如果把所有方法函數寫到父級作用域中,是做到了函數復用,但同時方法函數只能在父級作用域的某個類型中進行調用,這對于父級作用域有點名不副實,同時對于自定義引用類型沒有封裝性可言。

3. 原型模式

function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function(){  alert(this.name);};var person1 = new Person();person1.sayName();   //"Nicholas"var person2 = new Person();person2.sayName();   //"Nicholas"alert(person1.sayName == person2.sayName);   //true

理解要點:

① 無論什么時候,只要創建了一個新函數,就會根據一組特定規則為該函數創建一個 prototype 屬性,這個屬性指向函數的原型對象。
② 在默認情況下,所有原型對象都會自動獲得一個 constructor 屬性,這個屬性包含一個指向 prototype 屬性所在函數的指針。至于原型中的其他方法則都是從 Object 繼承而來。
③ 當調用構造函數創建了一個新實例后,該實例的內部將包含一個指針 [[prototype]](內部屬性) ,指向構造函數的原型對象。
④ 當調用構造函數創建一個新實例后,該實例的實例環境,即構造函數,會針對原型對象上的非引用類型的原型屬性,在構造函數中自動構建相應的實例環境屬性。也就是說,之后根據構造函數創建的實例,它的實例屬性中的非引用類型屬性,都仍是根據構造函數中的實例環境屬性創建的。

但是為減少不必要的輸入,也為了從視覺上更好地封裝原型的功能,更常見的做法是用一個包含所有屬性和方法的對象字面量來重寫整個原型對象。如下所示:

function Person(){}Person.prototype = {  name : "Nicholas",  age : 29,  job: "Software Engineer",  sayName : function () {    alert(this.name);  }};

但是這種寫法,其本質上完全重寫了默認的 prototype 對象,因此 constrctor 屬性也就變成了新對象的 constructor 屬性(指向 Object 構造函數),不在指向 Person 函數。盡管此時,instanceOf 操作符還能返回正確的結果。

如果 constructor 屬性真的很重要,可以像下面這樣特意將它設置回適當的值:

function Person(){}Person.prototype = {  constructor : Person,  name : "Nicholas",  age : 29,  job: "Software Engineer",  sayName : function () {    alert(this.name);  }};

注意,以這種方式重設 constructor 屬性會導致他的 [[Enumerable]] 特性被設置為 true 。默認情況下,原生的 constructor 屬性是不可枚舉的,因此,如果你使用兼容 ECMAScript 5 的 JavaScript 引擎,你可以試試 Object.defineProperty() 方法:

function Person(){}Person.prototype = {  name : "Nicholas",  age : 29,  job : "Software Engineer",  sayName : function () {    alert(this.name);  }};//重設構造函數,只適用于 ECMAScript 5 兼容的瀏覽器Object.defineProperty( Person.prototype, "constructor", {  enumerable: false,  value: Person});

注意,重寫原型對象會切斷新原型與已經存在的對象實例之間的聯系;它們引用的仍然是最初的原型。

優點:對自定義類型的方法解決了函數復用的問題。

缺點:

① 不能為構造函數傳遞初始化參數;
② 原型模式中實現了對于包含引用類型值的屬性的共享,這就意味著一個實例中修改了該引用類型值,所有實例的該屬性都會被修改!?。?/p>

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

在組合使用構造函數模式和原型模式中,構造函數模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性,而且還支持向構造函數傳遞參數。如以下示例代碼所示:

function Person(name, age, job){  this.name = name;  this.age = age;  this.job = job;  this.friends = ["Shelby", "Court"];}Person.prototype = {  sayName : function(){    alert(this.name);  }}Object.defineProperty( Person.prototype, "constructor", {  enumerable: false,  value: Person);var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.friends.push("Van");alert(person1.friends);  //"Shelby,Count,Van"alert(person2.friends);  //"Shelby,Count"alert(person1.friends === person2.friends);   //falsealert(person1.sayName === person2.sayName);   //true

優點:能為構造函數傳遞初始化參數;該復用復用,不該復用的沒復用。
缺點:封裝性不好,構造函數和原型分別獨立于父級作用域進行申明。

5. 動態原型模式(推薦)

該模式把所有信息都封裝在構造函數中,通過構造函數來實現初始化原型 (僅在必要的情況下),又保持了同時使用構造函數和原型的優點。請看以下示例代碼:

function Person(name, age, job){  //屬性  this.name = name;  this.age = age;  this.job = job;  //方法  if (typeof this.sayAge != "function"){   // 此處應該永遠去判斷新添加的屬性和方法    Person.prototype.sayName = function(){      alert(this.name);    };    Person.prototype.sayAge = function(){      alert(this.age);    };  }}var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();

if 語句檢查的可以是初始化之后應該存在的任何屬性或方法——不必用一大堆 if 語句檢查每個屬性和每個方法;只要檢查其中一個即可。

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

優點:封裝性非常好;還可使用 instanceOf 操作符確定它的類型。
缺點:無。

6. 寄生構造函數模式

除了使用 new 操作符并把使用的包裝函數叫做構造函數之外,這個模式跟工廠模式其實是一模一樣的。請看以下代碼:

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 friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();    //"Nicholas"

在使用 new 操作符下,構造函數在不返回值的情況下,默認會返回新對象實例。而通過在構造函數的末尾添加一個 return 語句,可以重寫調用構造函數時返回的值。

缺點:沒有解決對象識別的問題(即不知道這個對象是什么類型),不能依賴 instanceOf 操作符來確定對象類型;對于對象的方法沒有做到復用。

7. 穩妥構造函數模式

先來了解下穩妥對象:指的是沒有公共屬性,而且其方法也不引用 this 的對象。穩妥對象最適合在一些安全的環境中 (這些環境中會禁止使用 this 和 new),或者再防止數據被其他應用程序 (如 Mashup 程序) 改動時使用。穩妥構造函數遵循與寄生構造函數類似的模式,但有兩點不同:一是新創建對象的實例方法不引用 this;二是不使用 new 操作符調用構造函數。以下為示例代碼:

function Person(name, age, job){  var o = new Object();    //創建要返回的對象  //可以在這里定義私有變量和函數  o.sayName = function(){   //添加方法    alert(name);  };  return o;    //返回對象}var friend = Person("Nicholas", 29, "Software Engineer");friend.sayName(); //"Nicholas"

其原理就是利用閉包,保有對私有變量和私有方法的引用。

優點:不可能有別的方法訪問到傳入到構造函數中的原始數據。
缺點:沒有解決對象識別的問題(即不知道這個對象是什么類型),不能依賴 instanceOf 操作符來確定對象類型;對于對象的方法沒有做到復用。

8. ES6 中的 class

咱們這塊以 class 實例來展開講述:

class Parent {  name = "qck";  sex = "male";  //實例變量  sayHello(name){    console.log('qck said Hello!',name);  }  constructor(location){   this.location = location;  }}

我們來看看這段代碼通過 babel 編譯后的 _createClass 函數:

var _createClass = function () {  function defineProperties(target, props) {    for (var i = 0; i < props.length; i++) {      var descriptor = props[i];      // 對屬性進行數據特性設置      descriptor.enumerable = descriptor.enumerable || false; // enumerable設置      descriptor.configurable = true;             // configurable設置      if ("value" in descriptor) descriptor.writable = true; // 如果有value,那么可寫      Object.defineProperty(target, descriptor.key, descriptor); // 調用defineProperty() 進行屬性設置    }  }  return function (Constructor, protoProps, staticProps) {    // 設置到第一個 Constructor 的 prototype 中    if (protoProps) defineProperties(Constructor.prototype, protoProps);    // 設置 Constructor 的 static 類型屬性    if (staticProps) defineProperties(Constructor, staticProps);    return Constructor;  };}();

首先該方法是一個自執行函數,接收的一參是構造函數本身,二參是為構造函數的原型對象需要添加的方法或者屬性,三參是需要為構造函數添加的靜態屬性對象。從這個函數就可以看出 class 在創建自定義類型時,用了原型模式。

我們看看編譯后的結果是如何調用 _createClass 的:

var Parent = function () {   // 這里是自執行函數  _createClass(Parent, [{   // Parent的實例方法,通過修改Parent.prototype來完成    key: "sayHello",    value: function sayHello(name) {      console.log('qck say Hello!', name);    }  }]);  function Parent(location) {   //在Parent構造函數中添加實例屬性    _classCallCheck(this, Parent);    this.name = "qck";    this.sex = "male";    this.location = location;  }  return Parent;}();function _classCallCheck(instance, Constructor) {  if (!(instance instanceof Constructor)) {    throw new TypeError("Cannot call a class as a function");  }}

這里調用 _createClass 的地方就證實了我們剛才的想法——確實應用了原型模式:我們的 class 上的方法,其實是通過修改該類 (實際上是函數) 的 prototype 來完成的。

而通過返回的構造函數,我們可以發現:實例屬性還是通過構造函數方式來添加的。

最后,我們來看看 _classCallCheck 方法,它其實是一層校驗,保證了我們的實例對象是特定的類型。

所以,綜上所述,ES6 中的 class 只是個語法糖,它本質上還是用組合使用構造函數模式創建自定義類型的,這也就是為什么我們要學上面那些知識的初衷。

希望本文所述對大家JavaScript程序設計有所幫助。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩欧美一区二区三区| 欧美激情奇米色| 欧美一区亚洲一区| 97精品国产97久久久久久春色| 福利视频导航一区| 国产成人综合久久| 亚洲国产欧美一区二区三区同亚洲| 亚洲大胆美女视频| 2021国产精品视频| 日韩av在线最新| 热久久免费国产视频| 国产一区二区香蕉| 国产亚洲a∨片在线观看| 欧美高清激情视频| 久久久久亚洲精品成人网小说| 日韩免费观看av| 成人网在线观看| 亚洲精品动漫100p| 色多多国产成人永久免费网站| 日韩高清欧美高清| 久久综合电影一区| 久久九九亚洲综合| 欧美日韩免费区域视频在线观看| 最近更新的2019中文字幕| 最近2019中文字幕大全第二页| 亚洲成人av资源网| 亚洲一级片在线看| 亚洲字幕一区二区| 麻豆国产va免费精品高清在线| 国产美女精品免费电影| 97超级碰在线看视频免费在线看| 成人欧美在线视频| 日韩精品999| 久久这里只有精品视频首页| 亚洲第一网中文字幕| 97热精品视频官网| 国产精品久久久久久久久久久新郎| 亚洲精品一区中文| 国产女精品视频网站免费| 一区二区三区回区在观看免费视频| 国产精品久久久久久久久借妻| 亚洲国产欧美一区| 日韩欧美亚洲范冰冰与中字| 亚洲综合在线做性| 欧美激情免费观看| 国产综合久久久久| 欧美黑人巨大xxx极品| 97精品国产97久久久久久免费| 欧美一区二区.| 中文字幕在线看视频国产欧美在线看完整| 热久久美女精品天天吊色| 中文字幕免费精品一区高清| 亚洲男人天堂网站| 亚洲精品在线91| 日韩精品极品毛片系列视频| 欧美麻豆久久久久久中文| 成人福利在线观看| 国产精品自拍偷拍视频| 91精品视频免费观看| 亚洲人成电影网站色www| 日韩精品极品在线观看| 91成人天堂久久成人| 国产精品高精视频免费| 久久激情视频久久| 国内精品视频一区| 亚洲精品视频在线播放| 一区二区三区四区视频| 久久精品免费电影| 97视频在线观看免费高清完整版在线观看| 欧美日韩不卡合集视频| 国产免费成人av| 在线日韩欧美视频| 青草青草久热精品视频在线观看| 亚洲无限乱码一二三四麻| 国产精品盗摄久久久| 欧美中文字幕在线播放| 97不卡在线视频| 欧美—级高清免费播放| 亚洲色图日韩av| 成人久久一区二区| 国产精选久久久久久| 日韩高清av在线| 中文字幕综合在线| 8x海外华人永久免费日韩内陆视频| 日韩精品视频三区| 亚洲精品美女视频| 色多多国产成人永久免费网站| 亚洲经典中文字幕| 欧美日韩成人在线观看| 亚洲一区二区少妇| 国产精品视频久久| 亚洲精品影视在线观看| 26uuu久久噜噜噜噜| 欧美日韩日本国产| 亚洲国产日韩一区| www国产精品com| 国产91ⅴ在线精品免费观看| 欧美极品在线播放| 国产精品美女免费| 欧美丰满少妇xxxxx做受| 中文字幕无线精品亚洲乱码一区| 国产91精品高潮白浆喷水| 欧美日韩一区二区在线播放| 久久精品国产成人精品| 国产欧美一区二区三区视频| 日本精品久久久久久久| 欧美激情久久久久| 亚洲最大av网| 色偷偷888欧美精品久久久| 在线丨暗呦小u女国产精品| 欧美成年人视频网站欧美| 欧美另类极品videosbestfree| 日韩电影免费观看在线| 亚洲精品福利在线| 热久久免费视频精品| 欧美日本中文字幕| 亚洲成色777777在线观看影院| 国产aⅴ夜夜欢一区二区三区| 2019中文字幕在线免费观看| 久久久久久噜噜噜久久久精品| 日韩电影中文字幕| 日韩在线视频观看正片免费网站| 欧美中文字幕精品| 精品国产一区二区三区四区在线观看| 日韩一区二区精品视频| 日韩av在线一区二区| 美女国内精品自产拍在线播放| 久久6精品影院| 久久久久久久久国产精品| 亚洲在线视频观看| 中文字幕亚洲一区二区三区五十路| www.亚洲男人天堂| 91av视频在线| 欧美大片欧美激情性色a∨久久| 欧美怡春院一区二区三区| 欧美福利视频网站| 国内外成人免费激情在线视频| 久久97精品久久久久久久不卡| 亚洲成色777777女色窝| 欧洲一区二区视频| 亚洲人成网站999久久久综合| 中文字幕日韩精品有码视频| 成人福利在线视频| 91香蕉国产在线观看| 91精品国产色综合| 92看片淫黄大片看国产片| 疯狂做受xxxx高潮欧美日本| 亚洲最大的网站| 欧美电影院免费观看| 日韩少妇与小伙激情| 日韩欧美国产视频| 中文字幕亚洲一区二区三区五十路| 国产+人+亚洲| 国产精品扒开腿做爽爽爽的视频| 亚洲精品国产精品乱码不99按摩| 国产精品爽爽爽爽爽爽在线观看| 最近的2019中文字幕免费一页| 日韩高清av一区二区三区| 国产成人精品999| 日韩视频免费在线观看| 中文国产成人精品久久一| 欧美另类69精品久久久久9999| 久久久国产视频91| 国产亚洲视频在线|