JavaScript之OOP編程,javascriptoop
定義一個對象的最簡單的方法
var car = {color:"red",drive:function() {alert(this.color + " car moved");}}這個方法不是在非常有用,因為它建立了一個單獨的對象,而這個對象和任何常見的資料結構沒有任何聯絡,為了建立第二個car執行個體,必須重新定義它的結構.
通過建構函式建立一個新對象
function Car() {this.color = "red";this.drive = function(){alert(this.color + " car moved");}}var car = new Car();car.drive();
JavaScript中的每個函數都有一個稱為prototype的屬性.如果某個函數被用作建構函式,則這個屬性會被自動通過new調用建立對象的原型
function Car() {this.color = "red";}Car.prototype.drive = function(){alert(this.color + " car moved");}var car = new Car();car.drive();
對prototype屬性所做的任何更改能夠應用於通過new Car()構造的每一個對象,不管它是在更改之前還是更改後建立.為Car.prototype 添加新函數.對於共用相同原型的每一個對象,該函數都可以立即使用,無論它是在更改之前或是之後構造,具體如下:
// 更新原型function Car(color) {this.color = color;}Car.prototype.drive = function(){alert(this.color + " car is driving");}var car1 = new Car("red");alert(car1.__proto__ === Car.prototype); // truecar1.drive();// 為原型添加新的函數Car.prototype.stop = function(){alert(this.color + " car has stopped");}var car2 = new Car("blue");// 共用同一對象原型的對象alert(Object.getPrototypeOf(car1) === Object.getPrototypeOf(car2)); // true// 這兩個對象現在都能訪問這個新的方法car2.stop();car1.stop();
物件導向的很重要一個特性就是繼承了,這裡只要介紹通過原型鏈繼承
// 繼承的基本設定function Car(color) {this.color = color;}Car.prototype.drive = function(){alert(this.color + " car is driving");}Car.prototype.turn = function(direction) {alert(this.color + " car turns " + direction);}Car.prototype.stop = function() {alert(this.color + " car has stopped");}// 消防車function FirTruck() {}FirTruck.prototype.turnCannon = function(direction) {alert("Cannon moves to the " + direction);}var truck = new FirTruck();// 因為Car.prototype 並不在truck對象的原型鏈中// 因此方法move()並不可用truck.move();truck.turnCannon("right");
JavaScript中有一個很特殊的Object.create(proto, properties)方法, 注:IE8及以下不支援
它可以用來建立一個新的空白對象並將其原型設定為proto
function Car(color) {this.color = color;}Car.prototype.move = function(){alert(this.color + " car is driving");}Car.prototype.turn = function(direction) {alert(this.color + " car turns " + direction);}Car.prototype.stop = function() {alert(this.color + " car has stopped");}if(!Object.create){ // 相容性處理 FireTruck.prototype = Car.prototype;}else{FireTruck.prototype = Object.create(Car.prototype);}function FireTruck() {}// 檢查,以防萬一//alert(Object.getPrototypeOf(FireTruck.prototype) === Car.prototype);// 如果為true// 則Car.prototype被添加到鏈中FireTruck.prototype.turnCannon = function(direction){alert("Cannon moves to the " + direction);}var truck = new FireTruck();// 現在可以工作了,因為Car.prototype已經在truck對象的原型鏈中truck.move();truck.turnCannon("right");
代碼可以運行,但其輸出還是存在一些問題
true
undefined car is driving
Cannon moves to the right
小汽車的color現在可以通過Car的建構函式進行設定.建構函式本身沒有執行,所以其color顯示為undefined.這個問題很好解決,修改FireTruck建構函式,在其中添加對Car建構函式的調用,這樣Car就可以初始化它自己的物件變數
function FireTruck() {Car.call(this, "red");}再次運行這段代碼,欣賞一下正確的輸出
即使該代碼能夠成功運行,但仍有一個小問題需要解決.當這個新函數建立時,它的prototype屬性並不為空白.它有一個稱為constructor的屬性將引用函數本身,當調用
FireTruck.prototype = Object.create(Car.prototype);
時,該屬性丟失,因為這個建立的對象並不具有自己的屬性.可以將丟失的屬性作為第二個參數傳遞給create
FireTruck.prototype = Object.create(Car.prototype, {constructor: {value: FireTruck, // 和FireTruck.prototype.constructor一樣enumerable: false,writable: true,configurable: true}});這裡的constructor並不是通常的屬性.當對象的鍵值進行迴圈時,它不會出現,但可以通過名稱直接存取.通過給create()的第二個參數傳遞屬性"描述符"可以取得相同的效果
描述符具有如下所示的幾個欄位
value: 初始值
enumerable: 如果屬性顯示在對象屬性的列表中, 在像for...in 這樣的迴圈或Object.keys中
writable: 如果屬性可以被指派一個不同的值
configurable: 如果描述符定義的規則可以被修改或其屬性可以刪除,則為true
這樣就可以模模擬實的 constructor 的行為
最終版本的的繼承樣本
function extend(subConstructor, superConstructor) {if(!Object.create){subConstructor.prototype = superConstructor.prototype;subConstructor.constructor = {value: FireTruck, // 和FireTruck.prototype.constructor一樣enumerable: false,writable: true,configurable: true}}else{subConstructor.prototype = Object.create(superConstructor.prototype, {constructor: {value: FireTruck, // 和FireTruck.prototype.constructor一樣enumerable: false,writable: true,configurable: true}});}}function Car(color) {this.color = color;}Car.prototype.move = function(){alert(this.color + " car is driving");}Car.prototype.turn = function(direction) {alert(this.color + " car turns " + direction);}Car.prototype.stop = function() {alert(this.color + " car has stopped");}function FireTruck() {Car.call(this, "red");}extend(FireTruck, Car);FireTruck.prototype.turnCannon = function(direction){alert("Cannon moves to the " + direction);}var truck = new FireTruck();truck.move();truck.turnCannon("right");