JavaScript預設採用原型繼承。雖然沒有類別(class)的概念,它的函數(function)可以充當建構器(constructor)。建構器結合this,new可以建構出類似Java的類別。因此,JavaScript透過擴展自身能模擬類別式(class-based)繼承。
JavaScript和其它物件導向語言一樣,物件類型採用引用方式。持有物件的變數只是一個地址,而基本型別資料是值。當原型上儲存物件時,就可能有一些陷阱。
先看第一個例子
複製程式碼 代碼如下:var create = function() {
function Fn() {}
return function(parent) {
}
} ()
var parent = {
name: 'jack',
age: 30,
isMarried: false
} >console.log(child)
create工具函數實現了一個基本的原型繼承,每次調用create都會根據parent對象去複製一個新對象,新對象全部的屬性都來自於parent 。這裡parent有三個屬性,都是基本資料型態:字串,數字,布林。
這時修改child看看會不會影響parent
複製程式碼
console.log(child)
console.log(parent)
結果如下
即修改child不會影響parent。
再看另一個例子
複製程式碼
var parent = {
data: {
name: 'jack',
},
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 {文字列} className
* @param {文字列/関数} superCls
* @param {関数} ファクトリー
*/
function $class(name, superClass, Factory) {
if (superClass === '') superClass = Object
function clazz() {
if (typeof this.init === 'function') {
this.init.apply(this, argument)
}
}
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)
}
オブジェクト型を親クラスのプロトタイプに配置する場合、サブクラスがそれを変更する場合、親クラスから継承したサブクラスのインスタンスはすべて次のようになります。変更されました。そして、これによって引き起こされるバグは見つけるのが非常に困難です。
ES5 では、プロトタイプの継承を実装するための新しい API Object.create が追加されています。次のように、これを使用して、上記の自己実装された作成関数を置き換えることができます。
varparent = {
name: 'jack',
age: 30,
isMarried: false
}
var child = Object.create(parent )
console.log(子)