4- Private & public Hầu hết các ngôn ngữ lập trình hướng đối tượng quen thuộc như Java hay C# đều hỗ trợ từ khóa public và private cho các thuộc tính cũng như phương thức trong class. Với JavaScript chúng ta cũng có thể tạo ra các pattern để giả lập...
4-
Private & public
Hầu
hết các ngôn ngữ lập trình hướng đối
tượng quen thuộc như Java hay C# đều hỗ trợ từ khóa
public và private cho các thuộc tính cũng như
phương thức trong class. Với JavaScript chúng ta cũng
có thể tạo ra các pattern để giả lập hai từ
khóa này trong các class (cũng giả lập
luôn!!):
Hoặc
chúng ta có thể xây đựng một cách
đơn giản theo các Pattern dưới đây:
//Public:
function
Constructor(...)
{
this.membername
= value;
}
Constructor.prototype.membername
= value;
//Private:
function
Constructor(...)
{
var
that = this;
var membername
= value;
function
membername(...)
{...}
}
//chú
ý
function
membername(...)
{...}
//có
thể thay thế cho
var
membername
= function membername(...)
{...};
//Privileged
function
Constructor(...)
{
this.membername
= function (...)
{...};
}
5-
Kế thừa với prototype
Nếu
như các bạn đã từng tìm hiểu về kế
thừa với JavaScript thì hẳn các bạn nhận ra
rằng có khá nhiều cách để cài đặt,
tuy nhiên trong bài viết này tôi sẽ chỉ
giới thiệu một cách mà theo cá nhân
là khá đơn giản và tiện lợi khi triển
khai, đó là sử dụng prototype và
constructor.
Trước
hết, giả sử chúng ta có hai class ParentClass và
ChildClass. Để thực hiện cho ChildClass kế thừa
ParentClass ta lần lượt các bước sau:
Như
vậy với hai bước đơn giản ta đã thực hiện được
kế thừa trong JavaScript. Tuy nhiên còn khá
nhiều vấn đề nảy sinh trong khi bạn xây dựng các
class phức tạp, ví dụ như làm thế nào
để gọi phương thức của ParentClass trong khi ChildClass đã
overridden nó, hay vấn đề về virtual class tức là
class chỉ có thể kế thừa mà không cho
phép tạo một thể hiện cho nó. Chúng ta
sẽ lần lượt giải quyết vấn đề này tiếp sau
đây.
Nhưng
trước khi đi vào các vấn đề đã nêu
ta hãy làm một ví dụ thú vị sau:
//Class
Động vật có vú
function
Mammal(name){
this.name=name;
this.offspring=[];//Mùa
sinh sản!!!
}
//Phương
thức sinh con (hoặc có con)
Mammal.prototype.haveABaby=function(){
var
newBaby=new Mammal("Baby "+this.name);
this.offspring.push(newBaby);
return
newBaby;
}
//Hàm
trả về tên con vật
Mammal.prototype.toString=function(){
return
'[Mammal "'+this.name+'"]';
}
//Class
Mèo kế thừa Class Động vật có vú
Cat.prototype
= new Mammal();
Cat.prototype.constructor=Cat;
//Constructor
của class Cat
function
Cat(name){
this.name=name;
}
//Hàm
trả về tên con vật
Cat.prototype.toString=function(){
return
'[Cat "'+this.name+'"]';
}
//Tạo
Mr.Bill !!!
var
someAnimal = new Mammal('Mr. Bill');
//Tạo
mèo Tom
var
myPet = new Cat('Tom');
alert('someAnimal
is '+someAnimal);
//Trả về 'someAnimal is [Mammal "Mr. Bill"]'
alert('myPet
is '+myPet);
//Trả về 'myPet is [Cat "Tom"]'
//Cho
mèo sinh con (kế thừa từ Mammal)
myPet.haveABaby();
//Thông
báo về số con của mèo Tom
alert(myPet.offspring.length);
alert(myPet.offspring[0]);
//Trả về [Mammal "Baby Tom"]'
*Vấn
đề Super & Sub Class
Hãy
thử mở rộng ví dụ trên để ta có dịp
minh họa cách mà một class con gọi đến một
phương thức của class cha trong khi nó đã được
overridden. Ta sẽ muốn rằng ngay sau khi mèo con được
sinh ra nó sẽ kêu một tiếng "meeoo!"
chẳng hạn. Để làm được điều này ta sẽ
viết một hàm haveBaby của riêng class Cat trong đó
sẽ gọi lại hàm haveBaby trong class Mammal:
Cat.prototype.haveABaby=function(){
Mammal.prototype.haveABaby.call(this);
alert("mew!");
}
Ở
đây, các bạn hãy nhớ lại cách thức
sử dụng phương thức call của đối tượng Function mà
ta đã từng đề cập. Như vậy với việc sử dụng
call() ta hoàn toàn có thể làm được
giống như phương thức "super()" trong Java và
các ngôn ngữ khác.
Do
vậy, từ bây giờ để cho tiện thì tại sao
chúng ta không cài đặt luôn một
"super" cho class của chúng ta. Làm điều
đó không mấy phiền hà như sau:
Cat.prototype=newMammal();
Cat.prototype.constructor=Cat;
Cat.prototype.parent=Mammal.prototype;//"super"
...
Cat.prototype.haveABaby=function(){
var
theKitten = this.parent.haveABaby.call(this);
//"super(this)"
alert("mew!");
return
theKitten;
}
//Các
bạn sẽ có thắc mắc nhỏ là tại sao không
dùng từ super thay cho parent? Lí do là vì
hình như JavaScript có ý định sài từ
này trong các phiên bản tương lai thì
phải! Một điều nữa nếu bạn băn khoăn là từ
parent đã được DOM sử dụng khi truy cập đến các
node, điều này thì cứ vô tư đi vì
đây là JavaScript mà!!!:D
*Vấn
đề virtual Class
Một số ngôn ngữ
lập trình hướng đối tượng có giải quyêt
vấn đề về virtual class, tức là một class không
thể có một thể hiện của chính nó,
nhưng có thể kế thừa từ nó. Như trong ví
dụ trên, ta muốn thêm vào một class
LivingThing mà Mammal sẽ kế thừa từ nó, nhưng
ta không muốn ai đó lại có thể tạo ra
một LivingThing không mong muốn (chẳng hạn
LivingStone!!:P). Với JavaScript ta có thể thực hiện
điều này bằng cách thay thế function bằng một
object cho virtual class.
//Khai báo
class kiểu JSON
LivingThing={
beBorn
: function(){
this.alive=true;
}
}
//...
Mammal.prototype =
LivingThing;
Mammal.prototype.parent
= LivingThing;
//Để ý
rằng không phải là 'LivingThing.prototype'
Mammal.prototype.haveABaby=function(){
this.parent.beBorn.call(this);
var
newBaby=new this.constructor("Baby "+this.name);
this.offspring.push(newBaby);
return newBaby;
}
Như
vậy nếu một ai đó khai báo như sau:
var
stone= new LivingThing(); // Sẽ gây lỗi
Bởi
vì LivingThing bây giờ không phải là
kiểu function mà có kiểu là object, do đó
không thể coi nó như là một constructor với
từ khóa new.
Như
các bạn đã thấy, với cách cài đặt
kế thừa trên ta luôn phải thực hiện hai dòng
lệnh bắt buộc mỗi khi thực hiện kế thừa. Để cho
tiện lợi, ta có thể mở rộng khả năng này
cho bản thân object Function trong JavaScript, và coi
đó như là một thuộc tính vốn có
của BLOCKED SCRIPT
Function.prototype.inheritsFrom=function(parentClsOrObj){
if
(parentClsOrObj.constructor == Function ){
//Normal
Inheritance
this.prototype = new parentClsOrObj;
this.prototype.constructor = this;
this.prototype.parent=parentClsOrObj.prototype;
}
else
{
//Pure Virtual Inheritance
this.prototype = parentClsOrObj;
this.prototype.constructor
= this;
this.prototype.parent = parentClsOrObj;
}
return this;
}
//
//
LivingThing = {
beBorn : function(){
this.alive = true;
}
}
//
//
function Mammal(name){
this.name=name;
this.offspring=[];
}
Mammal.inheritsFrom(
LivingThing );
Mammal.prototype.haveABaby=function(){
this.parent.beBorn.call(this);
var newBaby=new
this.constructor( "Baby "+this.name);
this.offspring.push(newBaby);
return newBaby;
}
//
//
function Cat( name ){
this.name=name;
}
Cat.inheritsFrom(
Mammal );
Cat.prototype.haveABaby=function(){
var
theKitten = this.parent.haveABaby.call(this);
alert("mew!");
return theKitten;
}
Cat.prototype.toString=function(){
return '[Cat "'+this.name+'"]';
}
//
//
var tom = new Cat( "Tom" );
var kitten =
tom.haveABaby( ); // mew!
alert( kitten ); //
[Cat "Baby Tom"]
////////////////////////////////////////////////////////
Phù!!!Giá
như JavaScript hỗ trợ tốt OOP thì đỡ biết mấy!