讀書筆記--對象稚茅、實(shí)例纸淮、原型、繼承

創(chuàng)建對象的設(shè)計(jì)模式

  • 工廠模式
    • 抽象了創(chuàng)建具體對象的過程亚享,用函數(shù)封裝以特定接口創(chuàng)建對象的細(xì)節(jié)
    • 解決了創(chuàng)建多個(gè)相似對象的問題咽块,沒有解決對象識別的問題
function createPerson(name,age){
  var obj = new Object();
  obj.name = name;
  obj.age = age;
  obj.sayName = function(){
    alert(this.name)
  }

return obj;
}

var person = createPerson('aa',12);
  • 構(gòu)造函數(shù)模式
    • 構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象,可以用instanceof檢測類型
    • 使用new操作符會經(jīng)歷以下四個(gè)步驟
      • 創(chuàng)建一個(gè)新對象
      • 將構(gòu)造函數(shù)的作用域賦給新對象(因此this指向了這個(gè)新對象)
      • 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對象添加屬性)
      • 返回新對象
    • 缺點(diǎn):
      • 每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍
      • 每個(gè)方法(函數(shù))就是一個(gè)對象虹蒋,不同實(shí)例上的同名函數(shù)是不相等的
  • 將函數(shù)抽離到外部使多個(gè)對象共共享全局作用域的函數(shù)雖然可以解決上面問題糜芳,但讓this.sayName = sayName全局方法會破壞了對象的封裝性
function Person(name,age){
  this.name = name;
  this.age = age;
  this.sayName = function(){
    alert(this.name)
  }
}

var person = new Person('aa',12);

person instanceof Person => true

原型模式

  • prototype
  • 只要創(chuàng)建了一個(gè)新函數(shù),就會為函數(shù)創(chuàng)建一個(gè)prototype屬性魄衅,這個(gè)屬性指向函數(shù)的原型對象
  • 包含了特定類型的所有對象實(shí)例共享的方法和屬性
  • prototype是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對象實(shí)例的原型對象
  • 默認(rèn)情況下所有原型對象會獲得一個(gè)constructor(構(gòu)造函數(shù)屬性)峭竣,這個(gè)屬性指向prototype屬性所在函數(shù)的指針
  • 當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例時(shí),內(nèi)部將包含一個(gè)指針晃虫,指向原型對象皆撩,連接實(shí)例與原型對象
function Person(name,age){
 
}
Person.prototype.name = name;
Person.prototype.age = age;
Person.prototype.sayName = function(){
    alert(this.name)
  }
var person = new Person('aa',12);

person instanceof Person => true
  • 判斷原型對象

    • Person.prototype.isPropertyOf(person) => true
    • Object.getPropertyOf(person) == Person.prototype => true (es5)
  • 多個(gè)對象實(shí)例共享原型所保存的屬性和方法的基本原理

    • 每當(dāng)代碼讀取某個(gè)對象的某個(gè)屬性時(shí),搜索從對象實(shí)例本身開始哲银,如果在實(shí)例中找到具有給定名字的屬性則返回該屬性的值
    • 如果沒有找到則繼續(xù)搜索指針指向的原型對象扛吞,如果在原型對象中找到給定名字的屬性,則返回這個(gè)值
  • 雖然可以通過對象實(shí)例訪問保存在原型實(shí)例中的值荆责,但是不能通過對象實(shí)例重寫原型中的值滥比,如果在實(shí)例中添加一個(gè)與原型實(shí)例中的一個(gè)屬性同名,訪問時(shí)實(shí)例屬性會屏蔽原型中這個(gè)屬性的值做院,只能訪問到這個(gè)實(shí)例上屬性的值盲泛。可以通過delete操作符刪除實(shí)例屬性键耕,重新訪問原型中的屬性

  • 使用hasOwnProperty()可以檢測一個(gè)屬性是存在實(shí)例中還是原型中寺滚,只有存在實(shí)例中才會返回true;person.hasOwnProperty('name')

  • 'name' in person 無論name是存在實(shí)例中還是原型對象中都會返回true

  //判斷對象原型上是否有這個(gè)屬性
  function hasPrototypeProperty(obj, attr){
    return (attr in obj) && !obj.hasOwnProperty(attr);
  }
  • 在使用for-in 循環(huán)時(shí)屈雄,返回的是能夠通過對象訪問的村视,可枚舉的屬性,包括了實(shí)例上的屬性和原型上的屬性酒奶。原型上不可枚舉的屬性蚁孔,但是在實(shí)例中定義了也可以獲取得到奶赔,如在實(shí)例中定義toString

  • Object.keys() (es5)

    • 取得對象上所有可枚舉的實(shí)例屬性,返回一個(gè)包含所有可枚舉的字符串?dāng)?shù)組
    • 也可以傳入一個(gè)原型對象勒虾,Object.keys(Person.prototype)纺阔,但不會沿著原型鏈往上尋找,只返回當(dāng)前prototype下的屬性
  • Object.getOwnPropertyNames()(ie9+)

    • 枚舉所有實(shí)例屬性修然,不管是否可枚舉

原型的動態(tài)性

  • 在原型中查找值的過程是一次搜索笛钝,對原型 對象所做的任何修改都能立即從實(shí)例上反映出來--即使是先創(chuàng)建了實(shí)例后修改原型也照樣如此
  var friend = new Person();
  Person.prototype.sayHi = function(){
      
    alert('hi')
  }
  friend.sayHi() //hi
  • 實(shí)例和原型之間松散的連接關(guān)系,可以隨時(shí)為原型添加屬性和方法愕宋,并且修改能夠立即在所有對象實(shí)例中反映出來
  • 但是重寫整個(gè)原型對象就不一樣了玻靡,調(diào)用構(gòu)造函數(shù)時(shí)會為實(shí)例添加一個(gè)指向最初原型的[[prototype]]指針,而把原型修改為另外一個(gè)對象就等于切斷了構(gòu)造函數(shù)與最初原型的聯(lián)系
  • 實(shí)例中的指針僅指向原型對象中贝,而不指向構(gòu)造函數(shù)
  var friend = new Person();
  Person.prototype = {
    sayHi:function(){
      alert('hi')
    }
  }
  friend.sayHi() //error
Paste_Image.png
  • 原型模式的缺點(diǎn)
    • 省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)囤捻,所有實(shí)例在默認(rèn)情況下都將取得相同的值
  • 當(dāng)使用原型屬性時(shí)會只要在一個(gè)實(shí)例上修改都會影響到所有的實(shí)例,例如在一個(gè)實(shí)例上修改數(shù)組

組合使用構(gòu)造函數(shù)和原型模式

  • 構(gòu)造函數(shù)定義實(shí)例屬性邻寿,原型模式定義方法和屬性

動態(tài)原型模式

  • 通過檢查某個(gè)應(yīng)該存在的方法是否有效蝎土,來決定是否需要初始化原型
 function Person(name,age){
  this.name = name;
  this.age = age;
  **if( typeof this.sayName != 'funcction'){
     Person.prototype.sayName = function(){
      alert('hi')
    }
  }**
}
  • 只有初次調(diào)用構(gòu)造函數(shù)時(shí)才會執(zhí)行將函數(shù)添加到原型中

寄生構(gòu)造函數(shù)模式

  • 類似于工廠模式,封裝創(chuàng)建對象的代碼绣否,然后返回新創(chuàng)建對象誊涯,return語句可以重寫調(diào)用構(gòu)造函數(shù)時(shí)返回的值
  function Person(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
      alert('hi');
    }
  return o;
  }
  • 使用場景:特殊情況下用來為對象創(chuàng)造構(gòu)造函數(shù)。

  • 假設(shè)創(chuàng)建一個(gè)具有額外方法的特殊數(shù)組蒜撮,由于不能直接修改Array構(gòu)造函數(shù)

    function SpecialArray(){
      var arr = new Array();
      arr.push.apply(values,arguments);
      arr.toPipedString = function(){
      return this.join('|');  
    }
    return arr;
    }
    

var colors = new SpecialArray('green','red');
colors.toPipedString()

- 缺點(diǎn):返回的對象和構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性之間沒有什么關(guān)系暴构,不能使用instanceof來確定對象類型

### 穩(wěn)妥構(gòu)造函數(shù)模式
- 特點(diǎn)
  - 新創(chuàng)建對象的實(shí)例方法不引用this
  - 不使用new操作符調(diào)用構(gòu)造函數(shù)
  - 除了返回對象上的屬性和方法,沒有其他辦法訪問到構(gòu)造函數(shù)內(nèi)的數(shù)據(jù)

  ```js
  function Person(name,age){
    var o = new Object();
    //這里可以定義私有數(shù)據(jù)
    o.sayName = function(){
      alert(name)
  }
  return o;
  }
var person = Person('green',12);
person.sayName() //只能通過sayName()方法去訪問name的值

繼承

原型鏈繼承
  • js以原型鏈作為實(shí)現(xiàn)繼承的主要方法
  • 基本思想是利用原型鏈讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法
  • 構(gòu)造函數(shù)段磨、原型取逾、實(shí)例的關(guān)系
    • 每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對象

    • 原型對象都有一個(gè)指向構(gòu)造函數(shù)的方法

    • 實(shí)例都包含一個(gè)指向原型對象的一個(gè)指針

function SuoperType(){
  this.property = true; 
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
}
function SubType(){
  this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubType = function(){
  return this.subprototype;
}
var instance = new SubType()
instance.getSuperValue();//true
833879510766076138.jpg
  • SubType不僅具有作為一個(gè)SuperType的實(shí)例所擁有的全部屬性和方法,而且擁有一個(gè)指向SuperType原型的指針

  • instance指向SubType原型苹支,SubType原型又指向SuperType的原型

  • 確定實(shí)例與原型的關(guān)系

  • instanceof操作符 只要用這個(gè)操作符來測試實(shí)例與原型中出現(xiàn)過的構(gòu)造函數(shù)砾隅,結(jié)果就會返回true

  • isPropertyOf() 只要是原型鏈中出現(xiàn)的原型,都可以說是該原型鏈所派生的實(shí)例的原型债蜜,都會返回true

  • 缺點(diǎn):

    • 原型屬性會被所有實(shí)例共享琉用,如果在父構(gòu)造函數(shù)中有一個(gè)this.colors=[]的數(shù)組,子構(gòu)造函數(shù)繼承后的實(shí)例可以修改這個(gè)存在于子構(gòu)造函數(shù)原型對象上的原型屬性
    • 在創(chuàng)建子類型的實(shí)例時(shí)策幼,不能向超類型的構(gòu)造函數(shù)傳遞參數(shù)。實(shí)際上是沒有辦法在不影響所有實(shí)例的情況下給超類型的構(gòu)造函數(shù)傳遞參數(shù)
借用構(gòu)造函數(shù)
  • 在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)奴紧,通過call,apply 改變對象的this指向
function SuoperType(name){
  this.colors = ['red']; 
  this.name = name;
}

function SubType(name){
  SuperType.call(this,name);
}
SubType.prototype = new SuperType();
var instance = new SubType('1')
instance.colors.push('green') => ['red','green']

var instance2 = new SubType('2')
instance2.colors.push('black') => ['red','black']
組合繼承
  • 將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起
  • 使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承
  • 使用借用構(gòu)造函數(shù)實(shí)現(xiàn)對實(shí)例屬性的繼承
function SuperType(name){
  this.colors = ['red']; 
  this.name = name;
}
SuperType.prototype.sayName = function(){
  alert(this.name)
}
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayName = function(){
  alert(this.name)
}
var instance = new SubType('1',12)
instance.sayName() => 1
instance.colors => ['red']
var instance2 = new SubType('2',21)
instance2.sayName() => 2
instance2.colors => ['red']
原型式繼承
  • 基于一個(gè)對象上,這個(gè)對象相當(dāng)于作為原型特姐,再根據(jù)需求對得到的對象加以修改
  • 在沒有必要興師動眾創(chuàng)建構(gòu)造函數(shù),而只想讓一個(gè)對象與另一個(gè)對象保持類似的情況下使用
  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  var person = {
    name:'n',
    friends:['a','b']
  }
  var anotherPerson = object(person);
  anotherPerson.name = 'y';  =>y
  anotherPerson.colors.push('c');  => ['a','b','c']
  var person = object(person);
 person.name = 'yy';  =>yy
person.color; =>['a','b','c']
  • Object.create() es5新增規(guī)范原型式繼承

 var person = {
   name:'n',
   friends:['a','b']
 }
 var anotherPerson = Object.create(person);
 anotherPerson.name = 'y';  =>y
 anotherPerson.colors.push('c');  => ['a','b','c']
 var person =  Object.create(person);
person.name = 'yy';  =>yy
person.color; =>['a','b','c']
寄生式繼承
  • 創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù)黍氮,在函數(shù)內(nèi)部以某種方式增強(qiáng)對象
  function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
  }
  function createAnother(original){
    var clone = object(original);
    clone.sayHi = function(){
      alert('hi')
    }
  return clone;
  }
  var person = {
    name:'n',
    friends:['a','b']
  }
  var anotherPerson = createAnother(person);
  anotherPerson.sayHi(); =>hi
寄生組合式繼承
  • 組合繼承最大的問題是無論什么情況下都會調(diào)用兩次超類型構(gòu)造函數(shù)
    • 一次是創(chuàng)建子類型原型時(shí)唐含,一次是子函數(shù)的內(nèi)部構(gòu)造函數(shù)
  • 寄生組合繼承通過借用構(gòu)造函數(shù)來繼承屬性浅浮,通過原型鏈混成形式來繼承方法。
  • 思路是不必為了指定的子類型原型而調(diào)用超類型的構(gòu)造函數(shù)捷枯。
  • 使用寄生式繼承來繼承超類型的原型滚秩,然后再將結(jié)果指定給子類型的原型
    function inheritPrototype(subType,superType){
      var prototype = object(superType);
      prototype.constructor = subType;
      subType.prototype = prototype;
  }

function SuperType(name){
  this.colors = ['red']; 
  this.name = name;
}
SuperType.prototype.sayName = function(){
  alert(this.name)
}
function SubType(name,age){
  SuperType.call(this,name);
  this.age = age;
}
SubType.prototype =inheritPrototype(subType,superType) ;
SubType.prototype.sayName = function(){
  alert(this.name)
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市淮捆,隨后出現(xiàn)的幾起案子郁油,更是在濱河造成了極大的恐慌,老刑警劉巖攀痊,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桐腌,死亡現(xiàn)場離奇詭異,居然都是意外死亡苟径,警方通過查閱死者的電腦和手機(jī)案站,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棘街,“玉大人蟆盐,你說我怎么就攤上這事≡庋常” “怎么了石挂?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恩沽。 經(jīng)常有香客問我誊稚,道長,這世上最難降的妖魔是什么罗心? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任里伯,我火速辦了婚禮,結(jié)果婚禮上渤闷,老公的妹妹穿的比我還像新娘疾瓮。我一直安慰自己,他們只是感情好飒箭,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布狼电。 她就那樣靜靜地躺著,像睡著了一般弦蹂。 火紅的嫁衣襯著肌膚如雪肩碟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天凸椿,我揣著相機(jī)與錄音削祈,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛髓抑,可吹牛的內(nèi)容都是我干的咙崎。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼吨拍,長吁一口氣:“原來是場噩夢啊……” “哼褪猛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起羹饰,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤伊滋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后严里,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體新啼,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年刹碾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了燥撞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡迷帜,死狀恐怖物舒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情戏锹,我是刑警寧澤冠胯,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站锦针,受9級特大地震影響荠察,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奈搜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一悉盆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馋吗,春花似錦焕盟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绍哎,卻和暖如春来农,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背崇堰。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工备图, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓揽涮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饿肺。 傳聞我的和親對象是個(gè)殘疾皇子蒋困,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評論 2 361

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