JavaScript默認採用原型繼承。雖然沒有類(class)的概念,它的函數(function)能夠充當構造器(constructor)。構造器結合this,new能夠構建出相似Java的類。所以,JavaScript經過擴展自身能模擬類式(class-based)繼承。javascript
JavaScript和其它面嚮對象語言同樣,對象類型採用引用方式。持有對象的變量只是一個地址,而基本類型數據是值。當原型上存儲對象時,就可能有一些陷阱。java
先看第一個例子數組
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有三個屬性,都是基本數據類型:字符串,數字,布爾。app
這時修改child看看會不會影響parent函數
child.name = 'lily' child.age = 20, child.isMarried = true console.log(child) console.log(parent)
結果以下工具
即修改child不會影響到parent。post
再看看另一個例子this
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,結果以下spa
能夠看到,此時parent也被修改了,和child的name,age等都同樣了。這是使用原型繼承時須要注意的。prototype
使用繼承時比較好的方式是:
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)