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:
[sourcecode language=”javascript”]
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
[/sourcecode]
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:
[sourcecode language=”javascript”]
(function(){
var name = "John";
Object.defineProperty( obj, "name", {
get: function(){ return name; },
set: function(value){ name = value; }
});
})();
[/sourcecode]
Đ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ụ:
[sourcecode language=”javascript”]
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
[/sourcecode]
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.
[sourcecode language=”javascript”]
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 );
};
[/sourcecode]
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.
[sourcecode language=”javascript”]
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 );
};
[/sourcecode]
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:
[sourcecode language=”javascript”]
"use strict";
[/sourcecode]
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ụ:
[sourcecode language=”javascript”]
function imStrict(){
"use strict";
// … your code …
}
[/sourcecode]
Khi đó toàn bộ mã lệnh trong hàm imStrict được bật chế độ strict mode.
Người dịch rất công phu để tìm hiểu. Và nếu dùng những câu ngắn hơn, dịch theo ý hiểu sẽ dễ hiểu hơn cho người đọc.