JavaScript之OOP編程

定義一個對象的最簡單的方法

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); // true
car1.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");



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章