Du lịch trong dải ngân hà Javascript

Như bạn đã biết, jQuery là một thế giới rộng lớn và lung linh, mang lại cho lập trình viên nhiều trải nghiệm thú vị, và cũng khiến họ bị mê hoặc và gần như nghĩ rằng: jQuery là nơi duy nhất mang lại sự sống cho các trang web. Người ta thường nói rằng: đi một ngày đàng học một sàng khôn. Điều này khiến bạn phải suy nghĩ một chút nếu cứ quanh quẩn trong cái thế giới của jQuery, dải ngân hà JavaScript còn chứa đựng vô số những hành tinh khác cùng nhiều điều kì lạ và bất ngờ. Trong bài viết này, với vai trò của một hướng dẫn viên du lịch “không gian” tôi xin giới thiệu các bạn đến với những thế giới khác, những vùng đất mới, và hy vọng rằng sau mỗi chuyến đi và những trải nghiệm thu được, các bạn sẽ có thêm niềm tin, sức mạnh và sự đam mê trong cuộc hành trình bất tận của mình.

Sau đây là những địa điểm du lịch mà tôi cảm thấy thật sự ấn tượng mỗi lần đi qua bởi vẻ đẹp và sự cuốn hút rất riêng của chúng:

1. Underscore.js: Nếu như “$” là dấu hiệu phổ biến trong jQuery thì “_” lại làm nên thương hiệu cho Underscore đúng như tên của thư viện này. Underscore tạo ra một thế giới của riêng mình, nó bổ sung thêm cho những đối tượng trong javascript những tính năng mạnh mẽ vốn là ưu điểm của các ngôn ngữ kịch bản khác như Ruby, Groovy, v.v.. Công nghệ tiên tiến đến từ Underscore khiến cho những thao tác trên mảng, tập hợp trở nên đơn giản và thuận tiện; những đối tượng và hàm sẽ được bổ sung thêm những tính năng mà bạn ước gì nó được build-in trong javascript.

2. Backbone.js: Thế giới của Backbone bao gồm ba “chủng tộc” chính là Model, View và Controller (viết tắt là MVC). Công nghệ của Backbone là sự kết hợp hài hòa giữa các yếu tố của  model, view và controller, tạo ra khả năng phát triển các ứng dụng phức tạp mà không mất quá nhiều công sức. Nếu bạn đang sở hữu những dịch vụ theo phong cách RESTful hoặc dựa trên JSON và mong muốn xây dựng một hệ thống ứng dụng đầu cuối theo single-page model thì Backbone là lựa chọn số một. Danh sách các khách hàng sử dụng công nghệ của Backbone có thể liệt kê ra đây một số cái tên, đủ để thấy tầm ứng dụng của nó:
– DocumentClound
– Linkedin Mobile
– WordPress.com
– Foursquare
– Khan Academy
– Groupon Now
– v.v.

3. Less.js: Nếu bạn là người đang ngập ngụa trong thế giới của CSS thì khi bước vào Less.js bạn sẽ có cảm giác như mình được sinh ra lần thứ hai. Less.js tạo ra một phiên bản CSS mới tinh mà CSS nguyên bản không có được đó là khả năng tham số hóa các thông số CSS, điều này đặc biệt hữu ích nếu bạn muốn có những giao diện linh hoạt và tương tác (một xu hướng thiết kế web đang rất nổi hiện nay: Responsive Web Design).

4. Processing.js: Nếu bạn chưa từng một lần nghe đến ngôn ngữ lập trình Processing thì tôi khuyên bạn hãy thử dạo qua nó một chút, và tôi tin rằng bạn sẽ bị cuốn hút ngay từ cái nhìn đầu tiên bởi sự đơn giản, mạnh mẽ và đặc biệt hiệu quả của nó trong việc xử lý và lập trình đồ họa. Với Processing.js, sức mạnh của Processing đã được chuyền sang cho Javascript. Ứng dụng web của bạn giờ đây không còn khô khan bó buộc bởi các quy tắc viết thẻ HTML và CSS, bạn có thể thỏa sức sáng tạo ra những thứ của riêng mình trong một không gian riêng của thẻ Canvas trong HTML5 theo một cách hết sức đơn giản và trực quan theo lối tiếp cận của Processing. Tôi tin với Processing.js sẽ có nhiều ý tưởng tuyệt vời bắt đầu từ đây.

Có lẽ chúng ta tạm dừng chân một chút để nghỉ ngơi và thả tầm mắt của mình chiêm ngưỡng quang cảnh hùng vĩ ở phía xa của giải ngân hà Javascript… Nào cùng đếm nhé:

5.6.7.8.9….. : Modernizr, JsTestDriver, Respond.js, jStat, Highcharts, jscharts, Treesaver.js, Popcorn.js, handlebars.js, mootools, YUI, DOJO, Blackbird, $fx(), C3DL, box2d.js, taffydb, Shortcuts.js , jsUnit v.v…..

Vâng, vũ trụ quả là bao la mà cuộc đời thì thật ngắn ngủi, chúng ta sẽ không đủ thời gian để du lịch và lang thang vô định như vậy, nhưng có một điều chắc chắn: hãy dừng lại ở thế giới do chính bạn tạo ra:

…N. Your_script.js 🙂

Advertisements

Hạt giống tri thức

Người ta nói rằng cuộc sống như một mảnh đất nuôi dưỡng cho hạt giống trong tâm hồn mỗi người trưởng thành hơn. Và ta gọi đó là hạt giống tâm hồn. Một hình ảnh ẩn dụ thú vị đầy chất thơ.

planting-seeds

Cũng thật tình cờ khi tôi bắt gặp dòng tâm sự của một cô bạn thời cấp 3 (nay đã theo chồng sang Mỹ định cư) trên facebook kể về chuyện những đứa trẻ mầm non ở Mỹ được dạy cách gieo một hạt giống vào đất, hàng ngày tự tay chúng chăm sóc và đến khi lên cây thì được mang về tặng mẹ, lên 5 tuổi chúng được vẽ và giải thích về quá trình gieo hạt và mọc thành vườn cây. Điều này thực sự gây ấn tượng bởi lẽ cái hạt giống kia không còn chỉ là một hình ảnh ẩn dụ với chất thơ nữa mà là những trải nghiệm thực sự sâu sắc trong cuộc đời của một đứa trẻ. Những đứa trẻ đã biết gieo hạt giống để tạo nên vườn cây, nhưng những người thầy đã gieo vào chúng một hạt giống khác mà kết quả không chỉ là những vườn cây.

seed

Lại nhớ, lần gần đây tôi tham dự buổi nói chuyện của nhà giáo Phạm Toàn, ông có nhắc đến một điều rằng cái khó nhất trong giáo dục ấy là tìm cho ra được cái hạt giống ấy, và nhóm Cánh Buồm của ông đã tìm được. Một khi hạt giống được gieo, chúng sẽ nảy nở, đơm hoa và kết trái, những trái ngon, quả ngọt trong tri thức của người học. Một nền giáo dục chỉ chăm đầu tư vào “máy cày, máy kéo, phân bón v.v…” mà không có được những hạt giống tốt thì quả thật… tai hại vô cùng, xã hội lại phải mất công đi dọn cỏ dại mà thôi.

Nói qua rồi lại nói lại, hạt giống tốt, cần có môi trường và cách chăm sóc tốt để nảy nở. Nhưng tôi thiết nghĩ cái môi trường ở đây không chỉ là môi trường xã hội, nhà trường, lớp học hay gia đình mà chính là bản thân người học. Nếu người học không biết cách chăm sóc cho những hạt giống của chính mình thì mọi nỗ lực khác chẳng hóa vô ích sao? Ngay cả những hạt giống ấy là tốt và người khác gieo cho thì rốt cuộc tự mình phải tự chăm sóc lấy nó thôi.

Cái cách chăm sóc ở đây phải chăng là cách học, cách tự học?

mustard-tree_mist

JavaScript và lập trình hướng đối tượng [2]

Trong bài trước chúng ta đã tìm hiểu những cách thức cơ bản để tạo các đối tượng trong JavaScript. Tuy nhiên một vấn đề rắc rối cho những cách thức đó là sự phiền hà khi phải làm việc với một lô các đối tượng tương tự nhau về phương thức cũng như thuộc tính. Tạo đối tượng kiểu như vậy có thể so sánh với công việc của những người thợ làm gốm thủ công, họ nặn ra vô số cái bát, cái đĩa, các con giống nhưng rõ ràng là các sản phẩm đó thật khó có sự đồng nhất về các đặc điểm mặc dù cùng là bát hay đĩa v.v… Để giải quyết vấn đề này những người thợ làm gốm đã phát minh ra cái gọi là khuôn. Sử dụng khuôn có thể tạo ra vô số các đối tượng với những đặc tính giống hệt nhau mà không phải bận tâm để làm chúng có giống nhau hay không bởi tất cả chúng đều sinh ra từ một khuôn. Với lập trình hướng đối tượng, chúng ta có khái niệm Class (Lớp) bao gồm các đặc tả về các đối tượng như thuộc tính và phương thức. Việc mô tả lớp có thể ví như công việc tạo khuôn, sau khi đã có khuôn chi tiết về đối tượng cần tạo ta chỉ việc đưa vào quy trình đề “sản xuất” các đối tượng từ khuôn đó. JavaScript cung cấp một cách thức tạo khuôn khá thú vị đó là Prototype.

Class và prototype 

Xây dựng class trong JavaScript

Như đã nói, trong lập trình hướng đối tượng, chúng ta thường tạo những đối tượng từ các lớp (class), trong đó các object được coi như là thể hiện của một lớp. Cả hai ngôn ngữ Java và JavaScript đều cung cấp cho chúng ta từ khóa new dùng để tạo một thể hiện của một đối tượng đã được định nghĩa trước đó.

Trong Java, mọi thứ đều là object, và được phát triển từ class nguyên thủy là java.lang.Object. Nếu bạn là lập trình viên Java hẳn sẽ thấy câu lệnh sau khá quen thuộc:

MyObject myObj=new MyObject(arg1,arg2);

Với JavaScript cũng có cách khai báo rất giống như vậy:

var myObj= new MyObject(arg1, arg2);

Nhưng có một sự khác biệt lớn về bản chất đó là chúng ta hoàn toàn không định nghĩa lớp MyObject như trong Java mà thay vào đó là một hàm thật sự có cùng tên:

function MyObject(){
    //do something here
}

Để minh họa cho điều này, ta xét ví dụ sau khai báo một class Animal với các thuộc tính đơn giản như name, food và phương thức đơn giản là eat:

function Animal(name, food){
    //Thuộc tính
    this.name=name;//Tên con vật, hoặc tên loài
    this.food=food;//Loại thức ăn có thể ăn

    //Phương thức
    this.eat=function(something){
        if(something===food){//Nếu thức ăn phù hợp
            alert('This is very delicious');
        }else{
            alert("I don't like this");
        }
    }
}

var myAnimal = new Animal("Cat","Mouse");
myAnimal.eat("Mouse");// This is very delicious

Chạy thử
Trong ví dụ trên, một lần nữa ta sử dụng từ khóa this, với cấu trúc trên thì ta có thể hiểu this là đối tượng được tạo ra sau từ khóa new.

Tất nhiên với cách khai báo như vậy mọi việc đều OK, nhưng nếu đi sâu hơn một chút chúng ta sẽ thấy: đầu tiên, cho mỗi thể hiện của class Animal mà chúng ta tạo ra, chúng ta cũng tạo ra một hàm eat() mới, điều này dẫn đến vấn đề về bộ nhớ và tốc độ xử lý nếu bạn có ý định tạo ra rất nhiều đối tượng trong chương trình từ class Animal (ví dụ tạo một đàn kiến chẳng hạn!!!); thứ nữa, khi làm việc với các DOM (Document Object Model), chúng ta sẽ gặp phải vô số vấn đề rắc rối không mong đợi. Chính vì vậy bây giờ ta hãy thử một cách khác an toàn hơn, và nó được biết đến cái tên khá lạ tai: prototype-base.

Prototype

Prototype là gì vậy? Thực ra đây là một thuộc tính sẵn có trong mọi object của JavaScript (trong JavaScrip thuộc tính cũng có thể là một đối tượng, ta sẽ đề cập chi tiết hơn về điều này sau), đây có thể coi là nét đặc trưng của JavaScript mà các ngôn ngữ hướng đối tượng khác không có. Các hàm cũng như các thuộc tính đều có thể kết hợp được với prototype. Prototype và từ khóa new có thể làm việc cùng nhau, khi một đối tượng được tạo bởi từ khóa new thì tất cả các thuộc tính và phương thức prototype đều được gắn vào đối tượng mới đó. Điều này nghe có vẻ lạ và rắc rối, nhưng khá là hiệu quả:

Ta xây dựng lại class Animal theo phong cách prototype như sau:

function Animal(name, food){
    //Thuộc tính
    this.name=name;
    //Tên con vật, hoặc tên loài
    this.food=food;
    //Loại thức ăn có thể ăn
}

Animal.prototype.eat=function(something){
    if(something===this.food){//Nếu thức ăn phù hợp
        alert('This is very delicious');
    }else{
        alert("I don't like this");
    }
}

var myAnimal = new Animal("Cat","Mouse"); 
myAnimal.eat("Mouse");// This is very delicious

Như vậy đầu tiên chúng ta vẫn khai báo class với các thuộc tính bình thường, sau đó đối với các phương thức ta chỉ việc gắn nó vào prototype như trên. Khi chúng ta tạo một thể hiện của class thì phương thức được gắn vào object đó mà không phải tạo mới, và từ khóa this sẽ đảm bảo rằng trường food là của object vừa tạo.

Trong việc thao tác với prototype, có một lưu ý là mọi object được tạo ra sau prototype thì sẽ được gắn các thuộc tính hoặc phương thức được khai báo kiểu prototype trước đó, ví dụ:

myObject.prototype.color="red";

var myObj1=new myObject();

myObject.prototype.color="blue";

myObject.prototype.sound="boom!";

var myObj2=new myObject();

alert(myObj1.color); //red

alert(myObj2.color); //blue

alert(myObj2.sound); //boom!

alert(myObj1.sound); //error!

Khi đó myObj1 chỉ có thuộc tính color với giá trị là red, còn myObj2 có thêm thuộc tính sound và giá trị color là blue

* Mở rộng một object trong JavaScript

Trên thực tế, cơ chế prototype còn có thể áp dụng cho các đối tượng xây dựng sẵn trong JavaScript, ta có thể dùng nó để mở rộng các đối tượng này. Một ví dụ rất hữu ích là mở rộng đối tượng Array như sau:

Array.prototype.indexof=function(obj){
    var result=-1;
    for(var i=0;i<this.length;i++){
        if(this[i]==obj){
            result=i;
            break;
        }
    }
    return result;
}

var ary=new Array();

ary=["one","two","three"];

alert(ary.indexof("one"))//0

Ví dụ trên, ta mở rộng Array bằng cách thêm vào một phương thức indexof tính chỉ số của một phần tử trong mảng, nếu đối tượng không có trong mảng thì trả về giá trị là -1;

Cơ chế của prototype

Một câu hỏi sẽ được đặt ra là cơ chế hoạt động của prototype như thế nào? Tại sao với prototype ta có thể giả lập khá nhiều các đặc tính hướng đối tượng? Thật ra prototype lại có một cơ chế hoạt động khá đơn giản: Mỗi khi bạn thực hiện thao tác với thuộc tính và phương thức của một đối tượng nào đó trong JavaScript, thì trình thông dịch sẽ thực hiện các bước tuần tự sau để xác định thuộc tính hay phương thức nào được thực thi:

–        Nếu thuộc tính hay phương thức của đối tượng đang xét có giá trị hoặc đã được gán giá trị thì thuộc tính hay phương thức đó được sử dụng.

–        Nếu không thì kiểm tra giá trị của thuộc tính của prototype trong cấu trúc của object.

–        Cứ tiếp tục như vậy cho đến khi tìm thấy thuộc tính phù hợp (thuộc tính đã được gán giá trị) hoặc giá trị tìm được là kiểu Object.

Chính vì vậy bất cứ khi nào muốn thêm một thuộc tính hay gắn thêm một phương thức mới cho một object bất kỳ ta chỉ việc khai báo nó như là thuộc tính của prototype.

Để thấy rõ hơn điều này ta sẽ cùng nhau tìm hiểu sâu hơn một chút về các thuộc tính và phương thức của các đối tượng cài đặt sẵn trong JavaScript là Object và Function

a) Object:

Thuộc tính:

+constructor

+prototype

Phương thức:

+hasOwnProperty()

+isPrototypeOf()

+toString()

+valueOf()

+toLocaleString()

Ở đây chúng ta chỉ cần lưu ý phương thức khá đặc biệt đó là

+hasOwnProperty();

Với hasOwnProperty() sẽ trả về giá trị true nếu object đang xét có thuộc tính nào đó nhưng không phải là thuộc tính được khai báo kiểu mở rộng prototype, ví dụ:

var myObj=new Object();

myObj.firstProperty="xyz";

myObj.prototype.secondProperty="abc";

alert(myObj.hasOwnProperty("firstProperty")) //true 
alert(myObj.hasOwnProperty("fdasffsdf")) //false

alert(myObj.hasOwnProperty("secondProperty")) //false

b) Function

Thuộc tính:

+constructor

+prototype

+arguments

+arity

+caller

+length

Phương thức:

+apply()

+call()

+toString()

+valueOf()

Với đối tượng Function, chúng ta sẽ tìm hiểu kĩ hơn một chút bởi lẽ đây là đối tượng chính dùng trong lập trình OOP với JavaScript.

Thuộc tính constructor: thuộc tính này trả về một constructor mà từ đó object được tạo ra, ví dụ:

var myFunc=new Function();

alert(myFunc.constructor); // kết quả sẽ là Function

Lưu ý rằng thuộc tính này chỉ có trong các biến có kiểu object, nên với một biến bất kỳ muốn sử dụng constructor thì ta phải kiểm tra kiểu trước khi sử dụng, ví dụ.

if(typeof myVar =="object"){
    alert(myVar.constructor);
}

Thuộc tính arguments: arguments thực chất là một mảng chứa các tham số cho function, khi function được gán cho một đối số thì đối số này sẽ được đẩy vào mảng arguments, ta xét ví dụ sau:

function test() {
    var arg=test.arguments;
    for(var i=0;i<arg.length;i++){
        alert(arg[i]);
    }
}
test(1,"a");// sẽ đưa ra giá trị 1 và sau đó là "a"

Để ý rằng hàm test ban đầu không khai báo tham số, nhưng khi chạy ta đã thêm hai tham số vào cho nó do vậy trong arguments sẽ có 2 giá trị.

Phương thức call() và apply(): Đây là hai phương thức của object Function, dùng để thực thi, hoặc gọi một function. Tham số đầu của hai phương thức trên thường là một object, cái mà sẽ được coi là object hiện thời và là tham số cho hàm thực thi phương thức cal () hay apply(). Trong thực tế người ta thường dùng call thay cho apply bởi chức năng không có gì khác nhau, chỉ khác ở tham số truyền vào cho hai phương thức. Để minh họa điều này ta xét ví dụ sau:

function showInfo(){
    alert(this.name);
}

function myObject(name){
    this.name=name;
    this.show=showInfo;
}

var myObj=new myObject("AxitDN");

//Cách thông thường

myObj.show();

//kết quả hiện AxitDN

//Sử dụng call

showInfo.call(myObj); //kết quả hiện AxitDN

Ví dụ trên cho ta thấy rằng trong JavaScript, các hàm được sử dụng một cách khá tự do, thậm trí được sử dụng với một mục đích hoàn toàn khác so với lúc nó được tạo cho đến khi kết thúc chương trình. Không những thế với thuộc tính đặc biệt prototype càng làm cho chương trình viết bằng JavaScript trở nên vô cùng sinh động.

Như vậy, trong bài viết này các bạn đã được tìm hiểu về cách mà JavaScript mô tả các lớp để từ đó tạo nên những đối tượng thông qua Function và prototype-base. Trong bài viết tiếp theo chúng ta sẽ tìm hiểu cách phát triển các đặc tính hướng đối tượng khác với JavaScript, và hứa hẹn sẽ có nhiều điều thú vị được gợi mở!

1 – 2 – 3

(Còn nữa)

Tài nguyên:

[link] http://www.w3schools.com/js/

[eBook] Head First JavaScript by Michael Morrison

[eBook] JavaScript Bible by Danny Goodman

JavaScript và lập trình hướng đối tượng [3]

Cấu trúc của một đối tượng đôi khi rất phức tạp, trong một đối tượng có thể có vô số các thuộc tính, các biến trạng thái, các phương thức và hàm công cụ. Nhưng trên thực tế, khi sử dụng các đối tượng, lập trình viên chỉ quan tâm đến những tính năng thật sự hữu ích mà đối tượng đó mạng lại mà không không quan tâm xem chúng có cấu trúc hay cơ chế phức tạp đến đâu. Để giải quyết vấn đề này, OOP đưa ra khái niệm về tính bao gói (Encapsulation). Liệu JavaScript có cung cấp cho chúng ta khả năng bao gói các đối tượng hay không? chúng ta sẽ cùng nhau khảo sát vấn đề này ngay sau đây.

Vấn đề 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, mục đích là tạo ra cơ chế che giấu hay hiển thị các đặc tính của đối tượng này đối với đối tượng khác. 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:

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 (...) {...};
}

Như vậy về cơ bản ta có thể chủ động tạo ra đặc tính đóng gói cho đối tượng được tạo ra trong JavaScript.

Trong thế giới của động vật, các đối tượng rất phong phú và đa dạng về đặc điểm cũng như tập tính, và vì vậy để việc quản lý dễ dàng hơn người ta đưa ra khái niệm phân loại các nhóm đối tượng thành lớp, bộ, ngành v.v… Trong lập trình hướng đối tượng, các đối tượng có đặc điểm đặc trưng giống nhau được mô tả chung bởi một lớp. Các đối tượng khác lớp nhưng có cùng chung nguồn gốc được nhóm lại bằng mối quan hệ về thế hệ (generation). Cách làm đó dựa trên đặc tính kế thừa và sự đa hình trong lập trình hướng đối tượng. Với JavaScript chúng ta cũng có thể giả lập khả năng này theo một cách rất riêng sau đây:

Vấn đề kế thừa với prototype

Kế thừa là một đặc tính vô cùng quan trọng trong lập trình hướng đối tượng, nhờ đặc điểm này việc phát triển và bảo trì ứng dụng trở nên đơn giản hơn bao giờ hết. Tuy nhiên JavaScript mặc nhiên không cung cấp đặc tính quan trọng này, muốn cài đặt, chúng ta phải dựa trên một vài pattern khá rắc rối. 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"]'

Chạy thử

*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 JavaScript:

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"]

////////////////////////////////////////////////////////

Chạy thử

Phù!!!Giá như JavaScript hỗ trợ tốt OOP thì đỡ biết mấy!

Với đặc tính linh hoạt và mềm dẻo có lẽ còn có rất nhiều cách thức khác thể hiện OOP trong JavaScript, tuy nhiên câu chuyện đã khá dài, xem ra chỉ phù hợp với những ai muốn xây cho mình một framework mạnh như JQuery chẳng hạn mới quan tâm hơn về vấn đề này. Do vậy chuỗi bài về OOP trong JavaScript xin khép lại ở đây để chuyển sang những vấn đề thiết thực và gần gũi, thường nhật hơn với các bạn lập trình. Hy vọng bài viết đem lại nhiều điều bổ ích cho các bạn lập trình trong quá trình chinh phục thử thách trên những dòng code.

1 – 2 – 3

(Hết)

Tài nguyên:

[link] http://www.w3schools.com/js/

[eBook] Head First JavaScript by Michael Morrison

[eBook] JavaScript Bible by Danny Goodman

Tự học lập trình trong 10 năm – Peter Norvig

Tự học lập trình trong 10 năm

Đây là quan điểm của Peter Norvig về các loại sách “ăn xổi” bày bán ngoài thị trường hiện nay với những cái “tít” nghe rất ư là hứng khởi: “Tự học Java trong 7 ngày” hay “Học Visual C++.NET trong 24 giờ”,v.v…Tất nhiên, mỗi người đều có động cơ và mục đích riêng khi học lập trình. Nhưng Peter cho rằng để trở thành một “expert programmer”, bạn cần ít nhất 10 năm, và việc trông chờ vào những cuốn sách kiểu như trên để “thay da đổi thịt” là điều không tưởng. Nhân tiện đây xin giới thiệu đôi chút về Peter Norvig:

Theo từ điển bách khoa Wikipedia thì Peter Norvig hiện đang giữ chức giám đốc trung tâm nghiên cứu của Google Inc., (Director of Research, formly Director of Search Quality) với nhiệm vụ cao cả là sắp xếp và tổ chức lại thế giới thông tin cho người dùng dễ dàng truy cập, khai thác (đúng với slogan của Google, xem thêm “Những anh hùng sau thành công của Google“). Ông đồng thời là ủy viên ban điều hành của Hiệp hội Trí tuệ nhân tạo Hoa Kì (American Association for Artificial Intelligence). Cùng với Stuart Russell, Peter Norvig là đồng tác giả của cuốn sách: “Artificial Intelligence – A Modern Approach” nổi tiếng, được giảng dạy tại 940 trường đại học thuộc hơn 90 quốc gia trên toàn thế giới. Cuốn sách là giáo trình chuẩn của rất nhiều University lừng danh như: MIT (Massachussetts Institute of Technology), University of California at Berkeley, Princeton University, Standford University, Oxford University, Yale University, v.v…Trước đó, ông giữ chức giám đốc điều hành trung tâm khoa học tính toán (Computational Science Division) của NASA tại Ames (NASA Ames Research Center), có nhiệm vụ giám sát hoạt động của hơn 200 nhà khoa học. Ngoài ra, ông còn đảm nhận chức vụ giáo sư tại đại học Nam California, thành viên hội đồng nghiên cứu khoa học của University of California at Berkeley, cũng như là một trong những nhà khoa học lão làng tại Sun Microsystems Laboratories…

Ý kiến dưới đây của Peter Norvig cũng đáng để mọi người cùng nhau thưởng thức và suy ngẫm. Nào, hãy bắt đầu…

Tại sao bây giờ người ta lại vội vàng đến như vậy?

Rảo bước quanh các cửa hàng sách, bạn sẽ thấy cái tựa: “Hướng dẫn lập trình Java trong 7 ngày” nằm bên cạnh một dãy dài vô tận những lời “đề nghị dạy học” đại loại như vậy về Visual Basic, Windows hay Internet… chỉ cần vài ngày hay vài giờ ngắn ngủi. Tôi đã thử thực hiện một  tìm kiếm nâng cao (advanced search) tại Amazon.com:

pubdate: after 1992 and title: days and (title: learn or title: teach yourself)

và nhận được 248 kết quả. 78 kết quả đầu tiên là sách về máy tính (cuốn thứ 79 là Learn Bengali in 30 days). Thử thay “days” bằng “hours”, những gì thu được cũng hoàn toàn tương tự: 253 kết quả, với 77 kết quả đầu tiên là máy tính (cuốn thứ 78 là Teach Yourself Grammar and Style in 24 Hours). Khi vượt ra ngoài top 200, tất cả các cuốn sách đều là sách về máy tính.

Từ đây có thể rút ra kết luận: “Hoặc là bây giờ người ta đổ xô đi tìm hiểu về máy tính, hoặc máy tính không hiểu vì một lí do hoang đường nào đó lại trở nên dễ học hơn những thứ khác”. Chẳng có quyển sách nào hướng dẫn học Beethoven, Vật lý Lượng tử hay thậm chí là “chăm sóc cho chó” (Dog Grooming) chỉ trong có vài ngày. Felleisen các đồng sự đồng tình với xu hướng này trong cuốn  How to Design Programs, khi họ cho rằnglập trình bẩn là rất dễ, những kẻ ngốc, thậm chí đần độn có thể học nó trong 24 giờ”  

Hãy thử xem cái title: Learn C++ in Three Days gợi lên được điều gì:

  • Learn: Trong 3 ngày thậm chí bạn còn chưa đủ thời gian viết được một vài chương trình cho ra hồn chứ đừng nói đến việc học từ những thành công và thất bại. Bạn cũng không có cơ hội làm việc với những người có kinh nghiệm và hiểu xem cái gì đang xảy ra xung quanh mình. Nói tóm lại là bạn chẳng thể nào học cho cặn kẽ được. 3 ngày chỉ đủ để bạn làm quen với phần nổi bề ngoài, đó không phải sự thấu hiểu. Và như Alexander Pope đã từng nói: “Hiểu biết nông cạn còn nguy hiểm hơn kẻ mù chữ” (A little learning is a dangerous thing).
  • C++: Trong 3 ngày có thể bạn học xong cú pháp của C++(nếu bạn đã thực sự biết một vài ngôn ngữ lập trình khác), nhưng bạn khó mà học được cách sử dụng chúng. Nói ngắn gọn, nếu bạn là một lập trình viên Basic, bạn có thể viết những chương trình theo phong cách Basic sử dụng cú pháp của Pascal, nhưng bạn sẽ chẳng hiểu được những ưu điểm hay nhược điểm của Pascal. Vậy quan điểm ở đây là gì? Alan Perlis (ND:giáo sư đại học Yale), trong Epigrams on Programming đã từng nói: “Một ngôn ngữ mà chẳng ảnh hưởng gì đến cách bạn tư duy về lập trình, ngôn ngữ ấy chỉ là đồ bỏ đi, không đáng để học”. Có thể chấp nhận được nếu bạn chỉ định học đôi chút về Pascal (hoặc Visual Basic hay JavaScript) vì bạn chỉ cần làm quen với những công cụ có sẵn để làm một việc nào đó. Nhưng đấy không phải là bạn học ngôn ngữ để lập trình, bạn chỉ học để hoàn thành nhiệm vụ cụ thể của mình mà thôi.
  • Trong 3 ngày: Thật đáng tiếc, điều này là không thể, như tôi sẽ chỉ ra dưới đây.

Tự học lập trình trong 10 năm

Các nhà nghiên cứu (Bloom (1985)Bryan & Harter (1899)Hayes (1989)Simmon & Chase (1973)) đã chỉ ra rằng cần ít nhất 10 năm để đạt được sự tinh thông trong nhiều lĩnh vực, từ đánh cờ, sáng tác âm nhạc, hội họa, bơi lội, tennis, hay thu được kết quả trong tâm lí thần kinh hay hình học topo. Điều quan trọng là bàn về phương pháp thực hành: không chỉ là việc lặp đi lặp lại đơn thuần, mà còn thử thách chính mình bằng những nhiệm vụ như vượt qua khả năng hiện tại của bản thân, cố gắng, phân tích hiệu xuất của mình trong và sau quá trình rèn luyện, và sửa chữa bất kỳ sai lầm nào. Cứ như vậy, lặp đi lặp lại. Và lịch sử đã chứng minh không thể có con đường tắt: dù cho đó là Mozart, thiên tài âm nhạc nảy nở từ năm lên 4 tuổi, cũng phải mất 13 năm để cho ra đời tác phẩm nhạc cổ điển đầu tiên. Dù cho đó là Beatles, trước khi xuất bản #1 đầu tiên vào năm 1964, họ cũng đã phải cặm cụi trong những câu lạc bộ nhỏ tại Liverpool hay Hamburg từ năm 1957, và trong khi họ có sức hấp dẫn đại chúng từ rất sớm, thành công quan trọng đầu tiên của nhóm là Sgt. Pepper, album được phát hành năm 1967. Malcolm Gladwell có bài báo nghiên cứu so sánh về các sinh viên tại Học viện âm nhạc Berlin trong ba nhóm tốt, khá và trung bình và hỏi họ đã thực hành chăm chỉ như nào:

Tất cả mọi người, từ tất cả ba nhóm, bắt đầu chơi ở cùng một khoảng thời gian – tầm khoảng năm tuổi. Trong những năm đầu tiên, tất cả mọi người thực hành một lượng như nhau – khoảng hai hoặc ba giờ một tuần. Nhưng tầm tám tuổi sự khác biệt thực sự bắt đầu xuất hiện. Các sinh viên trong nhóm tốt nhất, họ bắt đầu thực hành nhiều hơn những người khác: sáu giờ một tuần lúc chín tuổi, tám tuổi là 12 giờ, 16 giờ một tuần khi 14 tuổi, và tăng lên dần, cho đến tuổi 20 họ đã được tập luyện 30 giờ một tuần. 20 tuổi, nghệ sĩ ưu tú đã có tất cả 10.000 giờ thực hành trong cuộc sống của họ. Các sinh viên chỉ đơn thuần là tốt đã đạt 8.000 giờ, và giáo viên âm nhạc trong tương lai là hơn 4.000 giờ.

Vì vậy, có thể 10.000 giờ, chứ không phải 10 năm, là con số kỳ diệu. (Henri Cartier-Bresson (1908-2004) nói: “10.000  bức ảnh đầu tiên của bạn là tệ nhất”, nhưng ông đã chụp nhiều hơn một bức trong một giờ.). Samuel Johnson (1709-1784) nghĩ rằng việc này thậm chí còn lâu hơn: “Sự vượt trội ở bất cứ lĩnh vực nào cũng chỉ có thể đạt được bằng lao động cật lực trong suốt cuộc đời, bạn không thể mua nó bằng một cái giá rẻ hơn”. Và Chaucer (1340-1400) phàn nàn rằng: “Cuộc đời quá ngắn ngủi, trong khi những mánh khóe thì lại quá dài để có thể học được”. Hippocrates (khoảng 400BC) được biết đến với các trích đoạn “ars longa, vita brevis”, là một phần của chích đoạn dài hơn “Ars longa, vita brevis, occasio praeceps, periculosum experimentum, iudicium difficile”, với nghĩa tạm dịch là “Cuộc đời là ngắn ngủi,  với cơ hội phù du, đầy dãy các kỹ năng, với các thử nghiệm nguy hiểm, và phán quyết khó khăn”. Mặc dù trong tiếng Latin, ars có thể có nghĩa là nghệ thuật, hoặc mánh khóe, nếu so từ gốc tiếng Hy Lạp “techne” thì chỉ có thể có nghĩa là “kỹ năng”, không phải “nghệ thuật”.

Và nếu bạn muốn trở thành lập trình viên

Đây là công thức cho những thành công của tôi trong lập trình:

  • Hãy yêu thích nó, bạn làm vì bạn cảm thấy vui vẻ và hào hứng. Hãy chắc chắn rằng bạn luôn như thế trong 10 năm…
  • Hãy trao đổi với những lập trình viên khác, đọc chương trình của họ viết. Điều này còn quan trọng hơn bất kì quyển sách hay khóa đào tạo nào.
  • Lập trình: Cách học tốt nhất là học đi đôi với hành (learning by doing). Nói cho rõ ràng hơn thì: “Tầm cao nhất của một lĩnh vực nào đó không thể có được chỉ thông qua sự bổ sung về mặt kinh nghiệm. Nhưng kể cả khi đã có rất nhiều kinh nghiệm, nếu cố gắng và nỗ lực, bạn vẫn có thể tiến xa hơn” và “để học tập một cách hiệu quả nhất, bạn cần phải xác định rõ khả năng hiện tại của mình, thu thập kiến thức từ người khác, và tự tìm lấy những cơ hội để học theo và sửa đổi sai lầm của chính mình”. Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life là một cuốn sách rất hữu ích cho quan điểm này.
  • Nếu bạn muốn, hãy giành 4 năm ở đại học (và nhiều hơn ở trường phổ thông). Bạn sẽ có cơ hội làm những công việc yêu cầu khả năng và hiểu biết chuyên sâu hơn trong một vài lĩnh vực,  nhưng nếu bạn không thích trường học, bạn có thể (với một số sự nỗi lực và cống hiến) có được kinh nghiệm tương tự của riêng bạn hoặc trong công việc . Trong bất kì trường hợp nào, chỉ đọc sách thôi là chưa đủ. “Bút vẽ và mực màu không thể biến bạn trở thành họa sĩ, cũng như những bài giảng về khoa học máy tính trên lớp không thể giúp bạn trở thành chuyên gia lập trình”, Eric Raymond, tác giả của The New Hacker Dictionary đã nói như vậy. Một trong những lập trình viên giỏi nhất mà tôi đã từng thuê chưa từng có bằng đại học, nhưng anh ta đã tham gia viết những phần mềm tuyệt vời (ví dụ), có news group của riêng mình và anh ta thậm chí còn giàu hơn cả tôi.
  • Hãy tham gia vào các projects với những lập trình viên khác. Bạn có thể là best programmer trong một projects, nhưng cũng có thể là worst. Nếu là best, hãy thử khả năng làm leader của mình. Còn ngược lại, hãy học hỏi xem người giỏi nhất làm gì, và học xem họ không làm gì (vì họ muốn bạn làm những việc đó).
  • Trong một project, hãy bắt tay làm việc sau những lập trình viên khác, khi bạn đã hiểu chương trình của họ. Hãy tìm xem những gì cần thiết để hiểu và chỉnh sửa khi người viết ra chương trình đó không có mặt ở đây. Thử nghĩ xem làm thế nào để thiết kế những chương trình giúp cho những người sau này có thể dễ dàng hơn trong việc bảo trì nó.
  • Hãy học ít nhất khoảng nửa tá ngôn ngữ lập trình, bao gồm:
    • Một ngôn ngữ hỗ trợ việc tạo các lớp trừu tượng (Java hoặc C++)
    • Một ngôn ngữ lập trình hàm (Lisp hoặc ML)
    • Một ngôn ngữ cú pháp (Lisp)
    • Một ngôn ngữ hỗ trợ khai báo định danh (Prolog hoặc C++ templates)
    • Một ngôn ngữ hỗ trợ coroutine (Icon hay Scheme)
    • Một ngôn ngữ hỗ trợ song song (Sisal)
  • Hãy nhớ rằng “máy tính” là một phần của “khoa học máy tính”. Nên nắm rõ thời gian để máy tính thực hiện một chỉ thị, lấy một từ trong bộ nhớ (có hay không có cache), đọc những từ liên tiếp trong ổ đĩa, hay tìm kiếm một vùng nhớ cụ thể.

Thời gian cần thiết để thực hiện các phép toán với một PC tốc độ 1GHz:

execute typical instruction 1/1,000,000,000 sec = 1 nanosec
fetch from L1 cache memory 0.5 nanosec
branch misprediction 5 nanosec
fetch from L2 cache memory 7 nanosec
Mutex lock/unlock 25 nanosec
fetch from main memory 100 nanosec
send 2K bytes over 1Gbps network 20,000 nanosec
read 1MB sequentially from memory 250,000 nanosec
fetch from new disk location (seek) 8,000,000 nanosec
read 1MB sequentially from disk 20,000,000 nanosec
send packet US to Europe and back 150 milliseconds = 150,000,000 nanosec
  • Hãy cố gắng tuân theo những qui định tiêu chuẩn của ngôn ngữ. Có thể đó là của ANSI C++, hoặc đơn giản đó là của công ty bạn. Bạn cũng nên tìm hiểu xem người ta thích gì ở ngôn ngữ đó, họ cảm thấy thế nào, hay vì sao họ thích nó.
  • Hãy sẵn sàng từ bỏ những tiêu chuẩn của ngôn ngữ càng nhanh càng tốt.

Với những điều tôi đã nói ở trên, bạn có thể băn khoăn rằng bạn sẽ đạt đến mức nào nếu chỉ đọc sách? Trước khi con trai cả của mình ra đời, tôi đã đọc tất cả những cuốn sách How To (làm thế nào), và vẫn cảm thấy vô cùng mù mờ, rối rắm. 30 tháng sau, khi sinh đứa thứ hai, phải chăng tôi đã quay lại chúng với cái nhìn hoàn toàn mới mẻ? Không, tôi chỉ dựa vào kinh nghiệm của chính mình. Chúng có ích và khiến tôi vững tin hơn nhiều so với việc đọc hàng ngàn trang sách của các bậc chuyên gia.

Fred Brooks, trong bài luận nổi tiếng No Silver Bullets đã làm rõ ba bước để tìm một nhà thiết kế tốt:

  • Hãy giải thích một cách có hệ thống cho câu hỏi: “Thế nào là một nhà thiết kế đỉnh?” càng sớm càng tốt.
  • Hãy giao triển vọng và tương lai phát triển của công ty cho những người thông thái, có nhiều kinh nghiệm và tìm cách giữ chân họ cẩn thận.
  • Trao cơ hội cho những nhà thiết kế đang trong giai đoạn trưởng thành để họ có thể tiếp xúc, học hỏi, nâng cao trình độ.

Điều này coi như giả định là một số người có đầy đủ những phẩm chất cần thiết để trở thành một nhà thiết kế lớn, và công việc đủ để dụ họ theo. Alan Perlis đã từng nói: “Mọi người đều có thể được dạy điêu khắc, Michealangelo thì không. Và ông đã tự trở thành nhà điêu khắc vĩ đại. Những lập trình viên siêu hạng cũng nên thế”. Perlis đã từng nói rằng những người vĩ đại sẵn có một nội lực vượt qua cả sự đào tạo. Nhưng nội lực đó đến từ đâu? Bẩm sinh ư? Hay chúng được phát triển thông qua sự tích cực? Như Auguste Gusteau (đầu bếp huyền thoại của  Ratatouille) đã nói: “bất cứ ai cũng có thể nấu ăn, nhưng chỉ những người không biết sợ mới trở nên vĩ đại”. Tôi nghĩ về điều này nhiều đến mức cứ như là sẵn sàng cống hiến phần lớn thời gian trong đời của ai đó để luận bàn. Nhưng không hề sợ hãi có lẽ là cách để tóm lại tất cả những điều đó. Hoặc, như nhà phê bình Gusteau, Anton Ego, nói: “Không phải tất cả mọi người  đều có thể trở thành một nghệ sĩ lớn, nhưng một nghệ sĩ lớn có thể đến từ bất cứ nơi nào.”

Vì vậy, nếu mua quyển sách Java/Ruby/Javascript/PHP nói trên, bạn có thế sẽ thu được một vài điều có ích. Nhưng nó không thể thay đổi cuộc đời của bạn, hoặc đưa bạn đến sự thành thạo hay tinh thông, chỉ trong 24 giờ, vài ngày hay thậm chí là vài tháng.

Tham khảo

Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine, 1985.
Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p. 10-19.
Bryan, W.L. & Harter, N. “Studies on the telegraphic language: The acquisition of a hierarchy of habits. Psychology Review, 1899, 8, 345-375
Hayes, John R., Complete Problem Solver Lawrence Erlbaum, 1989.
Chase, William G. & Simon, Herbert A. “Perception in Chess” Cognitive Psychology, 1973, 4, 55-81.
Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life, Cambridge University Press, 1988.

Phụ lục: Lựa chọn ngôn ngữ

Có nhiều người hỏi nên chọn học ngôn ngữ nào đầu tiên. Không có câu trả lời cụ thể, dưới đây là một số quan điểm:

  • Hãy nhìn vào bạn bè mình. Khi hỏi: “Tôi nên sử dụng hệ điều hành nào đây, Windows, Unix hay Mac?”, tôi thường đáp rằng: “Hãy nhìn vào bạn của mình”. Lợi ích thu được từ việc học hỏi bạn bè sẽ bù đắp cho những khác biệt căn bản giữa các hệ điều hành hay ngôn ngữ lập trình. Thêm nữa, hãy nhìn những người bạn “sắp quen”: đó là tập hợp các programmers mà bạn sẽ song hành cùng họ nếu bạn vẫn tiếp tục hành trình. Ngôn ngữ bạn chọn có một cộng đồng phát triển rộng lớn hay chỉ một vài người tham gia? Có những tài liệu, websites hay forums nào mà bạn sẽ nhận được lời giải đáp cho thắc mắc của mình? Bạn có thích những người ở đó không?
  • Hãy làm thật đơn giản. Những ngôn ngữ như C++ và Java được thiết kế cho các ứng dụng chuyên nghiệp thực hiển bởi đội ngũ đông đảo các lập trình viên giàu kinh nghiệm, những người vốn rất quan tâm đến hiệu suất những dòng mã của họ. Vì thế, chúng thường bao gồm nhiều thành phần rất phức tạp. Nếu mới bắt đầu, bạn không cần đến sự phức tạp. Bạn hãy học những ngôn ngữ có cấu trúc đơn giản, dễ học.
  • Chơi. Bạn thích học piano theo cách nào hơn: kiểu tương tác thông thường, tức là bạn nghe mỗi nốt nhạc ngay khi nhấn một phím đàn hay cách thức theo “lô”, tức bạn chỉ nghe các nốt sau khi đã hoàn thành cả nhạc phẩm? Rõ ràng, cách thứ nhất giúp bạn dễ dàng học hơn phải không? Lập trình cũng như vậy. Hãy chọn cho mình một kiểu tương tác và sử dụng nó

Theo tiêu chuẩn của riêng mình, tôi khuyên bạn nên bắt đầu với Python hoặc SchemeNhưng mỗi người có những hoàn cảnh khác nhau, và có thể có những lựa chọn tốt hơn. Nếu tuổi của bạn mới chỉ là số có một chữ số, theo tôi bạn nên chọn Alice hoặc Squeak (người lớn cũng có thể thích chúng). Tuy nhiên, đó không phải là điều quan trọng. Quan trọng là, hãy chọn đi và bắt đầu ngay lập tức.


Phụ lục: Sách và các tài nguyên khác

Nhiều người hay hỏi rằng họ nên học từ những cuốn sách hay những trang web nào. Tôi luôn lặp lại rằng: “chỉ học trong sách vở là không đủ”, nhưng tôi có thể giới thiệu một vài cuốn sau đây:

Tác giả: Peter Norvig

http://www.norvig.com

Sử dụng bộ nhớ hiệu quả với Memory Pool

Vấn đề cân bằng giữa tốc độ và sử dụng bộ nhớ luôn là vấn đề đau đầu đối với lập trình viên. Khi phải đối mặt với vấn đề này, người lập trình phải cân nhắc xem nên ưu tiên tốc độ hay bộ nhớ. Đối với những ứng dụng chạy theo thời gian thực thì bộ nhớ luôn là vấn đề hàng đầu, bởi trong suốt vòng đời của ứng dụng, các đối tượng liên tục được tạo và hủy gây phân mảnh vùng nhớ, do vậy nếu quản lý bộ nhớ không tốt sẽ gây lãng phí tài nguyên dẫn đến hệ thống nhanh chóng rơi vào tình trạng cạn bộ nhớ. Đối với việc lập trình cho các thiết bị hạn chế về tài nguyên thì cần phải xem trọng cả hai. Việc tìm ra điểm cân bằng giữa tốc độ thực thi và sử dụng bộ nhớ là một công việc không phải lúc nào cũng dễ dàng.

Bài viết này sẽ cung cấp cho các bạn một giải pháp để tham khảo trong việc quản lý bộ nhớ sử dụng Memory Pool. Memory Pool là một kỹ thuật giúp khai thác vùng nhớ một cách hiệu quả bằng cách khởi tạo một vùng nhớ cố định sau đó quản lý việc cấp phát và tái chế các đối tượng trong vùng nhớ này.

Để hiểu được cách mà Pool hoạt động, ta thử đặt một tình huống giả định sau đây: Một đạo diễn phim nhận được một kịch bản trong đó có hơn 100 nhân vật, nhưng thực tế ông chỉ có 10 diễn viên trong tay. Làm thế nào để hoàn thành bộ phim có hơn 100 nhân vật mà trong tay chỉ có 10 diễn viên? Câu trả lời trong trường hợp này đơn giản là: cho mỗi diễn viên đóng nhiều vai khác nhau.

Thử một tình hống khác: Làm thế nào để tiêu diệt 1000 máy bay địch mà trong tay bạn chỉ có 10 viên đạn? :D. Các bạn sẽ thắc mắc rằng thực tế làm sao mà làm được như vậy, nhưng nếu bạn đã từng chơi một game đi cảnh bắn súng kiểu như SkyForce thì điều này hoàn toàn là sự thật đứng dưới góc độ của lập trình viên, còn tất nhiên người chơi thì vẫn chỉ biết bắn và bắn mà không hề biết rằng mình chỉ có 10 viên đạn!!! Tình huống này so với tình huống trên là không khác gì nhau nếu nhìn nhận dưới góc độ của Pool.

Nếu các bạn vẫn cảm thấy chưa thuyết phục, ta hãy code một game đơn giản giúp minh họa cách hoạt động của memory pool như sau: Trò chơi với một khẩu súng máy có 1000 viên đạn, bắn liên tục vào một bia đích cách đó 10m. Trò chơi sẽ kết thúc khi không còn viên đạn nào được bắn ra.

Sau đây ta cùng nhau khảo sát mã nguồn của trò chơi này (tuy viết bằng Java, nhưng về mặt tư tưởng có thể áp dụng trên các ngôn ngữ và nền tảng khác):

– Mô tả lớp các viên đạn, với một thuộc tính cơ bản là vị trí của viên đạn theo thời gian (đơn vị m):

public class Bullet {
    private int position;

    public int getPosition() {
         return position;
    }

    public void setPosition(int position) {
         this.position = position;
    }

    public Bullet() {
    }

    public void move() {
         position++;
    }
}

– Lớp MemoryPool cho phép tạo pool cho một nhóm đối tượng cụ thể:

import java.util.LinkedList;

public abstract class MemoryPool<T> {
    private LinkedList<T> free_items = new LinkedList<T>();
    public void freeItem(T item) {
        free_items.add(item);
    }
    protected abstract T allocate();
    public T newItem() {
        T out = null;
        if ( free_items.size() == 0 ) {
             out = allocate();
        } else {
             out = free_items.getFirst();
             free_items.removeFirst();
        }
        return out;
    }
}

Ta thấy MemoryPool sử dụng cấu trúc dữ liệu FreeList, trong đó free_items là một danh sách liên kết cho phép lưu các đối tượng đã được giải phóng khi hết vai trò, chúng sẽ được tái sử dụng trong phương thức newItem(). MemoryPool có một phương thức allocate() dùng để tạo mới các đối tượng trên thực tế, thay vì tạo mới và tái sử dụng như phương thức newItem().

– Lớp BulletPool là một MemoryPool được triển khai như sau:

public class BulletPool extends MemoryPool<Bullet> {
    @Override
    protected Bullet allocate() {
        return new Bullet();
    }
}

– Lớp Gun, mô tả một khẩu súng máy với khả năng bắn tiết kiệm (dùng Pool) và bắn “xả láng” (không dùng Pool).

public class Gun {
    private int bulletCount=1000;
    public void fireInPool() {
         BulletPool pool=new BulletPool();
         List<Bullet> plist=new ArrayList<>();
         for(int i=0;i<bulletCount;i++) {
             Bullet p=pool.newItem();
             p.setPosition(0);
             plist.add(p);
             for(int j=0;j<plist.size();j++) {
                 Bullet pp=plist.get(j);
                 pp.move();
                 System.out.print("-"+pp.getPosition());
                 if(pp.getPosition()==10) {
                      pool.freeItem(pp);
                      plist.remove(pp);
                 }
             }
             System.out.println();
         }
    }

    public void fire() {
         List<Bullet> plist=new ArrayList<>();
         for(int i=0;i<bulletCount;i++) {
              Bullet p=new Bullet();
              p.setPosition(0);
              plist.add(p);
              for(int j=0;j<plist.size();j++) {
                  Bullet pp=plist.get(j);
                  pp.move();
                  System.out.print("-"+pp.getPosition());
                  if(pp.getPosition()==10) {
                      plist.remove(pp);
                  }
              }
              System.out.println();
        }
    }
}

Cuối cùng là gameplay:

public class DemoMemoryPool {
    /**
    * @param args the command line arguments
    */
    public static void main(String[] args) {
        // TODO code application logic here
        Gun gun=new Gun();
        System.out.println("Start");
        gun.fireInPool();
        gun.fire();
        System.out.println("Game over");
    }
}

Dùng Profiler đo việc sử dụng bộ nhớ đối với đối tượng Bullet ta thấy kết quả khá rõ ràng, khi không sử dụng pool để bắn được 1000 viên đạn bạn cần khởi tạo 1000 đối tượng Bullet, trong khi với pool ta chỉ cần khởi tạo 11 đối tượng :

Điều thú vị là con số 11 đối tượng được khởi tạo không phụ thuộc vào số lần lặp trong vòng lặp for ở trên mà chỉ phụ thuộc vào điều kiện giải phóng đối tượng (trong ví dụ này là thời điểm position=10).

Như vậy có thể thấy rằng, việc sử dụng pool một cách khéo léo sẽ giải quyết khá triệt để vấn đề hạn chế về bộ nhớ trong ứng dụng, đặc biệt là game hoặc các ứng dụng tương tác thời gian thực. Tất nhiên triển khai pool trên thực tế còn phải phụ thuộc nền tảng và bản thân bài toán cụ thể. Hy vọng rằng qua bài viết này các bạn lập trình viên sẽ rút ra được những giải pháp, những ý tưởng cho các bài toán thực tế hàng ngày.

Tham khảo:
http://en.wikipedia.org/wiki/Memory_pool
http://en.wikipedia.org/wiki/Free_list
http://box2d.org/forum/viewtopic.php?f=5&t=2794&start=10

Hậu trường: Chuyện về hàm $ trong jQuery

Nhớ lại bài viết trước, hàm $ trong jQuery của chúng ta xuất hiện với dáng vẻ xem ra rất hiền lành đơn giản, một hàm $ có khả năng tạo, truy vấn và tìm kiếm mọi phần tử hiện diện trên trang web dựa trên cấu trúc truy vấn CSS Selector. Tuy nhiên, câu chuyện hậu trường sau đây sẽ hé mở nhiều tình tiết ly kỳ đằng sau cái dáng vẻ đơn giản và hiền lành ấy. Những mẩu chuyện mà tôi đã tận mắt chứng kiến và chưa bao giờ kể!

Hãy xem lại cảnh mở màn của $. Ở cảnh này, hàm $ đến với bạn như một cỗ máy tìm kiếm các phần tử trong DOM. Nhưng hãy thử dừng hình một chút! bạn sẽ thấy có điều gì đó không đúng?! Vâng, đúng vậy, hãy thử xem một đoạn code sau đây:

//the function that creates dream
function dream(){
    //calculating random color of dream
    var color = 'rgb('+Math.floor(Math.random()*255)+','
                      +Math.floor(Math.random()*255)+','
                      +Math.floor(Math.random()*255)+')';

    //calculating random X position
    var x = Math.floor(Math.random()*$(window).width());

    //calculating random Y position
    var y = Math.floor(Math.random()*$(window).height());

    //creating the dream and hide
    drawingpix = $('').attr({class: 'drawingpix'}).hide();

    //appending it to body
    $(document.body).append(drawingpix);

    //styling dream.. filling colors.. positioning.. showing.. growing..fading
    drawingpix.css({
                       'background-color':color,
                       'border-radius':'100px',
                       '-moz-border-radius': '100px',
                       '-webkit-border-radius': '100px',
                       top: y-14, //offsets
                       left: x-14 //offsets
                   }).show().animate({
                                      height:'500px',
                                      width:'500px',
                                      'border-radius':'500px',
                                      '-moz-border-radius': '500px',
                                      '-webkit-border-radius': '500px',
                                      opacity: 0.1,
                                      top: y-250, //offsets
                                      left: x-250
                                     }, 3000).fadeOut(2000);

     //Every dream's end starts a new dream
     window.setTimeout('dream()',200);
}

$(document).ready(function() {
     //calling the first dream
     dream();
});

Xem ngay

Nếu tinh ý các bạn sẽ thấy các elements mà $ tạo ra bỗng dưng không còn là chính mình nữa. Chúng bỗng trở nên linh hoạt khác thường, chúng có thể “nhảy múa”, biến đổi sắc thái, ẩn hiện như có phép màu. Lúc này hẳn bạn nghĩ mình vừa đụng phải “cây đũa phép $” của Harry Potter. Hàm $ → đũa phép $, bạn đang mơ chăng?? Rất có thể! Nếu bạn đã từng xem bộ phim Inception (hoặc Matrix) chắc sẽ vẫn còn nhớ cái ảo giác rằng tất cả chúng ta thật ra đang mơ… Ít phút nữa thôi, các bạn cũng sẽ được chứng kiến cảm giác đó, nhưng may mắn nhân vật chính không phải chúng ta mà là các elements khi bị rơi vào mê trận của hàm $ trong cái thế giới được tạo ra bởi jQuery (bạn có nhớ $=jQuery :D).



Tuy nhiên, trước khi bước vào cuộc hành trình lần theo những giấc mơ này, chúng ta cần trang bị một chút hành trang để không bị môi trường trong jQuery gây sốc:

+ Cơ bản về hàm trong javascript:

Theo đặc tả của ECMA script, có 3 cách tạo và khai báo hàm:

1. Sử dụng hàm tạo của đối tượng Function

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Sử dụng cách khai báo hàm.

function sum(a, b)
{
    return a + b;
}
alert(sum(10, 10)); //Alerts 20;

3. Sử dụng biểu thức hàm

var sum = function(a, b) { return a + b; }
alert(sum(5, 5)); // alerts 10

Với biểu thức hàm ta có thể tạo hàm ở bất cứ đâu chấp nhận biểu thức trong javascript. Ví dụ bạn có thể viết:

//tạo hàm fsum
var fsum=function(a,b){return a+b};
//gọi hàm
sum=fsum(4,5);
//tương đương với
sum=(fsum)(4,5);
//cũng tương đương với.
sum=(function(a,b){return a+b})(4,5);

Chú ý cặp dấu () bao quanh khai báo hàm chỉ đơn giản là để nhóm phần tử hàm bên trong biểu thức.

+ Tính hướng đối tượng trong javascript 

(xem loạt bài JavaScript và lập trình hướng đối tượng)

+ Các kiến thức cơ bản về DOM Elements

(tham khảo trên w3school.com)

OK! Đến đây bạn đã sẵn sàng đi vào bên trong giấc mơ jQuery (=>link mã nguồn). Nhưng đừng vội, việc xông vào mã nguồn của jQuery ngay là việc làm hết sức mạo hiểm. Chính vì vậy tôi đã bỏ chút thời gian tạo ra một môi trường giả lập jQuery tạm gọi là iQuery để giảm sóc cho những nhà thám hiểm trước khi trải nghiệm trong môi trường thật. Và đây là iQuery framework:

(function(window) {
    var iQuery = function(selector) {
                      return new DOMWrapper(selector);
                 };
    DOMWrapper= function(selector) {
                      var elems;
                      if (typeof selector == "string") {
                          elems = document.getElementById(selector);
                      } else {
                          elems = selector;
                      }
                      this.collection = elems[1] ? Array.prototype.slice.call(elems) : [elems];
                      return this;
                };
    iQuery.fn = DOMWrapper.prototype = {
                    aaaaaaa: function() {
                                alert("Aaa!!!");
                                return this;
                             },
                    addStyles: function(styles) {
                                var elems = this.collection;
                                for (var i = 0, l = elems.length; i < l; i++) {
                                   for (var prop in styles) {
                                       elems[i].style[prop] = styles[prop];
                                   }
                                }
                                return this;
                               },
                    click:function(fn) {
                                var elems = this.collection;
                                for (var i = 0, l = elems.length; i < l; i++) {
                                    elems[i].onclick=fn;
                                }
                           }
                         };
     window.$ = iQuery;
     return iQuery;
 })(window);

//Demo sử dụng iQuery
 $('a').addStyles({
      color: 'yellow',
      backgroundColor: 'blue'
 }).aaaaaaa();

$('a').click(function(){alert("Hello iQuery");});

Xem Demo

Hãy quan sát từ ngoài vào trong:
– Đầu tiên là “bức tường” ngăn cách giữa thế giới iQuery và môi trường bên ngoài:

(function(window){
    …
})(window);

Cấu trúc này bản chất là một biểu thức hàm (như trên đã nói), với window là cửa thông giữa môi trường bên ngoài và thế giới iQuery bên trong (hiển nhiên :D).
– Đi qua vách ngăn đó ta bắt gặp một đối tượng mà ở đó mọi giấc mơ bắt đầu:

var iQuery = function(selector) {
                  return new DOMWrapper(selector);
             };

Tại đây, câu “thần chú” selector dùng để triệu hồi các DOM Element được đưa vào và đi qua cỗ máy DOMWrapper, thực chất là nơi mà tất cả các elements được lưu giữ. Lúc này, iQuery chính là một hàm mà giá trị trả về của nó là một đối tượng DOMWrapper.
– Ta hãy xem cấu tạo của DOMWrapper:

 DOMWrapper= function(selector) {
                 var elems;
                 if (typeof selector == "string") {
                        elems = document.getElementById(selector);
                 } else {
                        elems = selector;
                 }
                 this.collection = elems[1] ? Array.prototype.slice.call(elems) : [elems];
                 return this;
             };

Khâu đầu tiên trong DOMWrapper là thực thi câu thần chú selector để thu về các elements. (Việc làm này giống như hàm $ nguyên bản trong phần trước). Sau đó cất giữ toàn bộ chúng vào trong chiếc hòm collection của DOMWrapper.

Tôi khuyên các bạn hãy dừng lại ở đây nếu yếu tim hoặc không đủ can đảm đi tiếp, bởi chỉ một bước nữa thôi là chúng ta sẽ nhìn thấy những “điều khủng khiếp” sẽ xảy đến với các DOM element tội nghiệp đang bị nhốt trong DOMWrapper.collection:

   iQuery.fn = DOMWrapper.prototype = {
                    aaaaaaa: function() {
                                alert("Aaa!!!");
                                return this;
                             },
                    addStyles: function(styles) {
                                var elems = this.collection;
                                for (var i = 0, l = elems.length; i < l; i++) {
                                   for (var prop in styles) {
                                       elems[i].style[prop] = styles[prop];
                                   }
                                }
                                return this;
                               },
                    click:function(fn) {
                                var elems = this.collection;
                                for (var i = 0, l = elems.length; i < l; i++) {
                                    elems[i].onclick=fn;
                                }
                           }
                         };

Vâng cái đoạn này: iQuery.fn = DOMWrapper.prototype thực sự là “thủ đoạn” của iQuery. Nó khai báo một thuộc tính có tên fn của iQuery, nhưng thuộc tính này lại được gán là nguyên mẫu của DOMWrapper. Nhắc lại một chút: trước đó iQuery là hàm triệu hồi DOMWrapper và giờ iQuery.fn là nguyên mẫu của DOMWrapper. Kết quả của mẫu kết cấu này là iQuery có đầy đủ những đặc tính mà DOMWrapper nắm giữ. Chính tại đây “phép thuật” bắt đầu xuất hiện:

– Đầu tiên là tiếng kêu la aaaaaaa, báo hiệu điềm chẳng lành cho các elements trong DOMWrapper.collection.

– Tiếp theo là addStyles, khi phép này được gọi, các element sẽ bị lôi ra và được tô son, chát phấn theo kiểu nghệ thuật “body painting” :D.

– Cuối cùng là click, điều này thật sự gây khủng hoảng cho các element bởi giờ đây chúng có thể bị sai khiến và thực hiện mọi hành động mà các lập trình viên tạo ra. Bởi tham số truyền vào cho click chính là một hàm bất kỳ và nó được gán cho sự kiện onclick của các element.

Vẫn còn một chiến thuật nguy hiểm nữa mà DOMWrapper cài vào, nếu tinh ý bạn sẽ nhận ra ở hai phương thức aaaaaaa và addStyles có đoạn: return this. Đây chính điểm sẽ tạo ra cơn ác mộng liên hồi và dường như không có hồi kết trong môi trường của iQuery:

$('a').addStyles({
     color: 'yellow',
     backgroundColor: 'blue'
 }).aaaaaaa().aaaaaaa().aaaaaaa(); //Aaa!!........

Như bạn thấy, DOMWrapper có thể được coi là “Kẻ đánh cắp giấc mơ” nguy hiểm nhất trong iQuery, và không may nó cũng chính là iQuery!!!

Có lẽ tôi nên dừng lại ở đây, bởi như đã nói, iQuery chỉ là một mô hình thu nhỏ của jQuery, mà nó đã ẩn chứa rất nhiều cạm bẫy và ma thuật. Vậy nên các bạn hãy khởi động thật kỹ trước khi theo đuổi hành trình đến thế giới rộng lớn “jQuery Pandora” – thế giới của những giấc mơ.

Tham khảo:

Mã nguồn jQuery: http://code.jquery.com/jquery-1.7.2.js

http://djdesignerlab.com/2011/05/21/20-jquery-animate-resources-for-developers/

http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf

Câu chuyện về hàm $ trong jQuery

Khi sử dụng JavaScript tương tác với các thành phần của DOM, một trong những thao tác phổ biến nhất đó là tìm kiếm một phần tử của DOM thỏa mãn một điều kiện nào đó.

Ví dụ cách đơn giản nhất để tìm kiếm một element có thuộc tính id:

var el=document.getElementById(“foo”);

Tuy nhiên nếu câu truy vấn của tôi phức tạp hơn: tìm một element có thuộc tính class mang giá trị “blink” v.v…
Hay khó hơn nữa: tìm kiếm một element tuân theo một biểu thức quy tắc nào đó.
Khi đó câu lệnh tìm kiếm không còn đơn giản như trên nữa.

Thông tin nằm trong tài liệu HTML rất phong phú, để khai thác được chúng thì việc tất yếu đối với lập trình viên là phải thiết lập một hàm tìm kiếm thống nhất, đơn giản để đáp ứng mọi nhu cầu tìm kiếm trong chương trình.

Và một trong số những hàm nổi tiếng nhất trong các hàm javascript đã ra đời: hàm $() hay còn gọi là hàm nguyên mẫu (lần đầu tiên xuất hiện trong Prototype.js). Sau đây là phiên bản đơn giản của hàm $:

function $() {
    var elements = new Array();
    for (var i = 0; i < arguments.length; i++) {
        var element = arguments[i];
        if (typeof element == 'string')
            element = document.getElementById(element);
        if (arguments.length == 1)
            return element;
        elements.push(element);
    }
    return elements;
}

// Ví dụ:
var obj1 = document.getElementById('element1');
var obj2 = document.getElementById('element2');
function alertElements() {
    var i;
    var elements = $('a','b','c',obj1,obj2,'d','e');
    for(i=0;i<elements.length;i++) {
         alert(elements[i]);
    }
}

Chạy thử

Rất đẹp! Bạn có thể thấy hàm $ trên có thể nhận một hay nhiều tham số, hoặc truyền vào id của element hay thậm chí là object. Và kết quả luôn trả về các element thỏa mãn.


Một phiên bản đầy đủ hơn của $ function có thể tìm thấy trong JQuery framework:

var jQuery=function( selector, context, rootjQuery ) {
    var match, elem, ret, doc;

    // Handle $(""), $(null), or $(undefined)
    if ( !selector ) {
        return this;
    }

    // Handle $(DOMElement)
    if ( selector.nodeType ) {
        this.context = this[0] = selector;
        this.length = 1;
        return this;
    }

    // The body element only exists once, optimize finding it
    if ( selector === "body" && !context && document.body ) {
        this.context = document;
        this[0] = document.body;
        this.selector = selector;
        this.length = 1;
        return this;
    }

    // Handle HTML strings
    if ( typeof selector === "string" ) {
    // Are we dealing with HTML string or an ID?
    if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
        // Assume that strings that start and end with <> are HTML and skip the regex check
        match = [ null, selector, null ];

    } else {
        match = quickExpr.exec( selector );
    }

    // Verify a match, and that no context was specified for #id
    if ( match && (match[1] || !context) ) {

         // HANDLE: $(html) -> $(array)
        if ( match[1] ) {
             context = context instanceof jQuery ? context[0] : context;
             doc = ( context ? context.ownerDocument || context : document );

             // If a single string is passed in and it's a single tag
             // just do a createElement and skip the rest
             ret = rsingleTag.exec( selector );

             if ( ret ) {
                 if ( jQuery.isPlainObject( context ) ) {
                     selector = [ document.createElement( ret[1] ) ];
                     jQuery.fn.attr.call( selector, context, true );

                 } else {
                     selector = [ doc.createElement( ret[1] ) ];
                 }

             } else {
                 ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
                 selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
             }

             return jQuery.merge( this, selector );

             // HANDLE: $("#id")
         } else {
             elem = document.getElementById( match[2] );

             // Check parentNode to catch when Blackberry 4.6 returns
             // nodes that are no longer in the document #6963
             if ( elem && elem.parentNode ) {
                 // Handle the case where IE and Opera return items
                 // by name instead of ID
                 if ( elem.id !== match[2] ) {
                      return rootjQuery.find( selector );
                 }

                 // Otherwise, we inject the element directly into the jQuery object
                 this.length = 1;
                 this[0] = elem;
             }

             this.context = document;
             this.selector = selector;
             return this;
         }

     // HANDLE: $(expr, $(...))
     } else if ( !context || context.jquery ) {
         return ( context || rootjQuery ).find( selector );

     // HANDLE: $(expr, context)
     // (which is just equivalent to: $(context).find(expr)
     } else {
          return this.constructor( context ).find( selector );
     }

     // HANDLE: $(function)
     // Shortcut for document ready
  } else if ( jQuery.isFunction( selector ) ) {
      return rootjQuery.ready( selector );
  }

  if ( selector.selector !== undefined ) {
       this.selector = selector.selector;
       this.context = selector.context;
  }

   return jQuery.makeArray( selector, this );
}
…
// Expose jQuery to the global object: $
window.jQuery = window.$ = jQuery;

Dòng cuối cùng trong đoạn mã trên thực chất là để “trả lại tên cho em” ($=jQuery). Nếu lướt qua đám comment trong hàm $ này ta có thể thấy các chức năng chính của nó gồm:
// Handle $(“”), $(null), or $(undefined)
// Handle $(DOMElement)
// Handle HTML strings
// HANDLE: $(“#id”)
// HANDLE: $(expr, $(…))
// HANDLE: $(expr, context)
// HANDLE: $(function)

Như vậy hàm $ trong jQuery thực sự là một hàm tìm kiếm đa năng, với tham số đầu vào từ Html string cho đến DOM element hay thậm chí là function. Hàm $ của jQuery còn cung cấp một khả năng truy vấn dựa trên biểu thức CSS selector . Có thể hình dung hàm $ trong jQuery giống như ô tìm kiếm trên trang chủ Google vậy! Chỉ cần search(“keyword”) là có ngay những gì mình cần 🙂

Đến đây, chắc hẳn các bạn lập trình viên sẽ cảm thấy thoải mái với hàm $, không chỉ bởi vì nó là $, mà còn bởi sử dụng nó sẽ tiết kiệm rất nhiều thời gian và đem lại vô số lợi ích thành tiền, thành $ thật sự 😀

Trong các bài viết tới chúng ta sẽ có dịp quay trở lại nói chuyện kỹ hơn về $, cách xài $ v.v… Nếu không muốn chờ đợi, các bạn có thể tham khảo về $ tại đây.

Tham khảo:

Mã nguồn jQuery 1.7.2 : http://code.jquery.com/jquery-1.7.2.js

Những điểm đáng giá trong ECMAScript 5

Lật lại lịch sử của Javascript, ngôn ngữ được Brendan Eich phát triển, lần đầu tiên ngôn ngữ này xuất hiện là vào năm 1995 bởi Sun Microsystem và Netcapse, sau đó một năm, trình duyệt Netcapse phát hành phiên bản hỗ trợ khả năng xử lý javascript làm thay đổi đáng kể bộ mặt của web thời đó. Cuối năm 1996, Microsoft cũng phát triển một phiên bản tương tự JavaScript của Netcapse lấy tên là JScript, nó được tích hợp vào phiên bản trình duyệt Internet Explorer 3.0. Để cuộc chiến trình duyệt không làm ảnh hưởng tới những nhà phát triển và người sử dụng, Netcapse đã giao JavaScript cho tổ chức ECMA quốc tế chuyên về chuẩn hóa, và tài liệu đặc tả tiêu chuẩn cho JavaScript ra đời với mã tài liệu ECMA-262. Tên gọi ECMAScript là một sự thỏa hiệp của các bên liên quan cho chuẩn ngôn ngữ này. Brendan Eich, tác giả của JavaScript, nhận xét ​​rằng “ECMAScript là một tên thương mại không mong muốn, nghe giống như một bệnh ngoài da”. (bệnh Eczema :D)

ECMAScript mặc dù là một tiêu chuẩn, nhưng các hãng phát triển trình duyệt và engine JavaScript luôn đưa thêm vào những thứ ngoài đặc tả, tạo ra sự khác biệt làm đau đầu những nhà phát triển web, những người luôn mong muốn sản phẩm của mình chạy được tốt trên thật nhiều trình duyệt. Cuộc cạnh tranh này cũng khiến cho các mã javascript viết theo chuẩn ECMAScript không phải lúc nào cũng có hiệu năng tốt như nhau trên tất cả các trình duyệt chưa kể việc chúng thậm chí còn cho ra những kết quả khác nhau, rất khó lường.

Giải pháp an toàn khi code javascript là sử dụng các tính năng bám sát đặc tả ECMAScript (với phiên bản phù hợp), hoặc tận dụng triệt để các thư viện JavaScript đã được kiểm định tính tương thích với hầu hết các trình duyệt phổ biến (jQuery là ví dụ điển hình). Với yêu cầu về tính tương thích thì các thư viện như jQuery chắc chắn sẽ không khai thác hết các tính năng mới nhất mà bản thân các trình duyệt hỗ trợ, mà đôi khi những tính năng đó lại hết sức mạnh mẽ và hiệu quả. Vì vậy nếu bạn muốn tận dụng cả tính năng lẫn khả năng tương thích tốt thì việc nghiên cứu kỹ chuẩn ECMAScript là điều tất yếu.

Kể từ khi ra đời đến nay, ECMAScript trải qua nhiều phiên bản, phiên bản gần nhất là ECMAScript 5.1. Tuy nhiên, các phiên bản trình duyệt mới nhất hiện nay cũng chỉ hỗ trợ một phần các đặc tả của ECMAScript 5 (Xem chi tiết)

Trong bài viết này tôi sẽ giới thiệu với các bạn một vài tính năng đáng giá trong ECMAScript 5 mà hầu hết các trình duyệt hỗ trợ. Một trong số đó tiêu biểu là các tính năng bổ sung thêm cho đặc tính hướng đối tượng khi lập trình với JavaScript: Kiểm soát việc kế thừa và tăng tính bao gói cũng như khả năng reflection trên các đối tượng.

Kiểm soát Thuộc tính và cấu hình thuộc tính cho đối tượng

Giờ đây với JavaScript, bạn có thể mô tả thuộc tính của đối tượng một cách chi tiết, kèm theo khả năng kiểm soát lên các thuộc tính này.
Bất cứ thuộc tính nào của đối tượng đều bao gồm:
+ value: giá trị của thuộc tính.
+ get: phương thức thực thi khi giá trị thuộc tính của đối tượng được đọc ra.
+ set: phương thức thực thi khi giá trị thuộc tính của đối tượng bị thay đổi.
+ Các mô tả, cấu hình thêm cho thuộc tính (property descriptor):
– writable: mặc định là true, nếu giá trị là false, giá trị của thuộc tính sẽ không thể thay đổi.
– configurable: mặc định là true, nếu giá trị là false, bất cứ thay đổi nào lên writable, configurable, enumerable đều không thể thực hiện.
– enumerable: mặc định là true, nếu giá trị là true, bạn có thể lấy ra thuộc tính này của đối tượng bằng vòng lặp đơn giản:  for (var prop in obj){…} .

Sau đây là ví dụ về cách thức định nghĩa và cấu hình thuộc tính cho đối tượng trong ECMAScript 5:

var obj = {};
Object.defineProperty( obj, "value", {
    value: true,
    writable: false,
    enumerable: true,
    configurable: true
});
(function(){
    var name = "John";

    Object.defineProperty( obj, "name", {
        get: function(){ return name; },
        set: function(value){ name = value; }
    });
 })();
 print( obj.value )
 // true
 print( obj.name );
 // John
 obj.name = "Ted";
 print( obj.name );
 // Ted
 for ( var prop in obj ) {
     print( prop );
 }
 // value
 // name
 obj.value = false; // Exception if in strict mode
 Object.defineProperty( obj, "value", {
     writable: true,
     configurable: false
 });
 obj.value = false;
 print( obj.value );
 // false
 delete obj.value; // Exception

Trong ví dụ trên ta thấy ECMAScript sử dụng phương thức quan trọng: Object.defineProperty( obj, prop, desc ) trong việc định nghĩa thuộc tính của đối tượng.
Chú ý đoạn mã sau:

(function(){
    var name = "John";

    Object.defineProperty( obj, "name", {
        get: function(){ return name; },
        set: function(value){ name = value; }
    });
})();

Đoạn này khai báo thuộc tính name của đối tượng obj, sử dụng một biến cục bộ name=”John” để lưu trữ giá trị của thuộc tính, và hai phương thức get, set kiểm soát việc truy xuất vào biến cục bộ này (hay nói cách khác là kiểm soát truy xuất vào giá trị của thuộc tính name). Cách khai báo này khá giống với khai báo thuộc tính trong C#.

Kiểm soát việc kế thừa trong JavaScript

Đối tượng Object được bổ sung thêm hai phương thức giúp kiểm soát việc kế thừa đó là:
Object.preventExtensions( obj ): Phương thức này khi thực thi sẽ ngăn cản khả năng mở rộng của đối tượng obj.
Object.isExtensible( obj ): Phương thức kiểm tra một đối tượng có khả năng mở rộng hay không.

Ví dụ:

var obj = {};
obj.name = "John";
print( obj.name );
// John
print( Object.isExtensible( obj ) );
// true
Object.preventExtensions( obj );
obj.url = "http://ejohn.org/"; // Exception in strict mode
print( Object.isExtensible( obj ) );
// false

Việc kiểm soát kế thừa còn có thể được áp dụng dựa trên các property descriptor. ECMAScript cung cấp thêm một số phương thức tiện ích cho việc này:

Object.seal( obj )
Object.isSealed( obj )

Phương thức seal sử dụng đặc tính configurable để ngăn cản việc thay đổi bản thân các property descriptor.

Object.seal = function( obj ) {
    var props = Object.getOwnPropertyNames( obj );
    for ( var i = 0; i < props.length; i++ ) {
        var desc = Object.getOwnPropertyDescriptor( obj, props[i] );
        desc.configurable = false;
        Object.defineProperty( obj, props[i], desc );
    }

    return Object.preventExtensions( obj );
 };

Object.freeze( obj )

Object.isFrozen( obj )

Phương thức freeze sử dụng cả writable và configurable giúp “đóng băng” đối tượng của bạn, đối tượng sau khi bị đóng băng không thể sửa đổi giá trị cũng như các mô tả thuộc tính.

Object.freeze = function( obj ) {
     var props = Object.getOwnPropertyNames( obj );

     for ( var i = 0; i < props.length; i++ ) {
          var desc = Object.getOwnPropertyDescriptor( obj, props[i] );

          if ( "value" in desc ) {
               desc.writable = false;
          }

          desc.configurable = false;
          Object.defineProperty( obj, props[i], desc );
     }

     return Object.preventExtensions( obj );
};

Tóm lại

Những tính năng mới trong ECMAScript trên thực tế còn khá nhiều, tuy nhiên nhìn chung hầu hết tập trung vào việc bổ sung thêm các đặc tính hướng đối tượng trong ngôn ngữ. Đây cũng chính là xu hướng phát triển trong các phiên bản tiếp theo của ECMAScript.

Tham khảo
http://en.wikipedia.org/wiki/ECMAScript
http://en.wikipedia.org/wiki/Comparison_of_layout_engines_(ECMAScript)
http://markcaudill.com/2009/04/javascript-new-features-ecma5/
http://ejohn.org/blog/ecmascript-5-objects-and-properties/
http://www.robertnyman.com/javascript/index.html
http://www.robertnyman.com/javascript/javascript-getters-setters.html
http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf

Phụ lục
Ý nghĩa chú thích:// Exception if in strict mode
Strict mode cũng là tính năng mới trong ECMAScript 5 cho phép ngăn cản một đoạn mã lệnh không an toàn được thực thi bằng cách tung ra các ngoại lệ. Để bật tính năng strict mode chỉ cần đưa dòng mã đơn giản như sau:

"use strict";

nếu muốn bật tính năng này trên toàn bộ mã script. Hoặc có thể đặt trong hàm, ví dụ:

function imStrict(){
 "use strict";
 // ... your code ...
 }

Khi đó toàn bộ mã lệnh trong hàm imStrict được bật chế độ strict mode.

Nền tảng Android – bức tranh nhìn từ trên cao


Vài nét về quá trình ra đời

Ngày nay, điện thoại di động hiện diện ở khắp mọi nơi. Một người có thể không có máy vi tính nhưng gần như không thể không có điện thoại di động. Điện thoại di động ngày càng thông minh và chúng đang dần thay thế các thiết bị di động cá nhân khác như: đồng hồ, máy tính cầm tay, máy ảnh du lịch, máy nghe nhạc MP3, hoặc các thiết bị có khả năng kết nối internet khác. Rõ ràng thị trường di động là một miếng bánh khổng lồ mà không ai có thể một mình nuốt gọn. Các nhà phát triển cũng cảm thấy sức hấp dẫn mà miếng bánh tỏa ra. Nhưng việc phát triển phần mềm cho các thiết bị di động chưa bao giờ là công việc đơn giản. Có hàng loạt các vấn đề mà các nhà phát triển phần mềm cho thiết bi di động trước kia phải đối mặt:

  • Không có mã nguồn của các nền tảng để giúp họ hình dung ra được cơ chế hoạt động đằng sau đó.
  • Giá thành để phát triển một sản phẩm cao do phải trả phí mua bản quyền API hoặc công cụ phát triển.
  • Vướng phải những ràng buộc hoặc giới hạn nhất định về tính năng khi phát triển kể cả khi họ có giấy phép phát triển.
  • Không có một bộ công cụ phát triển (SDK) nào thực sự dễ dàng cho việc xây dựng ứng dụng di động.
  • Không có một cơ chế phân phối sản phẩm hiệu quả đến người dùng cuối cũng như cách thức cài đặt phần mềm đơn giản lên thiết bị của họ.

Vậy chúng ta cần những gì để tạo ra một thiết bị di động tốt hơn, chi phí phát triển rẻ hơn và tạo ra nhiều lợi ích cho người dùng hơn? Đó chính là câu hỏi mà liên minh Open Handset Alliance gồm trên 40 hãng từ nhiều lĩnh vực liên quan do Google khởi sướng muốn giải đáp. Và câu trả lời của họ chính là hệ sinh thái Android.

Hình 1: Các thành viên trong liên minh OHA

Vào tháng 10-2008, thiết bị đầu tiên chạy Android được Google và HTC phối hợp phát triển đã ra đời với tên gọi G1. Từ đó đến nay, Android đã trải qua nhiều phiên bản với nhiều cải tiến, đã có vô số các thiết bị chạy nền tảng này được tung ra thị trường với mức độ đa dạng chưa từng thấy. Hiện Samsung đang là hãng sản xuất và phân phối hàng đầu các thiết bị chạy Android trên toàn cầu.
Nền tảng Android về cơ bản đáp ứng được những đòi hỏi của OHA:

  • Đó là một nền tảng mở vì vậy các nhà phát triển dễ dàng đào sâu tìm hiểu cũng như dễ dàng tạo ra những thành phần tích hợp hoặc tùy biến cho nó.
  • Ngoài ra cộng đồng phát triển Android còn được hỗ trợ bộ công cụ phát triển (SDK) hoàn chỉnh và dễ sử dụng.
  • Kênh phân phối sản phẩm đến tay người sử dụng rất phong phú cả về phần cứng lẫn phần mềm.

Để đạt được những điều đó yêu cầu Android phải có một kiến trúc mềm dẻo, khai thác được tối đa sức mạnh phần cứng cũng như sự thân thiện trong giao diện người dùng. Tuy vậy, Android không phải là không có những nhược điểm so với những nền tảng cạnh tranh khác, tuy nhiên trong phạm vi bài viết này ta sẽ chỉ mô tả bức tranh về Android như là nó vốn thế.

Nền tảng Android

Android được phát triển dựa trên nhân của Linux nên kế thừa được khả năng quản lý tài nguyên tốt từ hệ thống này. Toàn bộ các ứng dụng được thực thi bởi Dalvik VM, một máy ảo tượng tự như JVM nhưng đã được tối ưu cho thiết bị di động. Các ứng dụng được phát triển trên một framework linh hoạt giúp khai thác các tài nguyên của hệ thống. Hình dưới đây cho thấy bức tranh khái quát về các khối phần mềm trong toàn bộ nền tảng Android.

Hình 2: Các khối cơ bản trong nền tảng Android

Điểm đặc biệt đối với các ứng dụng Android là chúng được phát triển trên cùng một cấp, do vậy khái niệm phần mềm hệ thống và phần mềm thông thường chỉ mang tính chất tương đối. Bất cứ một ứng dụng hệ thống cơ bản nào (như ứng dụng gọi điện, nhắn tin, trình duyệt v.v…) cũng có thể bị thay thế bằng các ứng dụng khác tương tự.

Ngôn ngữ phát triển và cách thức biên dịch

Ngôn ngữ lập trình chính để viết các ứng dụng Android là Java, vì vậy nếu bạn có hứng thú phát triển ứng dụng cho nền tảng này thì hãy đầu tư thời gian vào Java căn bản trước khi bắt tay vào nghiên cứu nó.
Các ứng dụng Android có thể được phân làm hai loại căn cứ vào ngôn ngữ phát triển và cách thức biên dịch:

  • Ứng dụng Dalvik: ứng dụng được viết bằng ngôn ngữ lập trình Java, sử dụng các công cụ trong bộ SDK (Google) và JDK (Oracle) biên dịch và đóng gói thành file .apk trước khi cài đặt vào thiết bị.

Hình 4: Quá trình biên dịch từ mã nguồn Java của loại ứng dụng Dalvik

  • Ứng dụng Native: bên cạnh sử dụng mã nguồn Java, ta có thể sử dụng mã nguồn C/C++ kết hợp để xây dựng ứng dụng nhằm tối ưu hóa và tăng tốc độ thực thi hoặc khai thác các thư viện cấp thấp. Để biên dịch được ứng dụng loại này ngoài bộ SDK bạn phải cần thêm bộ NDK.

Hình 5: Quá trình biên dịch của ứng dụng Native

Mặc dù các ứng dụng Android được phát triển và biên dịch thông qua JDK nhưng không phải tất cả mọi thứ trong JDK đều có thể sử dụng được. Dưới đây là hình ảnh tham khảo về API trong JDK và mức độ được sử dụng trong Android:

Hình 6: Một phần Core Java trong Android

Kiến trúc ứng dụng

Ứng dụng Android được xây dựng từ nhiều thành phần có thể coi là những viên gạch cơ bản trong ứng dụng. Những viên gạch này bao gồm nhiều loại có thể chia thành ba nhóm căn cứ vào mục đích sử dụng:

  1. Những thành phần tạo giao diện người dùng: Activity, View/Widget
  2. Những thành phần cung cấp dịch vụ và tích hợp: Service, Broadcast Receiver/Intent.
  3. Những thành phần cung cấp dữ liệu, tài nguyên và cấu hình của ứng dụng: Content Provider, Resource, ApplicationManifest.

Hình 7: Các thành phần cơ bản trong một ứng dụng Android

1. Những thành phần tạo giao diện người dùng

  • Activity: đây chính là không gian cửa sổ nơi mà mọi thứ được hiển thị ra. Tuy nhiên trên màn hình nhỏ thì mỗi Activity sẽ chiếm trọn màn hình thiết bị, người dùng có thể di chuyển qua lại giữa các Activity thông qua thao tác điều hướng. Về mặt kiến trúc Activity tương đương với ViewModel trong kiến trúc MVVM (Model – View – ViewModel) rất quen thuộc trong các nền tảng lập trình khác như Windows Phone, WPF, JavaFX v.v… Với kiến trúc như vậy mỗi Activity được kết hợp với một Layout mô tả bố cục và các thành phần hiển thị trên đó (các View). Những mô tả này sử dụng ngôn ngữ XML và được đặt trong thư mục /res (xem hình 10).
  • View: đây là các thành phần hiển thị trên Activity như Button, CheckBox v.v… nó được thiết kế theo mô hình Composite giống như tất cả các hệ thống GUI khác. Sơ đồ sau đây thể hiện các lớp cơ bản để dựng giao diện trong Android:

Hình 8: Sơ đồ phân cấp của View

  • Widget: về bản chất đây là một ViewGroup, có khả năng hiển thị trên màn hình Home các thành phần cơ bản trong hệ thống View. Nhưng sự khác biệt này làm cho màn hình Home của Android trở nên rất sinh động và hữu dụng.

Hình 9: Hình ảnh về các widget trên màn hình Home

2. Những thành phần cung cấp dịch vụ và tích hợp

Android cung cấp một loại các thành phần nghiệp vụ cho phép các nhà phát triển khai thác các tính năng phần cứng cũng như các dịch vụ cơ bản của hệ thống để tạo nên những ứng dụng phong phú. Có thể chỉ ra một số thành phần cơ bản như:

  • Service: lập trình viên có thể khai thác các dịch vụ có sẵn hoặc tạo ra các dịch vụ chạy ngầm định khác trong hệ thống. Các dịch vụ này có thể được truy cập từ bất cứ ứng dụng nào.
  • Broadcast Receiver: đây là thành phần thú vị giúp ứng dụng của bạn có khả năng phản ứng lại với các sự kiện trong hệ thống hoặc liên quan đến việc xử lý một dữ liệu nào đó. Nhờ nó các ứng dụng trong Android tạo nên một môi trường ứng dụng thống nhất, người dùng thay vì có cảm giác “đang chạy một ứng dụng để làm việc gì đó” sẽ thấy rằng mình “đang làm một việc gì đó trong ngữ cảnh với một ứng dụng phù hợp”.
  • Intent: đây là thành phần cơ bản trong cơ chế quản lý giao tiếp của Android, nó cho phép chuyển đổi, truyền và nhận thông điệp giữa các Activity.

3. Những thành phần cung cấp dữ liệu, tài nguyên và cấu hình ứng dụng

  • Content Provider: có thể hình dung Content Provider giống như một database server thu nhỏ cung cấp khả năng truy vấn vào các nguồn dữ liệu khác nhau (phổ biến là SQLite) thông qua một URI (ví dụ như các dữ liệu Contact, Message v.v…). Kết quả truy vấn của Content Provider luôn trả về đối tượng Cursor (có thể hình dung nó như là ResultSet trong JDBC).
  • Resource: tài nguyên có thể sử dụng trong ứng dụng rất phong phú bao gồm hình ảnh, âm thanh, văn bản, văn bản đa ngôn ngữ, layout, xml v.v… tất cả chúng nằm trong thư mục /res. Mọi thứ trong /res không đơn giản chỉ là một phần được đóng gói đi cùng ứng dụng mà chúng có thể được thao tác và truy xuất từ trong code một cách dễ dàng ở thời điểm chạy cũng như khi đang biên địch (thông qua một lớp được sinh tự động nhằm tối ưu hóa việc truy xuất tài nguyên có tên là R.java).
  • AndroidManifest.xml: đây là file cấu hình cho mỗi ứng dụng android, nó chứa các thông tin cơ bản của ứng dụng cho hệ điều hành như điểm truy xuất, các quyền hạn, các thông tin cài đặt, cấu hình v.v…

Hình 10: Cấu trúc của một Android Project trong Eclipse

Bắt đầu khám phá

Bài viết đến đây mới chỉ là sự khởi đầu, ta vừa nhìn xuống “thành phố Android” từ trên máy bay, khi đó ta chỉ cảm nhận được những mảng, khối cơ bản mà không thấy chi tiết. Nhưng hy vọng với từng đó đã giúp bạn định vị được rằng để tiếp tục cuộc hành trình khám phá “thành phố” này bạn nên bắt đầu từ đâu và như thế nào. Chúc các bạn có một chuyến đi vui vẻ!

Nguyễn Ngọc Tú

Tài nguyên

ebook: Android In Practice by Charlie Collins & Michael Galpin & Matthias Kaeppler
http://www.ibm.com/developerworks/library/os-android-devel/
http://software.intel.com/en-us/articles/android-application-development-and-optimization-on-the-intel-atom-platform
http://vladnevzorov.com/2011/04/30/android-application-architecture-part-ii-architectural-styles-and-patterns/
http://apcmag.com/whats-android.htm
https://vkroz.wordpress.com/category/android/