JavaScript Object Oriented Programming: Pseudo-Classical Pattern
06 Feb 2016Pseudo-class declaration
在 pseudo-classical pattern 中,物件是由「建構子」(constructor)這個函式所建立,並把 method 放到建構子的 prototype 中。
function Animal(name) {
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function() {
this.canWalk = false;
console.log(this.name + ' sits down.');
},
};
var animal = new Animal('Pet'); // (1)
console.log(animal.canWalk); // true
animal.sit(); // (2)
console.log(animal.canWalk); // false
- 當「new Animal(name)」被呼叫的時候,新物件收到參考到 Animal.prototype 的
__proto__
。 animal.sit
改變animal.canWalk
的值,所以這個 animal 物件的 canWalk 為 false,但其餘的仍為 true。
備註
- method 和預設的屬性會放在 prototype 中。
- prototype 中的 method,若使用 this,指的是目前的物件。所以
animal.sit()
指的是這一個 animal。
Inheritance
使用 Rabbit.prototype.__proto__ == Animal.prototype
來讓 Rabbit 繼承 Animal。我們使用 inherit 這個 function 來做設定。
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
function Animal(name) {
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function() {
this.canWalk = false;
console.log(this.name + ' sits down.');
},
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = inherit(Animal.prototype);
Rabbit.prototype.jump = function() {
this.canWalk = true;
console.log(this.name + ' jumps!');
};
var rabbit = new Rabbit('John');
console.log(rabbit.canWalk); // true
rabbit.sit(); // John sits down.
console.log(rabbit.canWalk); // false
rabbit.jump(); // John jumps!
Calling superclass constructor
「superclass」這個建構子不是被自動呼叫的,我們使用「apply」將 Animal 這個函式套用到目前的物件上。這樣就能將 Animal 建構子的內文狀態設給目前使用的物件。
function Rabbit(name) {
Animal.apply(this, arguments);
}
Overriding a method(polymorphism)
當 method 被覆寫,我們仍可能會希望能呼叫之前尚未被覆寫的 method。
Calling a parent method after overriding
我們可以直接呼叫 parent prototype。
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
function Animal(name) {
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function() {
this.canWalk = false;
console.log(this.name + ' sits down.');
},
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = inherit(Animal.prototype);
var rabbit = new Rabbit('John');
Rabbit.prototype.sit = function() {
console.log(this.name + ' sits in a rabbity way.');
};
rabbit.sit(); // 使用覆寫後的 method
Rabbit.prototype.sit = function() {
console.log('calling superclass sit:');
Animal.prototype.sit.apply(this, arguments); // 使用尚未被覆寫的 method
};
rabbit.sit();
備註:All parent methods are called with apply/call to pass current object as this. A simple call Animal.prototype.sit() would use Animal.prototype as this.
我們也可以直接在 rabbit 這個物件上做覆寫的動作。
rabbit.sit = function() {
alert('A special sit of this very rabbit ' + this.name);
};
Sugar: removing direct reference to parent
因為在重構的時候可能修改了 parent,所以必須移除對 parent constructor 的參考。
function extend(Child, Parent) {
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
}
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
function Animal(name) {
this.name = name;
}
Animal.prototype = {
canWalk: true,
sit: function() {
this.canWalk = false;
console.log(this.name + ' sits down.');
},
run: function() {
this.canWalk = false;
console.log(this.name + ' run fast.');
},
};
function Rabbit(name) {
this.name = name;
Rabbit.parent.constructor.apply(this, arguments); // super constructor
}
extend(Rabbit, Animal);
Rabbit.prototype.run = function() {
Rabbit.parent.run.apply(this, arguments); // parent method
};
var rabbit = new Rabbit('John');
console.log(rabbit.canWalk); // true
rabbit.sit(); // John sits down.
console.log(rabbit.canWalk); // false
rabbit.run(); // John run fast.
因此,之後對 Animal 的修改都只需要回頭修正 Animal 和 extend 即可。
Private/protected methods(encapsulation)
JavaScript 中的 private / protected method 並沒有實質上的支援,可參考 Private/protected methods (encapsulation)。
Static methods and properties
static property / method 是被直接設定給建構子的。
function Animal() {
Animal.count++;
}
Animal.count = 0;
new Animal();
new Animal();
console.log(Animal.count); // 2
Summary
總結以上,我們得到完整的 Pseudo-Classical Pattern 程式碼。
function extend(Child, Parent) {
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
}
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
// --------- the base object ------------
function Animal(name) {
this.name = name;
}
// methods
Animal.prototype.run = function() {
console.log(this + ' is running!');
};
Animal.prototype.toString = function() {
return this.name;
};
// --------- the child object -----------
function Rabbit(name) {
Rabbit.parent.constructor.apply(this, arguments);
}
// inherit
extend(Rabbit, Animal);
// override
Rabbit.prototype.run = function() {
Rabbit.parent.run.apply(this);
console.log(this + ' bounces high into the sky!');
};
var rabbit = new Rabbit('Jumper');
rabbit.run();
References
這篇文章的原始位置在這裡-JavaScript Object Oriented Programming - Pseudo-Classical Pattern
由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。