萬盛學電腦網

 萬盛學電腦網 >> 腳本專題 >> javascript >> 淺析JavaScript原型繼承的陷阱

淺析JavaScript原型繼承的陷阱

 JavaScript和其它面向對象語言一樣,對象類型采用引用方式。持有對象的變量只是一個地址,而基本類型數據是值。當原型上存儲對象時,就可能有一些陷阱

JavaScript默認采用原型繼承。雖然沒有類(class)的概念,它的函數(function)可以充當構造器(constructor)。構造器結合this,new可以構建出類似Java的類。因此,JavaScript通過擴展自身能模擬類式(class-based)繼承。   JavaScript和其它面向對象語言一樣,對象類型采用引用方式。持有對象的變量只是一個地址,而基本類型數據是值。當原型上存儲對象時,就可能有一些陷阱。   先看第一個例子     代碼如下: var create = function() {      function Fn() {}      return function(parent) {          Fn.prototype = parent          return new Fn      }  }()    var parent = {      name: 'jack',      age: 30,      isMarried: false }  var child = create(parent)  console.log(child)    create工具函數實現了一個基本的原型繼承,每次調用create都會根據parent對象去復制一個新對象,新對象全部的屬性都來自於parent。這裡parent有三個屬性,都是基本數據類型:字符串,數字,布爾。   這時修改child看看會不會影響parent     代碼如下: child.name = 'lily' child.age = 20,  child.isMarried = true   console.log(child)  console.log(parent)      結果如下       即修改child不會影響到parent。   再看看另外一個例子     代碼如下: var create = function() {      function Fn() {}      return function(parent) {          Fn.prototype = parent          return new Fn      }  }()    var parent = {      data: {          name: 'jack',          age: 30,          isMarried: false     },      language: ['Java']  }  var child = create(parent)    child.data.name = 'lily' child.data.age = 20  child.data.isMarried = true child.language.push('javascript')  console.dir(child)  console.dir(parent)   注意這裡的parent的兩個屬性data,language都是引用類型,一個是對象,一個是數組。child仍然繼承與parent,隨後修改了child,結果如下       可以看到,此時parent也被修改了,和child的name,age等都一樣了。這是使用原型繼承時需要注意的。   使用繼承時比較好的方式是:   1,數據屬性采用類式繼承(掛在this上),這樣new時也可以通過參數配置   2,方法采用原型繼承,這樣能節省內存,同時子類重寫方法也不會影響父類   下面是一個滿足以上2點的寫類工具函數     代碼如下: /**   * @param {String} className   * @param {String/Function} superCls   * @param {Function} factory   */ function $class(name, superClass, factory) {      if (superClass === '') superClass = Object      function clazz() {          if (typeof this.init === 'function') {              this.init.apply(this, arguments)          }      }      var p = clazz.prototype = new superCls      clazz.prototype.constructor = clazz      clazz.prototype.className = className      var supr = superCls.prototype      window[className] = clazz      factory.call(p, supr)  }   對象類型放在父類原型上時務必小心子類修改其,這時繼承於該父類的所有子類的實例都將被修改。而這造成的bug很不容易發現。   ES5中加入了一個新API用來實現原型繼承:Object.create。可以用它替代上面自實現的create函數,如下     代碼如下: var parent = {      name: 'jack',      age: 30,      isMarried: false }  var child = Object.create(parent)  console.log(child) 
copyright © 萬盛學電腦網 all rights reserved