前言
在 JavaScript 中,繼承物件有以下兩種手法:
- 原型 (prototype) 繼承
- (新) 使用
extends
繼承
第一種手法是傳統的方法,第二種是 ES6 以後新增的語法糖;如果情境許可,應優先使用第二種手法,因為程式碼相對比較乾淨;相對來說,許有現存的程式碼還沒轉換到新的語法,所以還是要能讀傳統手法所寫的程式碼。
在本文中,我們會製作 Person
和 Employee
兩個物件,後者會繼承前者。在本文中,我們會展示兩種手法,供讀者比較。
使用原型繼承
以下是製作 Person
物件的程式碼範例:
let Person = function (name, age) {
var _age = 0.0;
Object.defineProperty(this, "age", {
get: function () {
return _age;
},
set: function (value) {
if (value <= 0) {
throw "Invalid age";
}
_age = value;
}
});
// Init the object.
this.name = name;
this.age = age;
return this;
};
由於我們要將 age
限制在合理的範圍值內,故我們設置 age
屬性 (property),用來包覆私有屬性 (field) _age
。
接著,建立 Employee
物件,該物件會繼承 Person
的建構函式:
let Employee = function (name, age, salary) {
// Inherit the constructor of `Person`.
Person.call(this, name, age);
var _salary = 0.0;
Object.defineProperty(this, "salary", {
get: function () {
return _salary;
},
set: function (value) {
if (value <= 0.0) {
throw "Invalid salary";
}
_salary = value;
}
});
// Init the object.
this.salary = salary;
return this;
};
這裡的關鍵在於使用 Person.call(this, name, age)
呼叫 Person
物件的建構函式,我們沒有在 Employee
建構函式中建立 name
和 age
屬性,但卻可以使用。
除了繼承建構函式外,也要繼承原型鍵:
// Inherit the prototype of `Person`
Employee.prototype = Object.create(Person.prototype);
但這時候 Employee
的建構函式會改成 Person
的建構函式,故要進行修正:
// Resume the constructor of `Employee`
Object.defineProperty(Employee, "constructor", {
value: Employee,
enumerable: false,
writable: true
});
透過這些步驟,我們就可以完整地將 Person
物件繼承到 Employee
物件中。
以下是使用 Employee
的範例:
let e = new Employee("Michelle", 30, 1000);
console.log(e.name);
console.log(e.age);
console.log(e.salary);
使用 extends
保留字繼承
在 ES6 之後,新增 extends
做為繼承物件的關鍵字,由於 JavaScript 本質上仍採用以原型為基礎的物件,可將 extends
視為一種語法糖。
用 class
建立 Person
物件:
let Person = (function () {
let fields = new WeakMap();
return class Person {
constructor (name, age) {
fields.set(this, {});
this.name = name;
this.age = age;
}
get age () {
return fields.get(this).age;
}
set age (value) {
if (value <= 0.0) {
throw "Invalid age";
}
fields.get(this).age = value;
}
};
})();
如同先前的例子,我們用 fields
做為私有變數。
接著,我們用 class
製作 Employee
物件,該物件會透過 extends
繼承 Person
:
let Employee = (function () {
let fields = new WeakMap();
return class Employee extends Person {
constructor (name, age, salary) {
super(name, age);
fields.set(this, {});
this.salary = salary;
}
get salary () {
return fields.get(this).salary;
}
set salary (value) {
if (value <= 0.0) {
throw "Invalid salary";
}
fields.get(this).salary = value;
}
};
})();
由這段程式碼可觀察到,我們不用再寫繼承原型鍵的部分。
使用方式和先前相同:
let e = new Employee("Michelle", 30, 1000);
console.log(e.name);
console.log(e.age);
console.log(e.salary);
結語
JavaScript 在本質上是以原型來繼承程式碼,但是若專案允許用 ES6 後的語法來寫 JavaScript 代碼,應優先使用 extend
保留字來繼承代碼,這樣寫起來會比較簡單。
至於原有的原型繼承模式則要能讀懂,畢竟許多現存的程式碼仍然是使用這些手法來寫的。