繼承:非構(gòu)造函數(shù)式

Javascript中,一切可變的鍵控集合(keyed collections),稱為對象。(好拗口,實(shí)際上就是指引用數(shù)據(jù)類型)

在一個純粹的原型模式中,應(yīng)當(dāng)拋棄類的概念,擁抱于對象。由于沒有了constructor的概念,繼承會顯得更為簡單——子對象可以繼承父對象的屬性/方法。


方法一:通過創(chuàng)建一個中介空構(gòu)造函數(shù)去繼承父對象,然后子對象通過中介構(gòu)造函數(shù)生成的實(shí)例來繼承父對象

/**
 * @param {object} o - 需要繼承的父對象
 */
function extendsObject(o) {
  var F = function() {};
  F.prototype = o;
  return new F;
}
// 父對象
var parent_1 = { // 由于已經(jīng)不在通過構(gòu)造函數(shù)的方式,所以這里的父對象首字母不再以大寫形式出現(xiàn)
  name: 'parent_1',
  sayName: function () {
    return this.name;
  }
};
// 子對象
var c1 = extendsObject(parent_1);
c1.uber = parent_1; // 訪問父對象的通道
// 進(jìn)而再對子對象進(jìn)行擴(kuò)展,所以,這種繼承方法,也叫做差異化繼承(differential inheritance)
// 即通過創(chuàng)建一個新的子對象,然后指明它與其繼承的父對象之間的區(qū)別
c1.name = 'child_1';
// 訪問子對象方法
console.log(c1.sayName()); // child_1
// 還可以方便地訪問父對象的方法
console.log(c1.uber.sayName()); // parent_1

方法二:子對象直接淺拷貝父對象上的屬性

/**
 * @param {object} o - 需要繼承的父對象
 */
function shallowCopyObject(o) {
  var c = {};
  for(var k in o) {
    c[k] = o[k];
  }
  c.uber = o;
  return c;
}
var parent_2 = {
  name: 'parent_2',
  children: ['foo', 'bar'], // 本意是給父對象創(chuàng)建一個“私有屬性”,但是這里卻會被繼承的子對象所篡改,等會會說到如何解決這個問題(轉(zhuǎn)方法四)
  sayName: function() {
    return this.name;
  }
};
var c2 = shallowCopyObject(parent_2); // 自此,c2繼承了parent_2上的所有屬性
// 不幸的是,由于父對象中的children屬性是一個數(shù)組對象,是引用數(shù)據(jù)類型,
// 故parent_2.children只是存儲了該數(shù)組對象在堆中的地址,也即是說,對子對象上children的任何改動,
// 都會影響到父對象,非常糟糕,例如:
c2.children[0] = 'ugly_foo'; // 父對象的“私有屬性”被非法篡改了
console.log(parent_2.children); // ['ugly_foo', 'bar']

方法三:子對象直接深拷貝父對象上的屬性

function deepCopyObject(o) {
  var c = {}, t;
  for (var k in o) {
    if (typeof o[k] === 'object') {
      c[k] = (o[k].constructor === Array) ? [] : {};
      c[k] = deepCopyObject(o[k]);
    } else {
      c[k] = o[k];
    }
  }
  c.uber = o;
  return c;
}

以上代碼有一個問題,就是父對象中的每一個引用類型的屬性,都會產(chǎn)生一個uber屬性,并指向該引用類型的屬性(好拗口> <)。
下面改進(jìn)一下:

function deepCopyObject() {
  // 使用閉包(closure),在閉包中創(chuàng)建root變量作為根父對象的判斷標(biāo)識
  var root = true;
  return function _deepCopyObject(o) {
      var c = {};
      if (root) { // 如此,子對象中便只有第一層會產(chǎn)生uber屬性,并指向父對象
          c.uber = o;
          root = false;
      }
      for (var k in o) {
          if (typeof o[k] === 'object') {
              i += 1;
              c[k] = (o[k].constructor === Array) ? [] : {};
              c[k] = _deepCopyObject(o[k]);
          } else {
              c[k] = o[k];
          }
      }
      return c;
  }
}
var parent_3 = {
  name: 'parent_3',
  children: ['foo', 'bar'],
  sayName: function () {
      return this.name;
  }
};
var c3 = deepCopyObject()(parent_3);
c3.name = 'child_3';
c3.children[0] = 'ugly_foo';
// 我們可以看到,對于子對象中引用類型的改動,沒有影響到父對象
// 即是說,子對象中的引用類型屬性,已經(jīng)完全地存儲到了不同的內(nèi)存地址中,這就是深拷貝的作用
console.log(parent_3.children); // ['foo', 'bar']

方法四:模塊化繼承

到此為止,我們所看到的繼承模式,無論是父還是子,都沒有私有屬性和方法,所有的屬性和方法都是對外可見的,且能夠被篡改。為此我們引進(jìn)另一種好的方法——模塊模式,如下:

// 父對象
var parent_4 = function(o) {
  var that = {};
  // 其他的私有屬性
  var children = ['foo', 'bar'];
  // ...
  var sayName = function() {
    return 'parent sayName: ' + o.name;
  };
  var sayChildren = function () {
    return children;
  };
  // 對外接口,暴露出可被繼承的方法
  // 且這里將函數(shù)的定義與暴露給that分兩步開寫的好處是:
  // 1. 如果其他方法想要調(diào)用sayName,可以直接調(diào)用sayName()而不是that.sayName()
  // 2. 如果該對象實(shí)例被篡改,比如that.sayName已經(jīng)被替換掉,sayName將同樣起作用,
  //    因?yàn)閟ayName方法是私有的,重寫that.sayName只會重新賦值,不會破壞到私有方法
  that.sayName = sayName;
  that.sayChildren = sayChildren;
  // 最后返回包含了可被繼承的屬性和方法的對象
  return that; 
};
// 子對象
var child_4 = function(o) {
  var that = parent_4(o); // 先繼承父對象
  // 同時我們還可以在子對象中調(diào)用父對象的方法,盡管上下文環(huán)境已經(jīng)變化成子對象了(即this的指向)
  var uberSayName = that.sayName;
  // 或者也可以創(chuàng)建整個錨來指向父對象(浪費(fèi)內(nèi)存的做法)
  that.uber = parent_4(o);
  // 子對象自身的屬性/方法
  var sayName = function() {
    return o.name;
  };
  var sayHi = function() {
    return o.saying;
  };
  // 對外暴露子對象的方法/屬性
  that.sayName = sayName;
  that.uberSayName = uberSayName;
  that.sayHi = sayHi;
  return that;
};
// 創(chuàng)建子對象實(shí)例
var c4 = child_4({
  // name和saying屬性現(xiàn)在完全是私有屬性了,除非調(diào)用對外接口sayName和sayHi,否則無法對其進(jìn)行訪問,
  // 這樣,我們擁有了真正意義上的私有屬性,而不是那些有著稀奇古怪名稱的“偽私有屬性”
  name: 'child_4',
  saying: 'hello world!'
});
console.log(c4.sayName()); // 'child_4'
console.log(c4.uberSayName()); // 'parent sayName: child_4'
console.log(c4.uber.sayName()); // 'parent sayName: child_4'
console.log(c4.sayHi()); // 'hello world!'

結(jié)論:使用模塊化繼承的好處很多,其中最重要的就是對私有屬性的保護(hù)(對象封裝),以及對外暴露接口(對象間通信),以及訪問父對象方法的能力

歡迎交流,完。兄弟篇——繼承:構(gòu)造函數(shù)式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 博客內(nèi)容:什么是面向?qū)ο鬄槭裁匆嫦驅(qū)ο竺嫦驅(qū)ο缶幊痰奶匦院驮瓌t理解對象屬性創(chuàng)建對象繼承 什么是面向?qū)ο?面向?qū)ο?..
    _Dot912閱讀 1,443評論 3 12
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,855評論 1 10
  • 前言 人生苦多,快來 Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,249評論 9 118
  • 今日臨完二遍,根據(jù)大家建議,寫得稍微小了些,盡量注意了轉(zhuǎn)折和留白,還不是特別到位。請大家指教?!颈疚挠伞皩扅c(diǎn)小書法...
    寫點(diǎn)小書法閱讀 628評論 0 0
  • 《最好的告別》,作者是美國著名外科醫(yī)生阿圖·葛文德。看到這個書名的時候讓我有一種莫名的感動,可能因?yàn)槲覍W(xué)的是醫(yī)藥專...
    Lynette_C閱讀 477評論 2 16