Loading...
Hiển thị các bài đăng có nhãn javascript. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn javascript. Hiển thị tất cả bài đăng

Không thể nào bỏ xuống được khi đọc. Cuốn sách này làm tôi thay đổi quan điểm của mình về Apress - một nhà sách được viết bởi mấy tác giả củ chuối của Ấn Độ, toàn là mấy cái dễ và ko có gì chuyên sâu cả.
Chương này tác giả đánh giá là chương quan trọng nhất trong cuốn sách, bàn về references, scope, closure, context.
Thuật ngữ object-oriented javascript có vẻ thừa thải vì javascript language toàn bộ là object, có cái nào khác object đâu. Nhưng tác giả muốn nhấn mạnh ở đây vì đa số cách sử dụng toàn là functionally, hiểu sai khái niệm và mơ hồ trong việc sử dụng object của hầu hết các developer.
References
Reference là 1 pointer trỏ đến location thật của object. Nếu 2 object có cùng 1 reference thì sửa cái này cái kia cũng bị thay đổi theo (cái này bình thường). Tuy nhiên, một số cái ta có thể ta không để ý:
* Javascript không có khái niệm reference đến 1 reference (Perl có), mà object khi reference sẽ reference đến một object thật sự. Do đó nếu object ban đầu trỏ đến một đối tượng khác thì các object kia reference đến nó sẽ không bị ảnh hưởng.
var items = new Array("one", "two", "three");
// itemsRef reference to item
var itemsRef = items;
// set items to equal a new object
items = new Array("four", "five");
alert(items != itemsRef);
* Chú ý một số tác vụ modify object có thể sinh ra một object mới (như concatenate string trong js)
var item = "test";
var itemRef = item;
item += "ing";
// do tác vụ cộng chuỗi làm cho object item trỏ đến một đối tượng khác, không còn là "test" nữa
alert(item != itemRef);
Scope
Scope được tính bên trong function, không phải bên trong block (vd while, if, for). Trong js, mỗi function được execute như là một method của một object.
Nếu ta khai báo biến và function khơi khơi, không chỉ định 1 scope nào hết thì nó thuộc về global scope, thật ra là thuộc về scope của object window. Xem ví dụ sau:
var foo = "test";
function test() {
var foo = "tele";
}
test();
alert(foo == "tele"); // return false;
do biến foo bên trong hàm test chỉ thuộc scope của hàm test nên nó không làm ảnh hưởng đến biến toàn cục foo bên ngoài.
Closures
Phần closure tác giả viết không hay như trong cuốn Asp.net Ajax in Action (chương 3 - javascript for ajax developers).
Closures là cách thức một inner function có thể tham khảo đến các biến trong một function cha, ngay cả khi function cha đó đã bị terminated. Xem ví dụ sau
function parent(arg) {
var parentVar = "I am a variable of parent";
function child() {
alert(arg + parentVar);
}
child();
}
parent("hello, "); // alert message hello, I am a variable of parent
Hàm child trong ví dụ trên gọi là nested function, được khai báo bên trong hàm cha. Ta thấy nó có thể truy xuất các biết định nghĩa bên ngoài function body của nó. Vấn đề này bình thường. Chuyện gì xảy ra nếu ta thay đổi như sau:
function parent2(arg) {
var parentVar = "I am a variable of parent";
function child() {
alert(arg + parentVar);
}
return child;
}
var theChild = parent2("yes, ");
theChild(); // alert message - yes, I am a variable of parent
Ta thấy rõ ràng sau khi hàm parent2 của nó được gọi và terminated, các variables của nó vẫn còn tồn tại và có thể được truy xuất bởi inner function. Đây gọi là closure.
Context
Là object vùng code mà object đang hoạt động bên trong. Biến this luôn reference đến object này (trong global context, biến this chỉ đến window object). Xem ví dụ sau:
var obj = {
yes: function() {
// this == obj
this.value = true;
},
no: function() {
this.val = false;
}
}
alert(obj.val == null); // ban đầu chưa gọi hàm nên biến val chưa tồn tại
obj.yes();
alert(obj.val == true);
// ta trỏ hàm no của object window vô hàm no của object obj
window.no = obj.no;
window.no(); // hàm no sẽ được execute trong context của window object
alert(obj.val == true); // do đó val của obj không bị thay đổi
alert(window.val == false); // hiển nhiên
Javascript cung cấp một cách set context vào trong function khi gọi hàm, thông qua hàm call. Ví dụ:
function changeColor(color) {
this.style.color = color;
}
var main = document.getElementById("main");
changeColor.call(main, "black"); // this bây giờ trỏ vô biến main
Object Creation
Phần này thấy đơn giản nhưng cũng có nhiều điều thú vị. Trong javascript, không có khái niệm class (nên không thể tạo object với một kiểu class nào đó), chỉ có object. Object này có thể tạo ra object kia và thừa kế từ các object khác (gọi là prototypal inheritance)
Để tạo một object có 1 kiểu nào đó, javascript cung cấp phương pháp khởi tạo function như là một object. Xem ví dụ sau:
function User(name) {
this.name = name;
}
var me = new User("my name"); // khởi tạo object của function đó qua toán tử new (constructor)
alert(me.name == "my name"); // name bây giờ trở thành property của object me, this pointer trỏ vào me
alert(me.constructor == User); // property constructor của me bây giờ chính là User
Ví dụ này chứng tỏ:
* Mỗi object đều có constructor property
* Constructor property trỏ vào function nào đã tạo ra nó.
Chương này tác giả đánh giá là chương quan trọng nhất trong cuốn sách, bàn về references, scope, closure, context.
Thuật ngữ object-oriented javascript có vẻ thừa thải vì javascript language toàn bộ là object, có cái nào khác object đâu. Nhưng tác giả muốn nhấn mạnh ở đây vì đa số cách sử dụng toàn là functionally, hiểu sai khái niệm và mơ hồ trong việc sử dụng object của hầu hết các developer.
References
Reference là 1 pointer trỏ đến location thật của object. Nếu 2 object có cùng 1 reference thì sửa cái này cái kia cũng bị thay đổi theo (cái này bình thường). Tuy nhiên, một số cái ta có thể ta không để ý:
* Javascript không có khái niệm reference đến 1 reference (Perl có), mà object khi reference sẽ reference đến một object thật sự. Do đó nếu object ban đầu trỏ đến một đối tượng khác thì các object kia reference đến nó sẽ không bị ảnh hưởng.
var items = new Array("one", "two", "three");
// itemsRef reference to item
var itemsRef = items;
// set items to equal a new object
items = new Array("four", "five");
alert(items != itemsRef);
* Chú ý một số tác vụ modify object có thể sinh ra một object mới (như concatenate string trong js)
var item = "test";
var itemRef = item;
item += "ing";
// do tác vụ cộng chuỗi làm cho object item trỏ đến một đối tượng khác, không còn là "test" nữa
alert(item != itemRef);
Scope
Scope được tính bên trong function, không phải bên trong block (vd while, if, for). Trong js, mỗi function được execute như là một method của một object.
Nếu ta khai báo biến và function khơi khơi, không chỉ định 1 scope nào hết thì nó thuộc về global scope, thật ra là thuộc về scope của object window. Xem ví dụ sau:
var foo = "test";
function test() {
var foo = "tele";
}
test();
alert(foo == "tele"); // return false;
do biến foo bên trong hàm test chỉ thuộc scope của hàm test nên nó không làm ảnh hưởng đến biến toàn cục foo bên ngoài.
Closures
Phần closure tác giả viết không hay như trong cuốn Asp.net Ajax in Action (chương 3 - javascript for ajax developers).
Closures là cách thức một inner function có thể tham khảo đến các biến trong một function cha, ngay cả khi function cha đó đã bị terminated. Xem ví dụ sau
function parent(arg) {
var parentVar = "I am a variable of parent";
function child() {
alert(arg + parentVar);
}
child();
}
parent("hello, "); // alert message hello, I am a variable of parent
Hàm child trong ví dụ trên gọi là nested function, được khai báo bên trong hàm cha. Ta thấy nó có thể truy xuất các biết định nghĩa bên ngoài function body của nó. Vấn đề này bình thường. Chuyện gì xảy ra nếu ta thay đổi như sau:
function parent2(arg) {
var parentVar = "I am a variable of parent";
function child() {
alert(arg + parentVar);
}
return child;
}
var theChild = parent2("yes, ");
theChild(); // alert message - yes, I am a variable of parent
Ta thấy rõ ràng sau khi hàm parent2 của nó được gọi và terminated, các variables của nó vẫn còn tồn tại và có thể được truy xuất bởi inner function. Đây gọi là closure.
Context
Là object vùng code mà object đang hoạt động bên trong. Biến this luôn reference đến object này (trong global context, biến this chỉ đến window object). Xem ví dụ sau:
var obj = {
yes: function() {
// this == obj
this.value = true;
},
no: function() {
this.val = false;
}
}
alert(obj.val == null); // ban đầu chưa gọi hàm nên biến val chưa tồn tại
obj.yes();
alert(obj.val == true);
// ta trỏ hàm no của object window vô hàm no của object obj
window.no = obj.no;
window.no(); // hàm no sẽ được execute trong context của window object
alert(obj.val == true); // do đó val của obj không bị thay đổi
alert(window.val == false); // hiển nhiên
Javascript cung cấp một cách set context vào trong function khi gọi hàm, thông qua hàm call. Ví dụ:
function changeColor(color) {
this.style.color = color;
}
var main = document.getElementById("main");
changeColor.call(main, "black"); // this bây giờ trỏ vô biến main
Object Creation
Phần này thấy đơn giản nhưng cũng có nhiều điều thú vị. Trong javascript, không có khái niệm class (nên không thể tạo object với một kiểu class nào đó), chỉ có object. Object này có thể tạo ra object kia và thừa kế từ các object khác (gọi là prototypal inheritance)
Để tạo một object có 1 kiểu nào đó, javascript cung cấp phương pháp khởi tạo function như là một object. Xem ví dụ sau:
function User(name) {
this.name = name;
}
var me = new User("my name"); // khởi tạo object của function đó qua toán tử new (constructor)
alert(me.name == "my name"); // name bây giờ trở thành property của object me, this pointer trỏ vào me
alert(me.constructor == User); // property constructor của me bây giờ chính là User
Ví dụ này chứng tỏ:
* Mỗi object đều có constructor property
* Constructor property trỏ vào function nào đã tạo ra nó.
Nguồn :-Dzinh on tech [Chapter 2 - Prof Javascript Techniques (Apress)]

The Execution Context
Execution context là một khái niệm trừu tượng, được đặc tả bởi ECMSScript specification (ECMA 262 3rd edition). Nó có thể được xem như là các objects cùng với những properties (nhưng không phải là public properties).
Tất cả các javascript code được executed trong một execution context. Global code (code được execute inline, hoặc trong JS file, hoặc HTML page...) được execute trong global execution context và mỗi một lời gọi hàm sẽ có một conetxt object đi kèm với nó. Code được execute bằng hàm eval cũng có một execution context riêng, nhưng vì eval không thường được sử dụng bởi javascript programmer nên nó sẽ không được discuss ở đây. (các bạn có thể xem đặc tả của nó trong section 10.2 của ECMA 262)
Khi một javascript function được gọi, nó bước vào một execution context. Nếu một function khác được gọi (hoặc gọi lại chính nó theo cách gọi đệ quy) thì một execution context mới sẽ được tạo ra và lời gọi hàm sẽ enter vào context đó cho đến khi nó trả ra kết quả. Việc này dẫn đến javascript code hình thành nên một stack các execution context trong quá trình gọi.
Khi một execution context được tạo ra, một số việc sẽ xảy ra theo một thứ tự được định nghĩa trước. Đầu tiên, trong execution context của một function, một "Activation" object được tạo ra. Activation object này là một cơ chế đặc tả khác. Nó có thể được xem như là một object vì nó có các properties có thể truy xuất được. Nhưng nó không phải là một object bình thường vì nó không có prototype và không thể được reference trực tiếp bằng javascript code.
Bước tiếp theo của việc tạo ra execution context cho một lời gọi hàm là việc tạo ra argument object, là một object giống như array với các index là các số interger tương ứng với thứ tự của các parameter được truyền vào. Nó cũng có length và callee property. Một property của Activation object được tạo ra với tên là "arguments" và trỏ đến object này.
Bước tiếp theo execution context được gán vào một scope. Scope bao gồm một danh sách (hoặc một dây - chain) các objects. Mỗi function object có một property bên trong tên là [[scope]] và nó cũng chứa một danh sách các object. Scope gán cho execution context cho một function là một danh sách được tham khảo bởi [[scope]] property của function object đó với một chút thay đổi là Activation object sẽ được thêm vào đầu tiên của danh sách.
Quá trình khởi tạo các variables diễn ra bằng cách sử dụng một object gọi là "Variable" object (tham khảo ECMA 262 phần khởi tạo biến 10.1.3). Thật sự, Activation object và Variable object là một (!!!). Các properties được đặt tên cho Variable object được tạo ra cho mỗi parameters của function, và nếu các arguments của lời gọi hàm tương ứng với các các parameters này, giá trị của chúng sẽ được gán cho các properties của Variable object (các giá trị default sẽ là undefined). Các inner function được định nghĩa bên trong sẽ tạo ra các function objects và được gán thành các properties của Variable object với tên tương ứng là tên của các inner function. Bước cuối cùng trong quá trình khởi tạo biến là tạo ra các properties với tên tương ứng với các local variables được khai báo bên trong function.
Các properties được tạo trên Variable object tương ứng với các biến cục bộ được khai báo bên trong hàm ban đầu sẽ được gán giá trị là undefined, sau đó việc khởi tạo thật sự sẽ diễn ra trong quá trình evaluation của assignment expression bên trong thân hàm.
Sự kiện Activation object - cùng với property arguments của nó - và Variable object - cùng với các properties có tên tương ứng các local variables - cùng chỉ đến một object cho phép arguments được đối xử như là biến local của function.
Cuối cùng, nếu một giá trị được gán đang tham khảo đến một object mà sau đó các tác vụ truy xuất property có prefix là keyword this, thì nó sẽ dùng giá trị của object đó. Nếu không có từ khóa this hoặc giá trị được gán là null (tức là không nằm trong một object nào đó), nó sẽ refer đến global object.
Global execution context có một vài khác biệt nhỏ vì nó không có arguments nên nó không cần phải có một Activation object tham khảo đến nó. Global execution context thật sự cần một scope và scope chain của nó chỉ chứa một object, đó là global object. Trong quá trình duyệt để khởi tạo biến của global object, nó sẽ được dùng như một Variable object, do đó các biến toàn cục (hoặc các hàm top-level function) sẽ được tạo thành các properties với cùng tên, giống như mô tả ở trên.
Execution context là một khái niệm trừu tượng, được đặc tả bởi ECMSScript specification (ECMA 262 3rd edition). Nó có thể được xem như là các objects cùng với những properties (nhưng không phải là public properties).
Tất cả các javascript code được executed trong một execution context. Global code (code được execute inline, hoặc trong JS file, hoặc HTML page...) được execute trong global execution context và mỗi một lời gọi hàm sẽ có một conetxt object đi kèm với nó. Code được execute bằng hàm eval cũng có một execution context riêng, nhưng vì eval không thường được sử dụng bởi javascript programmer nên nó sẽ không được discuss ở đây. (các bạn có thể xem đặc tả của nó trong section 10.2 của ECMA 262)
Khi một javascript function được gọi, nó bước vào một execution context. Nếu một function khác được gọi (hoặc gọi lại chính nó theo cách gọi đệ quy) thì một execution context mới sẽ được tạo ra và lời gọi hàm sẽ enter vào context đó cho đến khi nó trả ra kết quả. Việc này dẫn đến javascript code hình thành nên một stack các execution context trong quá trình gọi.
Khi một execution context được tạo ra, một số việc sẽ xảy ra theo một thứ tự được định nghĩa trước. Đầu tiên, trong execution context của một function, một "Activation" object được tạo ra. Activation object này là một cơ chế đặc tả khác. Nó có thể được xem như là một object vì nó có các properties có thể truy xuất được. Nhưng nó không phải là một object bình thường vì nó không có prototype và không thể được reference trực tiếp bằng javascript code.
Bước tiếp theo của việc tạo ra execution context cho một lời gọi hàm là việc tạo ra argument object, là một object giống như array với các index là các số interger tương ứng với thứ tự của các parameter được truyền vào. Nó cũng có length và callee property. Một property của Activation object được tạo ra với tên là "arguments" và trỏ đến object này.
Bước tiếp theo execution context được gán vào một scope. Scope bao gồm một danh sách (hoặc một dây - chain) các objects. Mỗi function object có một property bên trong tên là [[scope]] và nó cũng chứa một danh sách các object. Scope gán cho execution context cho một function là một danh sách được tham khảo bởi [[scope]] property của function object đó với một chút thay đổi là Activation object sẽ được thêm vào đầu tiên của danh sách.
Quá trình khởi tạo các variables diễn ra bằng cách sử dụng một object gọi là "Variable" object (tham khảo ECMA 262 phần khởi tạo biến 10.1.3). Thật sự, Activation object và Variable object là một (!!!). Các properties được đặt tên cho Variable object được tạo ra cho mỗi parameters của function, và nếu các arguments của lời gọi hàm tương ứng với các các parameters này, giá trị của chúng sẽ được gán cho các properties của Variable object (các giá trị default sẽ là undefined). Các inner function được định nghĩa bên trong sẽ tạo ra các function objects và được gán thành các properties của Variable object với tên tương ứng là tên của các inner function. Bước cuối cùng trong quá trình khởi tạo biến là tạo ra các properties với tên tương ứng với các local variables được khai báo bên trong function.
Các properties được tạo trên Variable object tương ứng với các biến cục bộ được khai báo bên trong hàm ban đầu sẽ được gán giá trị là undefined, sau đó việc khởi tạo thật sự sẽ diễn ra trong quá trình evaluation của assignment expression bên trong thân hàm.
Sự kiện Activation object - cùng với property arguments của nó - và Variable object - cùng với các properties có tên tương ứng các local variables - cùng chỉ đến một object cho phép arguments được đối xử như là biến local của function.
Cuối cùng, nếu một giá trị được gán đang tham khảo đến một object mà sau đó các tác vụ truy xuất property có prefix là keyword this, thì nó sẽ dùng giá trị của object đó. Nếu không có từ khóa this hoặc giá trị được gán là null (tức là không nằm trong một object nào đó), nó sẽ refer đến global object.
Global execution context có một vài khác biệt nhỏ vì nó không có arguments nên nó không cần phải có một Activation object tham khảo đến nó. Global execution context thật sự cần một scope và scope chain của nó chỉ chứa một object, đó là global object. Trong quá trình duyệt để khởi tạo biến của global object, nó sẽ được dùng như một Variable object, do đó các biến toàn cục (hoặc các hàm top-level function) sẽ được tạo thành các properties với cùng tên, giống như mô tả ở trên.
Nguồn :-Dzinh on tech