JS設(shè)計(jì)模式 -- 面向?qū)ο?-- 1 封裝

以下內(nèi)容來(lái)自張榮銘《JavaScript設(shè)計(jì)模式》第2章。

兩種編程風(fēng)格 -- 面向過(guò)程與面向?qū)ο蟆?/p>

面向?qū)ο缶幊淌侵笇⑿枨蟪橄蟪梢粋€(gè)對(duì)象,然后針對(duì)這個(gè)對(duì)象分析其特征(屬性)與動(dòng)作(方法),這個(gè)對(duì)象我們稱之為類。

面向?qū)ο缶幊趟枷胫?,其中一個(gè)特點(diǎn)就是封裝,即把需要的功能放在一個(gè)對(duì)象里,便于管理。但由于JavaScript屬于解釋性的弱類語(yǔ)言,沒(méi)有經(jīng)典強(qiáng)類型語(yǔ)言中那種通過(guò)class等關(guān)鍵字實(shí)現(xiàn)的類的封裝方式。

1 封裝

1-1 創(chuàng)建一個(gè)類

首先聲明一個(gè)函數(shù),保存在一個(gè)變量?jī)?nèi)。按照編程習(xí)慣,代表類的首字母一般會(huì)用大寫;
然后這個(gè)函數(shù)(類)的內(nèi)部通過(guò)對(duì)this(函數(shù)內(nèi)部自帶的一個(gè)變量,用于指向當(dāng)前這個(gè)對(duì)象)變量添加屬性或者方法來(lái)實(shí)現(xiàn)對(duì)類添加屬性或者方法。
也可以通過(guò)在類的原型(類也是一個(gè)對(duì)象,也有原型prototype)上添加屬性和方法。兩種方法:一種是一一為原型對(duì)象屬性賦值,另一種則是將一個(gè)對(duì)象賦值給類的原型對(duì)象。但兩種方法不建議混用。

var Book = function (id, bookname, price) {
  this.id = id;
  this.bookname = bookname;
  this.price = price;
}

Book.prototype.display = function () {};
// 或者
Book.prototype = {
  display: function () {}
}

使用:
new 關(guān)鍵字來(lái)實(shí)例化(創(chuàng)建)新的對(duì)象。使用實(shí)例化對(duì)象的屬性或者方法時(shí),可以通過(guò)點(diǎn)語(yǔ)法訪問(wèn)。

JavaScript是一種基于原型prototype的語(yǔ)言,所以在創(chuàng)建一個(gè)對(duì)象時(shí)都有一個(gè)原型prototype用于指向其繼承的屬性、方法。這樣通過(guò)prototype繼承的方法并不是對(duì)象自身的,在使用這些方法時(shí)需要通過(guò)prototype一級(jí)一級(jí)查找得到。

通過(guò) this 添加的屬性、方法是在當(dāng)前對(duì)象上添加的,是該對(duì)象自身?yè)碛械?,通過(guò)類創(chuàng)建一個(gè)新對(duì)象時(shí),this 指向的屬性和方法都會(huì)得到相應(yīng)的創(chuàng)建。通過(guò)prototype繼承的屬性或者方法是每個(gè)對(duì)象通過(guò)prototype訪問(wèn)到,每次通過(guò)類創(chuàng)建一個(gè)新對(duì)象時(shí)這些屬性和方法不會(huì)再次創(chuàng)建。

1-2 constructor屬性

當(dāng)創(chuàng)建一個(gè)函數(shù)或者對(duì)象時(shí)都會(huì)為其創(chuàng)建一個(gè)原型對(duì)象prototype,在prototype對(duì)象中會(huì)創(chuàng)建一個(gè)constructor屬性。constructor屬性指向的就是擁有整個(gè)原型對(duì)象的函數(shù)或?qū)ο蟆?/p>

1-3 屬性與方法封裝

通過(guò)JS函數(shù)級(jí)作用域的特征來(lái)實(shí)現(xiàn)在函數(shù)內(nèi)部創(chuàng)建外界訪問(wèn)不到的私有化變量和私有化方法。

//  私有屬性 與 私有方法  , 特權(quán)方法, 對(duì)象公有屬性和對(duì)象共有方法, 構(gòu)造器
var Book = functiion (id, name, price) {
  //  私有屬性
  var num = 1;
  // 私有方法
  function checkId () {};
  //  特權(quán)方法
  this.getName = function () {};
  this.getPrice = function () {};
  this.setName = function () {};
  this.setPrice = function () {};
  //  對(duì)象公有屬性
  this.id = id;
  // 對(duì)象公有方法
  this.copy = function () {};
  //  構(gòu)造器
  this.setName(name);
  this.setPrice(price);
}

通過(guò)new關(guān)鍵字實(shí)例化對(duì)象時(shí),由于對(duì)類執(zhí)行一次,所以類的內(nèi)部 this上定義的屬性和方法復(fù)制到新創(chuàng)建的對(duì)象上,成為對(duì)象公有化的屬性和方法,而其中的一些方法能訪問(wèn)到類的私有屬性和方法。
通過(guò)new關(guān)鍵字創(chuàng)建的新對(duì)象,無(wú)法獲取類外面通過(guò)點(diǎn)語(yǔ)法添加的屬性和方法,但是可以通過(guò)類來(lái)使用。因此在類外面通過(guò)點(diǎn)語(yǔ)法定義的屬性以及方法被稱為類的靜態(tài)共有屬性和類的靜態(tài)共有方法。
而類通過(guò)prototype創(chuàng)建的屬性或者方法在類實(shí)例的對(duì)象中是可以通過(guò)this訪問(wèn)到的,所有prototype對(duì)象中的屬性和方法稱為共有屬性和共有方法。

//  類靜態(tài)公有屬性(對(duì)象不能訪問(wèn))
Book.isChinese = true;
//  類靜態(tài)公有方法(對(duì)象不能訪問(wèn))
Book.resetTime = function () {
  console.log('new-Time');
}
Book.prototype = {
  //  公有屬性
  isJSBook: false,
  //  公有方法
  display: function () {}
}

聽(tīng)過(guò)new關(guān)鍵字創(chuàng)建的對(duì)象實(shí)質(zhì)是對(duì)新對(duì)象this的不斷賦值,并將prototype指向類的prototype所指向的對(duì)象,而類的構(gòu)造函數(shù)外面通過(guò)點(diǎn)語(yǔ)法定義的屬性方法是不會(huì)添加到新創(chuàng)建的對(duì)象上去的。因此想要在新創(chuàng)建的對(duì)象中使用 isChinese需要通過(guò)Book類,而不能通過(guò)this, 如 Book.isChinese。
通過(guò)類的原型prototype上定義的屬性在新對(duì)象里可以直接使用點(diǎn)方法訪問(wèn)。原因:新對(duì)象的prototype和類的prototype指向的是同一個(gè)對(duì)象。

例:

var b = new Book(11, 'javascript設(shè)計(jì)模式', 50);
console.log(b.num); //  undefined
console.log(b.isJSBook); // false
console.log(b.id); // 11
console.log(b.isChinese); // undefined

console.log(Book.isChinese); // true
Book.resetTime();  //  new-Time
1-4 閉包

閉包是有權(quán)訪問(wèn)另外一個(gè)函數(shù)作用域中變量的函數(shù),即在一個(gè)函數(shù)內(nèi)部創(chuàng)建另外一個(gè)函數(shù)。

//  利用閉包實(shí)現(xiàn)
var Book = (function () {
  // 靜態(tài)私有變量
  var bookNum = 0;
  //  靜態(tài)私有方法
  function checkBook (name) { };
  //  返回構(gòu)造函數(shù)
  return function (newId, newName, newPrice) {
    //  私有變量
    var name, price;
    //  私有方法
    function checkID (id) { };
    //  特權(quán)方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有屬性
    this.id = newId;
    //  公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100)  throw new Error('我們僅出版100本書');
    //  構(gòu)造器
    this.setName(name);
    this.setPrice(price);
  }
})();

Book.prototype = {
  //  靜態(tài)公有屬性
  isJSBook: false;
  //  靜態(tài)公有方法
  display: function () {}
}
//  利用閉包實(shí)現(xiàn)
var Book = (function () {
  // 靜態(tài)私有變量
  var bookNum = 0;
  //  靜態(tài)私有方法
  function checkBook (name) { };
  //  創(chuàng)建類
  function book(newId, newName, newPrice) {
    //  私有變量
    var name, price;
    //  私有方法
    function checkID (id) { };
    //  特權(quán)方法
    this.getName = function () {};
    this.getPrice = function () {};
    this.setName = function () {};
    this.setPrice = function () {};
    // 公有屬性
    this.id = newId;
    //  公有方法
    this.copy = function () {};
    bookNum++;
    if (bookNum > 100)  throw new Error('我們僅出版100本書');
    //  構(gòu)造器
    this.setName(name);
    this.setPrice(price);
  }

// 構(gòu)建原型
  _book.prototype = {
    //   靜態(tài)公有屬性
    isJSBook: false;
    //  靜態(tài)公有方法
    display: function () {}
  }

//   返回類
return _book;
})();

1-5 安全模式
//  圖書安全類
var Book = function (title, time, type) {
  //  判斷執(zhí)行過(guò)程中, this是否是當(dāng)前這個(gè)對(duì)象(如果是說(shuō)明是用new創(chuàng)建的)
  if (this instanceof Book) {
    this.title = title;
    this.time = time;
    this.type = type;
    //  否則重新創(chuàng)建這個(gè)對(duì)象
  } else {
    return new Book(title, time, type);
  }
}

var book = Book('JavaScript', '2014', 'js');
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容