Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: JavaScript Advanced - Hướng đối tượng với JS 2510

advanced js, oop js
phananhvu.myopenid.com 125
Updated over 4 years ago
Tác giả Sergio Pereira.
Sửa lần cuối: February 21st 2006.

Này, thế mà không biết đấy

Nếu bạn cũng là một web developer thì hẳn bạn cũng dùng Javascript khá nhiều, nhất là trong việc tạo giao diện cho web site.

Đến gần đây tôi mới biết JS hướng đối tượng hơn tôi nghĩ, nhưng tôi đã nghĩ rằng mình không cần những thứ đó. Cho đến khi các trình duyệt hỗ trợ những thuộc tính chuẩn của JS và DOM tốt hơn thì việc viết các đoạn code phức tạp hơn cho client trở nên khả dĩ hơn bao giờ hết. Chính điều đó đã góp phần tạo nên cái gọi là "hiện tượng AJAX".

Lúc bắt đầu viết được những ứng dụng AJAX thú vị cũng là lúc chúng ta nhận ra rằng những gì ta đã biết về JS chỉ là phần nổi của tảng băng. Chúng ta có bây giờ là hơn cả những đoạn code UI, input validation hay những hàm thực hiện những tác vụ "vô tích sự". Client code giờ đây cao cấp và phân lớp, không khác gì những ứng dụng desktop. Chúng ta đã có class libraries, object models, hierarchies, patterns, và rất nhiều những thứ "xa xỉ" khác mà trước kia chỉ thấy ở server side.

Thế là đột nhiên chúng ta cần nhiều kĩ năng hơn trước kia rất nhiều để có thể viết ứng dụng web. Nếu bạn dùng một thư viện JS nào đó như Prototype.js, Scriptaculous, moo.fx, Behaviour, YUI, ... thì cuối cùng bạn cũng vẫn phải đọc JS code để hiểu và vận dụng chúng tốt hơn. Lúc đó, bạn sẽ thấy người ta dùng các chiêu thức lạ hoắc và ghê gớm mà bạn chưa từng biết bao giờ.

Bài này xin được giới thiệu và giải thích các chiêu thức đó.

Các bài liên quan

Prototype.js documentation, Mẹo vặt JavaSccript, JavaScript in advanced 2 .

JSON

Một cách ngắn gọn, JavaScript Object Notation (JSON,) là cách khai báo object trong JS. Xem ví dụ sau và để ý xem JSON đơn giản thế nào.

var myPet = { color: 'black', leg_count: 4, communicate: function(repeatCount){ 
for(i=0;i<repeatCount;i++) alert('Woof!');} };

Chỉnh lại chút xíu cho dễ nhìn:

var myPet = {
color: 'black',
legCount: 4,
communicate: function(repeatCount){
for(i=0;i<repeatCount;i++)
alert('Woof!');
}
};

Ở đây, chúng ta tạo ra tham chiếu của một đối tượng có 2 thuộc tính (color and legCount) và một phương thức (communicate). Không khó để nhận thấy rằng thuộc ính và phương thức được khai báo thành một danh sách, ngăn cách nhau mới dâu phẩy. Mỗi thuộc tính/phương thức được khai báo tên, tiếp theo là dấu hai chấm và giá trị thuộc tính/định nghĩa phương thức. Sau khi tạo ra một đối tượng như thế và tham chiếu tới biến myPet, ta có thể sử dụng như thế này:

alert('my pet is ' + myPet.color);
alert('my pet has ' + myPet.legCount + ' legs');
//if you are a dog, bark three times:
myPet.communicate(3);

Bạn có thể thấy JSON được sử dụng khá nhiều trong JS để làm tham số cho hàm, làm giá trị trả về hay làm đáp ứng từ server cho client, ...

Cái gì? Hàm cũng là đối tượng à?

Có thể bạn thấy lạ nhưng trong JS, hàm cũng là đối tượng. Bạn cũng có thể dùng hàm như một tham số để truyền vào function y như bạn vẫn hay truyền chuỗi kí tự trước đây. Rât tiện phải không.

Xem thử ví dụ này. Chúng ta truyền các hàm vào một hàm khác:

var myDog = {
bark: function(){
alert('Woof!');
}
};

var myCat = {
meow: function(){
alert('I am a lazy cat. I will not meow for you.');
}
};

function annoyThePet(petFunction){
//let's see what the pet can do
petFunction();
}

//annoy the dog:
annoyThePet(myDog.bark);
//annoy the cat:
annoyThePet(myCat.meow);

Để ý rằng chúng ta truyền myDog.bark và myCat.meow mà không có "()". Vì nếu thêm dấu "()" có nghĩa là chúng ta muốn thực hiện hàm và lấy giá trị trả về để truyền vào hàm annoyThePet.

Nếu bạn muốn con mèo của bạn sủa thì chỉ cần làm thế này:

myCat.meow = myDog.bark;
myCat.meow(); //alerts 'Woof!'

Mảng, phần tử, và object members

Trong JS, hai dòng sau đây làm cùng một việc:

var a = new Array();
var b = [];

Chắc chắn bạn cũng biết truy cập các phần tử của mảng bằng cách dùng dấu "[]" như thế này:

var a = ['first', 'second', 'third'];
var v1 = a[0];
var v2 = a[1];
var v3 = a[2];

Nhưng trong JS, ngoài cách dùng chỉ số, bạn cũng có thể dùng chuỗi để xác định phần tử của mảng. Xem ví dụ sau:

var obj = {}; //new, empty object
obj['member_1'] = 'this is the member value';
obj['flag_2'] = false;
obj['some_function'] = function(){ /* do something */};

Ví dụ trên cho kết quả giống hệt ví dụ sau:

var obj = {
member_1:'this is the member value',
flag_2: false,
some_function: function(){ /* do something */}
};

Đối tượng, mảng và bảng băm (hash) trong JS na ná nhau. Hai lệnh sau cũng cho kết quả giống nhau:

obj.some_function();
obj['some_function']();

Đối tượng nhiều quá. Có class không?

Giá trị của OOP nằm ở việc sử dụng class.

//defining a new class called Pet
var Pet = function(petName, age){
this.name = petName;
this.age = age;
};

//let's create an object of the Pet class
var famousDog = new Pet('Santa\'s Little Helper', 15);
alert('This pet is called ' + famousDog.name);

Hãy xem chúng ta thêm phương thức cho lớp Pet như thế nào. Chúng ta sẽ sử dụng thuộc tính prototype mà tất cả các lớp đều có. Thuộc tính prototype là một object chức tất cả các thuộc tính/phương thức mà đối tượng của một lớp có thể có. Ngay cả với các lớp mặc định của JS như String, Number, và Date cũng có một đối tượng prototype mà chúng ta có thể thêm phương thức hay thuộc tính vào đó để tạo ra các đối tượng mới có các thuộc tính/phương thức mới.

Pet.prototype.communicate = function(){ 
alert('I do not know what I should say, but my name is ' + this.name);
};

Đó chính là lúc những thư viện như prototype.js ra tay. Nếu dùng prototype.js, chúng ta có thể viết lại đoạn code trên sạch sẽ hơn (ít ra là tôi nghĩ thế).

var Pet = Class.create();
Pet.prototype = {
//our 'constructor'
initialize: function(petName, age){
this.name = petName;
this.age = age;
},

communicate: function(){
alert('I do not know what I should say, but my name is ' + this.name);
}
};

Dùng hàm làm tham số, một pattern thú vị

Nếu bạn chưa từng làm việc với các ngôn ngữ lập trình có hỗ trợ closures, như Ruby hay C#2.0, có thể bạn sẽ thấy đoạn code sau rất kinh:

var myArray = ['first', 'second', 'third'];
myArray.each( function(item, index){
alert('The item in the position #' + index + ' is:' + item);
});

Úi dời! Để giải thích chút đã nhé trước khi bạn bảo tôi đi quá xa, sang một bài viết mới.

Đầu tiên, trong ví dụ trên, chúng a sử dụng thư viện prototype.js. Thư viện này tạo thêm phương thức each cho lớp Array. Phương thức each nhận một tham số là một hàm. Hàm này sẽ được gọi ứng với mỗi phần tử của mảng, nhận vào hai giá trị là phần tử và chỉ số của phần tử đó. Chúng ta gọi hàm này là iterator function. Chúng ta cũng có thể viết lại đoạn chương trình trên như sau:

function myIterator(item, index){
alert('The item in the position #' + index + ' is:' + item);
}

var myArray = ['first', 'second', 'third'];
myArray.each( myIterator );

Cách sau có vẻ hay hơn nhưng nó lại bắt ta quay trở lại để đọc xem hàm myIterator là hàm như thế nào. Thế thì hơi phiền nên viết luôn như cách trên cũng tốt.

 

"this" là "this" nhưng đôi khi "this" lại là "that"

Một trong những điều oái oăm nhất khi viết JS là khi ta sử dụng từ khóa "this".

Như đã nói ở trên, một hàm cũng là một đối tượng, và đôi khi chúng ta không để ý rằng chúng ta đang truyền một hàm đi lung tung.

Xem đoạn ví dụ sau:

function buttonClicked(){
alert('button ' + this.id + ' was clicked');
}

var myButton = document.getElementById('someButtonID');
var myButton2 = document.getElementById('someOtherButtonID');
myButton.onclick = buttonClicked;
myButton2.onclick = buttonClicked;

Vì hàm buttonClicked được định nghĩa bên ngoài đối tượng nên chúng ta có có xu hướng nghĩ là "this" tham chiếu đến một của sổ hay một document object nào đó(giả sử đoạn code này nằm trong một trang HTML được xem trên trình duyệt).

Khi chạy đoạn mã trên, chúng ta thấy rằng nó chạy đúng như mong đợi và thông báo id của button được click. Ở đây, chúng ta đã gán hàm onclick của mỗi button tới tham chiếu của đối tượng buttonClicked. Do đó, khi button được click, trình duyệt sẽ thực thi một thứ giống như lệnh sau:

myButton.onclick();

Cũng không lằng nhằng lắm phải không? Nhưng hãy thử xem điều gì xảy ra khi bạn xử lí các sự kiện của các đối tượng khác.

var myHelper = {

formFields: [ ],

emptyAllFields: function(){
for(i=0; i<this.formFields.length; i++){
var elementID = this.formFields[i];
var field = document.getElementById(elementID);
field.value = '';
}
}
};

//tell which form fields we want to work with
myHelper.formFields.push('txtName');
myHelper.formFields.push('txtEmail');
myHelper.formFields.push('txtAddress');

//clearing the text boxes:
myHelper.emptyAllFields();

var clearButton = document.getElementById('btnClear');
clearButton.onclick = myHelper.emptyAllFields;

Click vào Clear button thì 3 text box sẽ được xóa hết chăng? Thế mà khi click vào Clear button, bạn lại chỉ nhận được một thông báo lỗi runtime. Lỗi này liên quan tới (gì nhỉ?) từ khóa "this". Vấn đề nằm ở chỗ this.formFields chưa được định nghĩa khi "this" trỏ tới một button, đó chính xác là những gì diễn ra. Một giải pháp đơn giản là viết lại dòng cuối như sau:

clearButton.onclick = function(){ myHelper.emptyAllFields(); };

 

 

Comments

phananhvu.myopenid.com 125
over 4 years ago

Nên ghi cả vào mục lục của box Client side nữa.

Xong! Thế là đã có một series thú vị về JS. Bà con xem phần mục lục box Client side nhá :-)

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content