FPT-Aptech  »  Kiến thức  »  Lập trình

JavaScript và lập trình hướng đối tượng - Phần 3

Cập nhật: 18/8/2008 02:28 PM với no comments
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!!):

  • Private variables: những biến được khai báo với từ khóa var bên trong object, chỉ có thể được truy nhập bởi các hàm riêng (private functions) hoặc các phương thức đặc quyền(privileged methods) của object.

  • Private functions: những hàm được khai báo kiểu inline hoặc khai báo kiểu var functionName=function(){...} và được truy cập bởi các phương thức đặc quyền bên trong object.

  • Privileged methods: những hàm được khai báo kiểu this.methodName=function(){...} bên trong object. Những hàm này có thể được gọi bên ngoài tới object.

  • Public property: là những thuộc tính được khai báo kiểu this.propertyName, những thuộc tính này có thể thay đổi từ bên ngoài của object.

  • Public method: là những phương thức được khai báo kiểu ClassName.prototype.methodName=function(){...}, những phương thức này có thể được gọi từ bên ngoài object.

  • Static property: những thuộc tính được khai báo kiểu ClassName.propertyName=someValue.

  • Prototype property: những thuộc tính được khai báo kiểu ClassName.prototype.propertyName=someValue.

    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:

  • Cho prototype của ChildClass là một thể hiện của ParentClass:ChildClass.prototype=new ParentClass();

  • Cài đặt lại thuộc tính constructor cho ChildClass: ChildClass.prototype.constructor=ChildClass;

    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!

Xếp trong: ,
0 comment(s) Add comment Send Print this

Không có lời bình

Bài gần đây

Tầng 3, 51 Lê Đại Hành (Gần Đại học Bách Khoa Hà Nội)
quận Hai Bà Trưng, Hà Nội
Tel: +84 (4) 3.974.7480
Tầng 4, Cung văn hóa Hữu Nghị, 1A Yết Kiêu, Hà nội
Tel: +84 (4) 3.822.4880 Fax: +84 (4) 3.557.5593
Tầng 2, nhà D, Làng Sinh viên Hacinco, Thanh Xuân, Hà nội
Tel: +84 (4) 3.557.5595 Fax: +84 (4) 3.557.5593
Copyright © 2007-2008 FPT - Aptech Computer Education . Powered by Commnity ServerGiới thiệu  |  Liên hệ ( Xem tốt nhất với IE 7.0 & FF 2.0 ở 1024x768 pixels )