From 218934fa2ed1c702a6d3923d2aa2cc6b43c48684 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:43:23 -0500 Subject: initial commit --- .../control_flow_and_error_handling/index.html | 405 +++++++++ .../javascript/guide/cu-phap-lap-trinh/index.html | 710 ++++++++++++++++ .../guide/details_of_the_object_model/index.html | 742 +++++++++++++++++ .../guide/expressions_and_operators/index.html | 920 +++++++++++++++++++++ files/vi/web/javascript/guide/functions/index.html | 668 +++++++++++++++ .../vi/web/javascript/guide/gioi-thieu/index.html | 137 +++ files/vi/web/javascript/guide/index.html | 124 +++ .../guide/iterators_and_generators/index.html | 187 +++++ .../javascript/guide/keyed_collections/index.html | 153 ++++ .../guide/loops_and_iteration/index.html | 348 ++++++++ .../javascript/guide/numbers_and_dates/index.html | 385 +++++++++ .../guide/regular_expressions/index.html | 666 +++++++++++++++ .../javascript/guide/text_formatting/index.html | 250 ++++++ .../web/javascript/guide/using_promises/index.html | 329 ++++++++ .../guide/working_with_objects/index.html | 500 +++++++++++ 15 files changed, 6524 insertions(+) create mode 100644 files/vi/web/javascript/guide/control_flow_and_error_handling/index.html create mode 100644 files/vi/web/javascript/guide/cu-phap-lap-trinh/index.html create mode 100644 files/vi/web/javascript/guide/details_of_the_object_model/index.html create mode 100644 files/vi/web/javascript/guide/expressions_and_operators/index.html create mode 100644 files/vi/web/javascript/guide/functions/index.html create mode 100644 files/vi/web/javascript/guide/gioi-thieu/index.html create mode 100644 files/vi/web/javascript/guide/index.html create mode 100644 files/vi/web/javascript/guide/iterators_and_generators/index.html create mode 100644 files/vi/web/javascript/guide/keyed_collections/index.html create mode 100644 files/vi/web/javascript/guide/loops_and_iteration/index.html create mode 100644 files/vi/web/javascript/guide/numbers_and_dates/index.html create mode 100644 files/vi/web/javascript/guide/regular_expressions/index.html create mode 100644 files/vi/web/javascript/guide/text_formatting/index.html create mode 100644 files/vi/web/javascript/guide/using_promises/index.html create mode 100644 files/vi/web/javascript/guide/working_with_objects/index.html (limited to 'files/vi/web/javascript/guide') diff --git a/files/vi/web/javascript/guide/control_flow_and_error_handling/index.html b/files/vi/web/javascript/guide/control_flow_and_error_handling/index.html new file mode 100644 index 0000000000..55931e4a1f --- /dev/null +++ b/files/vi/web/javascript/guide/control_flow_and_error_handling/index.html @@ -0,0 +1,405 @@ +--- +title: Control flow and error handling +slug: Web/JavaScript/Guide/Control_flow_and_error_handling +translation_of: Web/JavaScript/Guide/Control_flow_and_error_handling +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}
+ +

JavaScript hỗ trợ một tập hợp lệnh gọn nhẹ, các lệnh điều khiển luồng chuyên biệt, mà khi kết hợp lại có thể tăng tính tương tác cho ứng dụng của bạn lên đáng kể. Chương này giới thiệu sơ qua về các lệnh này.

+ +

Mục JavaScript reference chứa các chi tiết đầy đủ về các câu lệnh trong chương này. Dấu chấm phẩy (;) được dùng để ngăn cách giữa các lệnh trong code JavaScript.

+ +

Bât cứ một biểu thức JavaScript nào cũng đều là một câu lệnh. Xem Expressions and operators để biết thông tin đầy đủ về các biểu thức.

+ +

Block statement

+ +

Một trong những lệnh căn bản nhất là khối lệnh (block statement), được dùng để nhóm lại các câu lệnh. Khối lệnh được phân định bởi cặp dấu ngoặc nhọn:

+ +
{
+  lệnh_1;
+  lệnh_2;
+  .
+  .
+  .
+  lệnh_n;
+}
+
+ +

Ví dụ

+ +

Khối lệnh thường được dùng với lệnh điều khiển luồng (như là if, for, while).

+ +
while (x < 10) {
+  x++;
+}
+
+ +

Ở đây, { x++; } là một khối lệnh.

+ +

Quan trọng: Trước ECMAScript2015 (phiên bản thứ 6), JavaScript chưa có phạm vi khối (block-scope). Trong phiên bản JavaScript cũ hơn, biến được khai báo trong khối được đặt phạm vi theo hàm hoặc đoạn mã bao bọc khối đó, và ảnh hưởng của việc thiết lập chúng sẽ vượt ra khỏi phạm vi của khối. Nói cách khác, khối lệnh không định nghĩa phạm vi. Các khối độc lập ("standalone" - tức là đi kèm với lệnh điều khiển nào) trong JavaScript có thể sản sinh kết quả khác so với khối lệnh tương tự trong Java hay C. Chẳng hạn:

+ +
var x = 1;
+{
+  var x = 2;
+}
+console.log(x); // trả về 2
+
+ +

Đoạn mã này trả về 2 bởi vì câu lệnh var x bên trong khối có cùng phạm vi với câu lệnh var x trước khối. Trong C hoặc Java, đoạn mã này sẽ trả về 1.

+ +

Kể từ ECMAScript2015, hai kiểu khai báo biến mới là letconst đều được đặt trong phạm vi khối lệnh. Tham khảo {{jsxref("Statements/let", "let")}} và {{jsxref("Statements/const", "const")}} để biết thêm chi tiết.

+ +

Lệnh điều kiện

+ +

Lệnh điều kiện là tập hợp các dòng lệnh sẽ thực thi nếu một điều kiện nhất định trả về true. JavaScript hỗ trợ hai lệnh điều kiện: if...elseswitch.

+ +

Lệnh if...else

+ +

Dùng mệnh đề if để thực thi lệnh nếu điều kiện trả về true. Có thể dùng thêm mệnh đề else để thực thi lệnh nếu điều kiện trả về false. Lệnh if trông như sau:

+ +
if (điều_kiện) {
+  lệnh_1;
+} else {
+  lệnh_2;
+}
+ +

Ở đây, điều_kiện có thể là bất cứ biểu thức nào để đánh giá true hoặc false. Xem Boolean để hiểu cách thức trả về truefalse. Nếu điều_kiện trả về true, lệnh_1 sẽ được thực thi; trái lại, lệnh_2 sẽ được thực thi. lệnh_1lệnh_2 có thể là bất cứ câu lệnh nào, bao gồm các câu lệnh if lồng nhau.

+ +

Bạn cũng có thể dùng lệnh else if để có một dãy điều kiện liên tiếp nhau, chẳng hạn như sau:

+ +
if (điều_kiện_1) {
+  lệnh_1;
+} else if (điều_kiện_2) {
+  lệnh_2;
+} else if (điều_kiện_n) {
+  lệnh_n;
+} else {
+  lệnh_cuối;
+}
+
+ +

Trong trường hợp có nhiều điều kiện, chỉ thực thi các lệnh trong điều kiện đầu tiên trả về true. Để thực thi nhiều lệnh cùng lúc, hãy nhóm chúng lại trong một khối lệnh ({ ... }) . Nói tóm lại, ta luôn nên dùng khối lệnh, nhất là khi lồng trong câu lệnh if:

+ +
if (điều_kiện) {
+  lệnh_1_nếu_điều_kiện_là_true;
+  lệnh_2_nếu_điều_kiện_là_true;
+} else {
+  lệnh_3_nếu_điều_kiện_là_false;
+  lệnh_4_nếu_điều_kiện_là_false;
+}
+
+ +
Đừng dùng lệnh gán trong biểu thức điều kiện, bởi vì lệnh gán có thể bị hiểu lầm với so sánh ngang bằng nếu xem sơ qua đoạn code. Chẳng hạn, đừng viết như sau:
+ +
// Dễ bị hiểu lầm là "x == y" trong khi ở bên dưới là gán giá trị y cho x
+if (x = y) {
+  /* tập hợp các câu lệnh */
+}
+
+ +

Nếu cần phải gán trong biểu thức điều kiện, hãy bọc chúng lại trong cặp dấu ngoặc tròn, chẳng hạn:

+ +
if ((x = y)) {
+  /* tập hợp các câu lệnh */
+}
+
+ +

Giá trị Falsy

+ +

Các giá trị sau sẽ trả về false (còn được gọi là giá trị {{Glossary("Falsy")}}):

+ + + +

Mọi giá trị khác, bao gồm tất cả object, trả về true khi được truyền vào câu lệnh điều kiện.

+ +

Chú ý: Đừng nhầm lẫn giữa giá trị Boolean sơ khai truefalse với giá trị true và false của object {{jsxref("Boolean")}}. Chẳng hạn:

+ +
var b = new Boolean(false);
+if (b) // điều kiện này trả về true
+if (b == true) // điều kiện này trả về false
+
+ +

Ví dụ

+ +

Trong ví dụ sau, hàm checkData trả về true nếu số lượng ký tự của object Text là 3; trái lại, một thông báo sẽ hiện ra và hàm sẽ trả về false.

+ +
function checkData() {
+  if (document.form1.threeChar.value.length == 3) {
+    return true;
+  } else {
+    alert('Enter exactly three characters. ' +
+    document.form1.threeChar.value + ' is not valid.');
+    return false;
+  }
+}
+
+ +

Lệnh switch

+ +

Lệnh switch cho phép một chương trình đánh giá một biểu thức và nổ lực thử nghiệm dần để tìm ra trường hợp giống nhau giữa giá trị của biểu thức đó với case label. Nếu một sự kết hợp được tìm thấy, chương trình sẽ chạy câu lệnh liên quan. Câu lệnh switch như sau:

+ +
switch (expression) {
+  case label_1:
+    statements_1
+    [break;]
+  case label_2:
+    statements_2
+    [break;]
+    ...
+  default:
+    statements_def
+    [break;]
+}
+
+ + + +

Lệnh break

+ +

Lệnh tuỳ chọn break liên kết với từng mệnh đề case nhằm đảm bảo chương trình nhảy ra khỏi switch một khi câu lệnh liên quan tại vị trí mệnh đề phù hợp đã thực hiện xong, và sau đó tiếp tục thực hiện câu lệnh phía sau lệnh switch. Nếu không có break, chương trình sẽ thực thi toàn bộ câu lệnh tiếp theo còn lại trong switch mà không cần đánh giá xem giá trị của biểu thức và case label có khớp nhau không.

+ +

Ví dụ

+ +

Trong ví dụ sau, nếu fruittype được gán giá trị "Bananas", chương trình sẽ khớp với nhãn "Bananas" và thực thi các lệnh đi kèm. Khi chương trình chạy tới break, chương trình ngừng và thực thi các lệnh sau khối lệnh switch. Nếu không có break, lệnh ứng với nhãn "Cherries" cũng sẽ được thực thi.

+ +
switch (fruittype) {
+  case 'Oranges':
+    console.log('Oranges are $0.59 a pound.');
+    break;
+  case 'Apples':
+    console.log('Apples are $0.32 a pound.');
+    break;
+  case 'Bananas':
+    console.log('Bananas are $0.48 a pound.');
+    break;
+  case 'Cherries':
+    console.log('Cherries are $3.00 a pound.');
+    break;
+  case 'Mangoes':
+    console.log('Mangoes are $0.56 a pound.');
+    break;
+  case 'Papayas':
+    console.log('Mangoes and papayas are $2.79 a pound.');
+    break;
+  default:
+   console.log('Sorry, we are out of ' + fruittype + '.');
+}
+console.log("Is there anything else you'd like?");
+ +

Lệnh xử lý ngoại lệ

+ +

Bạn có thể quăng ngoại lệ (exeption) bằng lệnh throw và xử lý chúng bằng lệnh try...catch.

+ + + +

Kiểu ngoại lệ

+ +

Gần như mọi object đều có thể bị quăng ra trong JavaScript. Tuy vậy, không phải object nào khi quăng ra cũng được tạo ra tương tự nhau. Trong khi thông thường các số và chuỗi được quăng ra để thông báo lỗi, song sử dụng một trong số những kiểu ngoại lệ chuyên biệt cho mục đích này thường hiệu quả hơn:

+ + + +

Lệnh throw

+ +

Lệnh throw để quăng ra ngoại lệ. Khi muốn quăng ra ngoại lệ, bạn phải đặc tả biểu thức chứa giá trị để quăng ra:

+ +
throw expression;
+
+ +

Bạn có thể quăng ra biểu thức nào cũng được, không chỉ biểu thức dành riêng cho kiểu ngoại lệ. Ví dụ sau đây quăng ra vô số kiểu:

+ +
throw 'Error2';   // String type
+throw 42;         // Number type
+throw true;       // Boolean type
+throw {toString: function() { return "I'm an object!"; } };
+
+ +
Ghi chú: Bạn có thể đặc tả object khi quăng ngoại lệ. Rồi đặt tham chiếu tới thuộc tính của object đó trong khối catch.
+ +
// Tạo object có kiểu UserException
+function UserException(message) {
+  this.message = message;
+  this.name = 'UserException';
+}
+
+// Bắt ngoại lệ in ra dòng lỗi màu mè một tí
+// (như khi in lên console chả hạn)
+UserException.prototype.toString = function() {
+  return this.name + ': "' + this.message + '"';
+}
+
+// Tạo một instance của object rồi quăng ra
+throw new UserException('Value too high');
+ +

Lệnh try...catch

+ +

Lệnh try...catch đánh dấu một khối để thử, và xác định khi nào sẽ quăng ra ngoại lệ. Khi ngoại lệ bị quăng ra, lệnh try...catch sẽ tóm gọn nó.

+ +

Lệnh try...catch bao gồm khối try, chứa một hoặc nhiều câu lệnh, và khối catch, chứa các lệnh dành để xử lý khi có ngoại lệ bị quăng ra trong khối try.

+ +

Tức là, bạn muốn lệnh trong khối try thành công, và nhỡ nó không thành công, bạn muốn truyền luồng xử lý xuống khối catch. Nếu bất cứ lệnh nào trong khối try (hoặc hàm được gọi trong khối try) quăng ra ngoại lệ, luồng điều khiển sẽ ngay lập tức chuyển xuống khối catch. Nếu không có ngoại lệ nào bị quăng ra trong khối try, khối catch sẽ bị bỏ qua. Khối finally thực thi sau khi khối trycatch thực thi xong nhưng trước các lệnh đặt ngay sau lệnh try...catch.

+ +

Ví dụ sau dùng lệnh try...catch. Trong ví dụ này, ta gọi một hàm nhận vào một giá trị và trả về tên tháng tương ứng. Nếu giá trị truyền vào không tương ứng với số tháng (1-12), một ngoại lệ sẽ bị quăng ra có kiểu "InvalidMonthNo" và lệnh trong khối catch sẽ đặt lại giá trị cho biến monthName thành unknown.

+ +
function getMonthName(mo) {
+  mo = mo - 1; // Chỉnh lại số tháng cho hợp với chỉ số mảng (1 = Jan, 12 = Dec)
+  var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
+                'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+  if (months[mo]) {
+    return months[mo];
+  } else {
+    throw 'InvalidMonthNo'; // dùng từ khoá throw ở đây nhé
+  }
+}
+
+try { // lệnh để thử
+  monthName = getMonthName(myMonth); // hàm để quăng ra ngoại lệ
+}
+catch (e) {
+  monthName = 'unknown';
+  logMyErrors(e); // truyền object ngoại lệ vào hàm xử lý lỗi của bạn
+}
+
+ +

Khối catch

+ +

Bạn có thể dùng khối catch để xử lý tất cả ngoại lệ sinh ra trong khối try.

+ +
catch (catchID) {
+  statements
+}
+
+ +

Khối catch nhận vào một định danh (catchID như trong cú pháp phía trên) giữ giá trị mà lệnh throw quăng ra; bạn có thể dùng định danh này để lấy thông tin về ngoại lệ bị quăng ra. JavaScript tạo ra định danh này khi chương trình chạy vào khối catch; định danh này chỉ tồn tại trong khối catch; sau khi khối catch thực thi xong, định danh đó sẽ không còn tồn tại nữa.

+ +

Chẳng hạn, đoạn code sau quăng ra một ngoại lệ. Khi ngoại lệ xảy ra, điều khiển được truyền xuống khối catch.

+ +
try {
+  throw 'myException'; // sinh ngoại lệ
+}
+catch (e) {
+  // lệnh xử lý ngoại lệ
+  logMyErrors(e); // truyền object ngoại lệ xuống hàm xử lý lỗi
+}
+
+ +

Thực tiễn tốt nhất: Khi log error ra console bên trong một catch block, hãy sử dụng console.error() thay vì console.log(), nó sẽ định dạng dòng văn bản như là một lỗi, và thêm nó vào danh sách các dòng văn bản lỗi được tạo ra bởi trang. Điều này tốt cho việc debugging.

+ +

Khối finally

+ +

Khối finally chứa các lệnh thực thi ngay sau khi thực thi khối trycatch nhưng trước các lệnh liền sau lệnh try...catch. Khối finally vẫn thực thi dù có xảy ra ngoại lệ hay không. Nếu ngoại lệ bị quăng ra, các lệnh trong khối finally vẫn thực thi dù khối catch có xử lý ngoại lệ hay không.

+ +

Bạn có thể dùng khối finally để khiến mã của bạn lỗi một cách yên bình nhỡ ngoại lệ xảy ra; chả hạn, bạn muốn giải phóng tài nguyên khỏi đoạn mã của mình. Ví dụ sau mở một tệp tin và thực thi lệnh dùng tài nguyên của tệp đó (JavaScript phía server cho phép bạn truy cập vào tệp tin). Nếu có ngoại lệ bị quăng ra trong lúc đang mở tệp tin, khối finally sẽ đóng đoạn tệp tin lại trước khi đoạn mã bị lỗi.

+ +
openMyFile();
+try {
+  writeMyFile(theData); // Ghi vào tệp, có thể có lỗi
+} catch(e) {
+  handleError(e); // Nếu có lỗi thì dùng hàm này để xử lý
+} finally {
+  closeMyFile(); // Luôn luôn đóng tệp lại
+}
+
+ +

Nếu khối finally trả về giá trị thì giá trị đó sẽ trở thành giá trị trả về của cả dãy lệnh try-catch-finally, bỏ qua mọi lệnh return trong khối trycatch:

+ +
function f() {
+  try {
+    console.log(0);
+    throw 'bogus';
+  } catch(e) {
+    console.log(1);
+    return true; // lệnh return ở đây bị tạm ngưng
+                 // cho tới khi thực thi xong khối finally
+    console.log(2); // không thực thi tới
+  } finally {
+    console.log(3);
+    return false; // ghi đè lệnh "return" phía trên
+    console.log(4); // không thực thi tới
+  }
+  // "return false" sẽ thực thi ngay lúc này
+  console.log(5); // không thực thi tới
+}
+console.log(f()); // 0, 1, 3, false
+
+ +

Ghi đè giá trị trả về bằng khối finally cũng áp dụng với các ngoại lệ bị quăng ra trong khối catch:

+ +
function f() {
+  try {
+    throw 'bogus';
+  } catch(e) {
+    console.log('caught inner "bogus"');
+    throw e; // lệnh thow này bị tạm ngưng
+             // cho tới khi thực thi xong khối finally
+  } finally {
+    return false; // ghi đè lệnh "throw" phía trên
+  }
+  // "return false" sẽ thực thi ngay lúc này
+}
+
+try {
+  console.log(f());
+} catch(e) {
+  // khối này sẽ không bao giờ tới được
+  // bởi vì khối catch phía trên đã bị ghi đè
+  // bởi lệnh return trong finally
+  console.log('caught outer "bogus"');
+}
+
+// ĐẦU RA
+// caught inner "bogus"
+// false
+ +

Lồng lệnh try...catch

+ +

Bạn có thể lồng một hoặc nhiều lệnh try...catch. Nếu lệnh try...catch bên trong không có khối catch :

+ +
    +
  1. Nó nên có khối finally
  2. +
  3. Lệnh try...catch bọc bên ngoài phải bắt được cái gì đấy. Để biết thêm thông tin, hãy đọc lồng khối try trên trang try...catch.
  4. +
+ +

Tận dụng object Error

+ +

Tuỳ theo kiểu của lỗi, bạn có thể dùng thuộc tính 'name' và 'message' để lấy ra thông điệp lỗi rõ ràng hơn. 'name' lấy ra class chung của Error (tức là 'DOMException' hoặc 'Error'), trong khi 'message' thường lấy ra thông điệp về lỗi súc tích hơn thông điệp tạo ra bởi ép object lỗi thành xâu ký tự.

+ +

Nếu bạn muốn quăng ra ngoại lệ của riêng mình, để tận dụng được những thuộc tính này (ví dụ như khi khối catch không phân biệt giữa ngoại lệ của bạn và của hệ thống), bạn có thể dùng hàm khởi tạo Error. Chẳng hạn:

+ +
function doSomethingErrorProne() {
+  if (ourCodeMakesAMistake()) {
+    throw (new Error('The message'));
+  } else {
+    doSomethingToGetAJavascriptError();
+  }
+}
+....
+try {
+  doSomethingErrorProne();
+} catch (e) {
+  console.log(e.name); // logs 'Error'
+  console.log(e.message); // logs 'The message' or a JavaScript error message
+}
+
+ +
{{PreviousNext("Web/JavaScript/Guide/Grammar_and_types", "Web/JavaScript/Guide/Loops_and_iteration")}}
diff --git a/files/vi/web/javascript/guide/cu-phap-lap-trinh/index.html b/files/vi/web/javascript/guide/cu-phap-lap-trinh/index.html new file mode 100644 index 0000000000..64f63492d2 --- /dev/null +++ b/files/vi/web/javascript/guide/cu-phap-lap-trinh/index.html @@ -0,0 +1,710 @@ +--- +title: Cú pháp lập trình +slug: Web/JavaScript/Guide/cu-phap-lap-trinh +translation_of: Web/JavaScript/Guide/Grammar_and_types +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Introduction", "Web/JavaScript/Guide/Control_flow_and_error_handling")}}
+ +

Phần này nói về các cú pháp cơ bản của ngôn ngữ JavaScript, các cách khai báo biến, các loại dữ liệu và chính tả (literal).

+ +

Cơ bản

+ +

Cú pháp của JavaScript (JS) phần lớn được vay mượn từ Java, nhưng JS cũng chịu ảnh hưởng từ cú pháp của các ngôn ngữ lập trình khác như Awk, Perl và Python.

+ +

JavaScript là ngôn ngữ lập trình sử dụng chuẩn kí tự Unicode và khi viết cũng cần phải lưu ý phân biệt chữ HOA và chữ thường (case-sensitive). Điều này có nghĩa là các từ như Früh (trong tiếng Đức có nghĩa là "sớm" - early) có thể được sử dụng đặt tên cho biến.

+ +
let Früh = "foobar"
+
+ +

In JavaScript, instructions are called {{Glossary("Statement", "statements")}} and are separated by a semicolon (;).

+ +

Dấu chấm phẩy (;) chỉ cần thiết nếu trên cùng một dòng có từ hai câu lệnh trở lên. Trường hợp câu lệnh nằm riêng một dòng thì không cần dấu chấm phẩy.

+ +

ECMAScript cũng có những nguyên tắc để tự động thêm dấu chấm phẩy (ASI) để đánh dấu kết thúc một dòng lệnh. (Để biết thêm, xem tài liệu tham khảo chi tiết tại đây: JavaScript's lexical grammar.)

+ +

Thực tế thì nên luôn luôn thêm một dấu chấm phẩy vào cuối mỗi câu lệnh, thậm chí khi điều này là không bắt buộc. Việc làm này sẽ giúp tránh bớt bug.

+ +

Các đoạn mã Javascript sẽ được đọc từ trái qua phải và được chuyển thể thành một chuỗi các input elements bao gồm: tokens, control characters, line terminators, comments hoặc {{glossary("whitespace")}}. (Spaces, tabs, và các ký tự đánh dấu dòng mới được xem là whitespace.)

+ +

Comments

+ +

Cú pháp của comments thì ... giống với C++ và một số ngôn ngữ lập trình khác:

+ +
// a one line comment <-- đây là cách comment trên 1 dòng
+
+/* this is a longer, cho những dòng dài hoặc nhiều dòng
+   multi-line comment. hãy sử dụng kiểu comment này
+ */
+
+/* You can't, however /* nest comments */ SyntaxError */ <-- Không nên lồng comment trong comment, như đoạn comment này sẽ gây ra lỗi vì "/*" được mở bên trong comment trước đó không có hiệu lực đối với từ SyntaxError nên dấu đóng comment "*/" sẽ gây ra lỗi cú pháp. 
+ +

Comments hoạt động như whitespace, và sẽ bị bỏ qua trong quá trình script chạy.

+ +

Note: Bạn có thể thấy loại cú pháp comment này trong một số file javascript #!/usr/bin/env node.

+ +

Đây là cú pháp hashbang comment, và là một comment đặc biệt sử dụng để chỉ định đường dẫn đến một Javascript interpreter cụ thể có nhiệm vụ sẽ chạy đoạn script. Xem chi tiết Hashbang comments.

+ +

Khai báo biến

+ +

Có 3 kiểu khai báo biến trong JavaScript.

+ +
+
{{jsxref("Statements/var", "var")}}
+
Khai báo một biến, và tùy ý bạn có hoặc không khởi tạo giá trị cho nó.
+
{{jsxref("Statements/let", "let")}}
+
Khai báo một block-scoped, hoặc biến local, chỉ có thể truy cập được trong block bao quanh nó.
+
+
function foo() {
+  var x = 10;
+  if (true) {
+   let x = 20; // x ở đây được khai báo lại nhưng khi ra khỏi block-scoped nó sẽ nhận lại giá trị bên trên là 10
+   console.log(x); // in ra 20
+  }
+   console.log(x); // in ra 10
+}
+
+
{{jsxref("Statements/const", "const")}}
+
Khai báo một hằng block-scoped, read-only.
+
+ +

Biến

+ +

Bạn sử dụng biến như là tên tượng trưng cho các giá trị trong chương trình. Tên của biến được gọi là {{Glossary("Identifier", "identifier")}}, tuân theo những quy tắc nhất định.

+ +

Tên biến phải bắt đầu bằng một 'chữ cái', kí tự gạch dưới (_), hoặc kí tự dollar ($). Các ký tự tiếp theo cũng có thể là các chữ số 0-9.  Vì JavaScript là case sensitive, các chữ các bao gồm các ký tự từ "A" đến "Z" (viết hoa) và "a" đến "z" (viết thường).

+ +

Bạn có thể sử dụng chuẩn ISO 8859-1 hoặc các kí tự Unicode như å và ü trong tên biến, thậm chí cả các kí tự dạng Unicode escape sequences.

+ +

Ví dụ: Number_hits, temp99, and _name.

+ +

Declaring variables

+ +

Có hai cách khai báo biến:

+ + + +

Bạn cũng có thể khai báo biến mà không dùng các từ khóa trên, ví dụ x = 42. Điều này sẽ tạo ra một biến undeclared global variable. Nó sẽ tạo nên một warning trong JavaScript. Undeclared variables có thể dẫn đến các hành vi không mong muốn. Nên khuyến cáo không nên sử dụng chúng.

+ +

Evaluating variables

+ +

Một biến được khai báo với cú pháp var hoặc let mà không gán giá trị, sẽ có giá trị mặc định là {{jsxref("undefined")}}.

+ +

Truong trường hợp truy cập đến một biến chưa được khai báo, thì exception {{jsxref("ReferenceError")}} sẽ được thrown:

+ +
var a;
+console.log('The value of a is ' + a); // The value of a is undefined
+
+console.log('The value of b is ' + b); // The value of b is undefined
+var b;
+// This one may puzzle you until you read 'Variable hoisting' below
+
+console.log('The value of c is ' + c); // Uncaught ReferenceError: c is not defined
+
+let x;
+console.log('The value of x is ' + x); // The value of x is undefined
+
+console.log('The value of y is ' + y); // Uncaught ReferenceError: y is not defined
+let y; 
+ +

Bạn có thể sử dụng undefined  để xét xem một biến có đang mang giá trị không. Dưới đây là một ví dụ, biến input chưa được gán giá trị, nên biến input lúc này mang giá trị mặc định undefined vậy câu điều kiện if là return true và không chạy vào else.

+ +
var input;
+if(input === undefined){
+  doThis();
+} else {
+  doThat();
+}
+
+ +

Giá trị undefined tương đương với fasle khi sử dụng trong bối cảnh boolean, như ví dụ dưới đây, chương trình sẽ chạy function myFunction vì element myArray[0]là undefined trả về false.

+ +
var myArray = [];
+if (!myArray[0]) {
+  myFunction();
+}
+
+function myFunction() {
+  alert('return false');
+}
+
+ +

Khi biến có giá trị = undefined thực hiện phép toán với biến có giá trị cụ thể hoặc hằng số sẽ cho ra giá trị  = NaN (not a number). 

+ +
var a;
+a + 2 = NaN
+ +

Null khác với Undefined. Khi biến có giá trị {{jsxref("null")}}, biến sẽ mang giá trị 0 trong các phép toán (numeric) và false trong các ngữ cảnh boolean. Ví dụ:

+ +
var n = null;
+console.log(n * 32); // Will log 0 to the console
+
+ +

Variable scope

+ +

Khi bạn khai báo một biến bên ngoài function, biến đó được gọi là global variable, bởi vì biến đó sẽ có hiệu lực đến bất kì đoạn code nào khác trong document hiện tại. Khi bạn khai báo một biến bên trong một function, nó gọi là local variable, vì nó chỉ có thể dùng được trong phạm vi function đó.

+ +

Javascript trước phiên bản ECMAScript 6 không có định nghĩa  block statement scope; thay vào đó, một biến được khai báo trong một block được xem là có giá trị local đối với function (hoặc global scope) mà block đó cư trú.

+ +

Ví dụ sau đây sẽ log ra 5, vì scope của x là global context (hoặc function context nếu đoạn code này là một phần của function khác). Scope của x không bị giới hạn vào block câu lệnh if trực tiếp gần nhất.

+ +
if (true) {
+  var x = 5;
+}
+console.log(x);  // 5
+
+ +

Điều này đã thay đổi khi sử dụng kiểu khai báo let được giới thiệu trong ECMAScript 6.

+ +
if (true) {
+  let y = 5;
+}
+console.log(y);  // ReferenceError: y is not defined (y bị giới hạn trực tiếp trong block chứa nó)
+
+ +

Variable hoisting

+ +

Một thứ không bình thường khác về các biến trong JavaScript là bạn có thể tham chiếu đến một biến tại vị trí phía trước câu lệnh khai báo. Khái niệm này gọi là hoisting; các biến trong JavaScript ở khía cạnh nào đó sẽ được "hoisted" (treo) hoặc lifted (nâng) vào vị trí trên cùng của câu lệnh hoặc hàm gần nó nhất. Tuy nhiên, các variables bị hoisted này sẽ trả về giá trị undefined. Nên cho dù bạn khai báo và khởi tạo sau khi bạn sử dụng hoặc tham chiếu đến biến này, thì trước đó nó vẫn trả về undefined.

+ +
/**
+ * Example 1
+ */
+console.log(x); // undefined
+var x = 3;
+console.log(x); // 3
+
+/**
+ * Example 2
+ */
+// will return a value of undefined
+var myvar = "my value";
+
+(function() {
+  console.log(myvar); // undefined vì bên dưới có dòng khai báo var myvar, điều này làm biến myvar bị hoisting và nhận giá trị mới là undefined (giá trị 'my value' lúc này không còn hiệu lực vì biến đã bị hoisting)
+  var myvar = "local value";
+})();
+
+ +

Ví dụ bên trên có thể được biểu đạt theo cách khác như sau:

+ +
/**
+ * Example 1
+ */
+var x;
+console.log(x === undefined); // logs "true"
+x = 3;
+
+/**
+ * Example 2
+ */
+var myvar = "my value";
+
+(function() {
+  var myvar;
+  console.log(myvar); // undefined
+  myvar = "local value";
+})();
+
+ +

Vì vấn đề hoisting này, tất cả các câu lệnh khai báo biến với var bên trong một function nên được đặt gần với đầu của function nhất có thể. Làm điều này giúp gia tăng độ rõ ràng của code (trường hợp dùng var như trong ví dụ 2 ở trên sẽ gây ra hoisting).

+ +

Function hoisting

+ +

Trong trường hợp của các function, chỉ các function được tạo theo kiểu function declaration là bị hoisted, còn function được tạo theo kiểu function expression thì không. Khác với variable hoisting, hoisted function không trả về giá trị mặc định undefined. Ví dụ:

+ +
/* Function declaration */
+
+foo(); // "bar"
+
+function foo() {
+  console.log('bar');
+}
+
+
+/* Function expression */
+
+baz(); // TypeError: baz is not a function
+
+var baz = function() {
+  console.log('bar2');
+};
+ +

Biến toàn cục (global variables)

+ +

Các global variables trên thực tế là những properties của global object. Trong các web page, global object chính là window, nên bạn có thể set và truy cập đến các global variables bằng cách sử dụng cú pháp window.variable.

+ +

Hệ quả  là, bạn có thể truy cập đến các global variables được khai báo trong một window hoặc frame từ một window hoặc frame khác, bằng cách chỉ định rõ tên của window hoặc frame. Ví dụ, nếu một biến có tên phoneNumber được khai báo trong một document, bạn có thể tham chiếu đến biến này từ một iframe như là parent.phoneNumber.

+ +

Constants

+ +

Bạn có thể tạo các hằng số read-only với từ khóa {{jsxref("Statements/const", "const")}}. Cú pháp đặt tên cho hằng cũng giống như biến, tên hằng phải bắt đầu bằng một chữ cái, hoặc dấu gạch dưới _, hoặc dấu dollar $, và có thể bao gồm mọi ký tự chữ, số, hoặc dấu gạch dưới.

+ +
const PI = 3.14;
+
+ +

Một hằng số đã khai báo không thể thay đổi giá trị thông qua việc gán lại giá trị hoặc không thể bị khai báo lại trong khi đoạn script đang chạy. Và bắt buộc phải được gán giá trị ngay khi khởi tạo.

+ +

Các nguyên tắc về scope cho các hằng số cũng giống như cách các biến block-scoped như let hoạt động.

+ +

Bạn không thể khai báo một hằng số với tên trùng với tên function hoặc biến trong cùng một scope. Ví dụ:

+ +
// THIS WILL CAUSE AN ERROR
+function f() {};
+const f = 5;
+
+// THIS WILL CAUSE AN ERROR ALSO
+function f() {
+  const g = 5;
+  var g;
+
+  //statements
+}
+
+ +

Tuy nhiên, khi gán một object vào một hằng, thì các thuộc tính của object đó là not protected, nên câu lệnh dưới đây sẽ được thực thi mà không lỗi.

+ +
const MY_OBJECT = {'key': 'value'};
+MY_OBJECT.key = 'otherValue';
+ +

Cũng tương tự, các nội dung trong một mảng cũng là not protected, nên câu lệnh dưới đây sẽ được thực thi mà không lỗi.

+ +
const MY_ARRAY = ['HTML','CSS'];
+MY_ARRAY.push('JAVASCRIPT');
+console.log(MY_ARRAY); //logs ['HTML','CSS','JAVASCRIPT'];
+ +

Cấu trúc dữ liệu và kiểu dữ liệu

+ +

Kiểu dữ liệu

+ +

Tiêu chuẩn mới nhất của ECMAScript định nghĩa 8 kiểu dữ liệu:

+ + + +

Mặc dù những kiểu dữ liệu này tương đối ít, chúng cho phép bạn có thể thể hiện những hàm rất hữu dụng. {{jsxref("Object", "Objects")}} và {{jsxref("Function", "functions")}} là những phần tử nền tảng khác của JavaScript. Bạn có thể xem object như là những vùng chứa được đặt tên (named container) để phục vụ cho các giá trị, và các hàm là những quy trình thủ tục để đoạn script của bạn thi hành.

+ +

Chuyển đổi kiểu dữ liệu

+ +

JavaScript là dynamically typed language. Điều đó có nghĩa bạn không cần phải chỉ định kiểu dữ liệu của biến khi bạn khai báo biến, và kiểu dữ liệu được tự động chuyển đổi khi cần thiết trong quá trình đoạn script được thực thi.

+ +

Cho nên, ví dụ, bạn có thể định nghĩa một biến như sau:

+ +
var answer = 42;
+
+ +

Và sau đó, bạn có thể gán cùng một biến này với một giá trị chuỗi, ví dụ:

+ +
answer = "Thanks for all the fish...";
+
+ +

Bởi vì JavaScript là dynamically typed, việc gán giá trị này sẽ không gây ra lỗi.

+ +

Các số và toán tử '+'

+ +

Trong các biểu thức (expression) có sự liên quan giữa các giá trị numeric và string với toán tử  +, JavaScript sẽ chuyển đổi giá trị số sang chuỗi. Ví dụ:

+ +
x = "The answer is " + 42 // "The answer is 42"
+y = 42 + " is the answer" // "42 is the answer"
+
+ +

Với tất cả mọi loại toán tử khác, JavaScript sẽ không chuyển đổi giá trị numeric sang string. Ví dụ:

+ +
"37" - 7 // 30
+"37" + 7 // "377"
+
+ +

Chuyển từ kiểu chuỗi (string) sang kiểu số (number)

+ +

Tong trường hợp một giá trị biểu thị một số nhưng lại được lưu trong bộ nhớ như là một chuỗi, có các phương thức để chuyển đổi.

+ + + +

parseInt sẽ chỉ trả về các số nguyên, nên mục đích của nó là làm giảm bớt giá trị cho các số decimals. Thêm nữa, một thực hành tốt nhất cho parseInt là luôn luôn thêm vào nó tham số radix. Tham số radix được dùng để chỉ định hệ số học nào sẽ được sử dụng.

+ +
parseInt('101', 2) // 5
+ +

Một phương thức khác để nhận được giá trị số từ một chuỗi là dùng toán tử + 

+ +
"1.1" + "1.1" = "1.11.1"
+(+"1.1") + (+"1.1") = 2.2
+// Note: the parentheses are added for clarity, not required.
+ +

Literals

+ +

Literals (nguyên văn), đại diện cho các giá trị trong JavaScript. Đây là những giá trị cố định - không phải biến - mà bạn cung cấp một cách litterally trong script của bạn. Phần này mô tả những kiểu literals sau đây:

+ + + +

Array literals

+ +

Một array literal là một danh sách của không hoặc nhiều biểu thức, mỗi biểu thức trong đó đại diện cho một phần tử của mảng (array element), được bao bọc trong dấu ngoặc vuông ([]). Khi bạn tạo một mảng bằng cách dùng array literal, nó được khởi tạo với những giá trị cụ thể như là các element của mảng, và length của mảng là số lượng đối số (argument).

+ +

Ví dụ sau tạo ra mảng coffees với 3 phần tử và một length của 3:

+ +
var coffees = ["French Roast", "Colombian", "Kona"];
+
+ +

Note: Một array literal là một kiểu của object initializer. Xem Using Object Initializers.

+ +

Nếu một mảng được tạo ra sử dụng một literal trong một top-level script, JavaScript sẽ interpret mảng mỗi lần nó đánh giá biểu thức có chứa array literal. Ngoài ra, một literal được sử dụng trong một hàm sẽ được tạo ra mỗi lần function được gọi.

+ +

Note: Các array literals cũng là các Array objects. Xem {{jsxref("Array")}} and Indexed collections để biết chi tiết về Array objects.

+ +

Dấu phẩy dư trong các array literals

+ +

Bạn không cần phải xác định mọi phần tử trong một array literal. Nếu bạn đặt 2 dấu phẩy trên cùng một hàng, mảng sẽ tạo ra một phần tử mang giá trị undefined. Ví dụ sau tạo ra mảng fish.

+ +
var fish = ["Lion", , "Angel"];
+
+ +

Mảng này gồm 2 phần tử có giá trị và một phần tử rỗng (fish[0] là "Lion", fish[1] là undefined, và fish[2] là "Angel").

+ +

Nếu bạn thêm một dấu phẩy theo sau (trailing comma) phần tử cuối cùng của mãng, dấu phẩy này sẽ bị bỏ qua. Trong ví dụ sau, length của mảng là 3. Không có myList[3]. Tất cả dấu phẩy khác trong danh sách ngầm chỉ định là một phần tử mới. (Note: trailing commas có thể xem là lỗi đối với các trình duyệt cũ và tốt nhất là nên xóa chúng đi).

+ +
let myList = ['home', , 'school', ];
+
+ +

Trong ví dụ bên dưới, length của mảng là 4, myList[0] và myList[2] bị thiếu.

+ +
var myList = [ , 'home', , 'school'];
+
+ +

Trong ví dụ dưới, length của mảng là 4, myList[1] và myList[3] bị thiếu. Chỉ có dấu phẩy cuối cùng bị bỏ qua.

+ +
var myList = ['home', , 'school', , ];
+
+ +

Hiểu được hành vi của cac dấu phẩy thêm này rất quan trọng để hiểu được JavaScript như là một ngôn ngữ. Tuy nhiên, khi viết code của riêng bạn, bạn nên khai báo một cách rõ ràng các phần tử bị thiếu (missing elements) là undefined. Làm vậy sẽ gia tăng độ rõ ràng cho code và dễ bảo trì sau này.

+ +

Boolean literals

+ +

Kiểu Boolean có 2 giá trị literal: truefalse.

+ +

Cẩn thận: Đừng nhầm lẫn giá trị Boolean nguyên thủy true và false với true và fales của {{jsxref("Boolean")}} object. Boolean object là một lớp bao bên ngoài kiểu dữ liệu Boolean nguyên thủy. Xem {{jsxref("Boolean")}} để biết thêm.

+ +

Numeric literals

+ +

Kiểu dữ liệu {{jsxref("Number")}} và {{jsxref("BigInt")}} có thể được biểu diễn bằng hệ decimal (hệ 10), hệ hexadecimal (hệ 16), octal (hệ 8) và binary (base 2 - hệ nhị phân).

+ + + +

Một số ví dụ của numeric literals:

+ +
0, 117, -345, 123456789123456789n             (decimal, base 10)
+015, 0001 -0o77, 0o777777777777n              (octal, base 8)
+0x1123, 0x00111, -0xF1A7, 0x123456789ABCDEFn  (hexadecimal, "hex" or base 16)
+0b11, 0b0011, -0b11, 0b11101001010101010101n  (binary, base 2)
+
+ +

Chi tiết xem thêm tại: Numeric literals in the Lexical grammar reference.

+ +

Floating-point literals

+ +

Một floating-point literal (literal số thực dấu chấm động) có thể có các bộ phận sau:

+ + + +

Bộ phận số mũ là một ký tự "e" hoặc "E", theo sau nó là một số nguyên integer (số nguyên integer này có thể có thêm phía trước là dấu "+" hoặc "-"). Một floating-point literal phải có ít nhất một ký tự số, và hoặc là một dấu chấm hệ 10 hoặc là ký tự "e" (hoặc "E"). Tóm lại, cú pháp như sau:

+ +
[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
+
+ +

Ví dụ: 

+ +
3.1415926
+-.123456789
+-3.1E+12
+.1e-23
+
+ +

Object literals

+ +

Một object literal là một danh sách của không hoặc nhiều cặp property names và associated values (tên thuộc tính và giá trị được liên kết) của một một object, bao bọc bởi cặp dấu ngoặc nhọn ({}).

+ +

Lưu ý đừng sử dụng một object literal ngay tại vị trí bắt đầu của một câu lệnh! Điều này sẽ dẫn đến một lỗi (hoặc nó sẽ thực thi theo cách bạn không mong muốn), vì dấu mở ngoặc nhọn { sẽ được interpreted như là bắt đầu của một block.

+ +

Ví dụ dưới đây là một ví dụ của object literal. Phần tử đầu tiên của object car định nghĩa là một thuộc tính (property), myCar, và được gán giá trị kiểu chuỗi là "myCar", phần tử thứ 2, thuộc tính getCar, ngay lập tức được gán giá trị là kết quả trả về của việc gọi hàm (CarTypes("Honda")); phần tử thứ 3, thuộc tính special, sử dụng một biến đã tồn tại (sales).

+ +
var Sales = "Toyota";
+
+function CarTypes(name) {
+  if (name == "Honda") {
+    return name;
+  } else {
+    return "Sorry, we don't sell " + name + ".";
+  }
+}
+
+var car = { myCar: "Saturn", getCar: CarTypes("Honda"), special: Sales };
+
+console.log(car.myCar);   // Saturn
+console.log(car.getCar);  // Honda
+console.log(car.special); // Toyota
+
+ +

Thêm nữa, bạn có thể sử dụng cả kiểu số hoặc chữ để đặt tên cho thuộc tính của object, hoặc bạn có thể lồng một object bên trong một object khác. Ví dụ:

+ +
var car = { manyCars: {a: "Saab", "b": "Jeep"}, 7: "Mazda" };
+
+console.log(car.manyCars.b); // Jeep
+console.log(car[7]); // Mazda
+
+ +

Các tên thuộc tính của object có thể là bất kỳ chuỗi nào, bao gồm cả chuỗi rỗng ''. Nếu tên thuộc tính không phải là một JavaScript identifier hợp lệ, hoặc nó là số, thì nó phải được bao bọc trong các dấu nháy.

+ +

Các tên thuộc tính mà không phải là các identifier hợp lệ không thể được truy cập đến như các thuộc tính thông thường là dùng dấu chấm (.), nhưng có thể được truy cập đến và set giá trị bằng cặp dấu ngoặc vuông giống mảng ("[]").

+ +
var unusualPropertyNames = {
+  "": "An empty string",
+  "!": "Bang!"
+}
+console.log(unusualPropertyNames."");   // SyntaxError: Unexpected string
+console.log(unusualPropertyNames[""]);  // An empty string
+console.log(unusualPropertyNames.!);    // SyntaxError: Unexpected token !
+console.log(unusualPropertyNames["!"]); // Bang!
+ +

Please note:

+ +
var foo = {a: "alpha", 2: "two"};
+console.log(foo.a);    // alpha
+console.log(foo[2]);   // two
+//console.log(foo.2);  // Error: missing ) after argument list
+//console.log(foo[a]); // Error: a is not defined
+console.log(foo["a"]); // alpha
+console.log(foo["2"]); // two
+
+ +

Enhanced Object literals

+ +

Trong ES2015, các object literals được mở rộng từ đó hỗ trợ thêm việc cài đặt các prototype tại construction, shorthand cho việc gán biến foo: foo, các phương thức defining, make super calls, và xử lý các tên thuộc tính với các biểu thức. Cùng nhau, những thứ này cũng mang object listerals và khai báo class đến gần nhau hơn, và cho phép các thiết kế object-based để đạt được lợi ích từ một số tiện nghi giống nhau.

+ +
var obj = {
+    // __proto__
+    __proto__: theProtoObj,
+    // Shorthand for ‘handler: handler’
+    handler,
+    // Methods
+    toString() {
+     // Super calls
+     return 'd ' + super.toString();
+    },
+    // Computed (dynamic) property names
+    [ 'prop_' + (() => 42)() ]: 42
+};
+ +

RegExp literals

+ +

Một regex literal (được định nghĩa chi tiết sau đây) là một mô hình mẫu bao bọc giữa các dấu xuyệt /. Ví dụ:

+ +
var re = /ab+c/;
+ +

String literals

+ +

Một string literal là không có hoặc có nhiều ký tự bao bọc trong dấu (") hoặc ('). Một chuỗi (string) phải được giới hạn trong cặp dấu cùng loại; hoặc là cùng nháy đơn hoặc là cùng nháy kép. Ví dụ:

+ + + +

Bạn có thể gọi các phương thức của {{jsxref("String")}} object lên một giá trị string nguyên văn (string literal) - JavaScript tự động chuyển đổi string literal sang một String object tạm, gọi phương thức để chạy, chạy xong hủy bỏ String object tạm đó. Bạn cũng có thể sử dụng thuộc tính String.length với một string literal:

+ +
console.log("John's cat".length)
+// Will print the number of symbols in the string including whitespace.
+// In this case, 10.
+
+ +

Trong ES2015, template literals cũng được đưa vào sử dụng. Template literals bao bọc trong dấu back-tick (`) (dấu huyền) thay vì dấu nháy đơn hay nháy kép.

+ +

Các template strings cung cấp cú pháp đặc biệt (synctactic sugar) để xây dựng các chuỗi (string). (Điều này tương tự với đặc điểm nội suy chuỗi string interpolation trong Perl, Python, v.v...)

+ +

Tùy trường hợp, một thẻ tag có thể được thêm vào để cho phép việc xây dựng chuỗi được tùy chỉnh, tránh injection attacks, hoặc xây dựng nên những cấu trúc dữ liệu higher-level từ các nội dung của chuỗi.

+ +
// Basic literal string creation
+// `In JavaScript '\n' is a line-feed.`
+
+// Multiline strings
+`In JavaScript, template strings can run
+ over multiple lines, but double and single
+ quoted strings cannot.`
+
+// String interpolation
+var name = 'Bob', time = 'today';
+`Hello ${name}, how are you ${time}?`
+
+// Construct an HTTP request prefix used to interpret the replacements and construction
+POST`http://foo.org/bar?a=${a}&b=${b}
+     Content-Type: application/json
+     X-Credentials: ${credentials}
+     { "foo": ${foo},
+       "bar": ${bar}}`(myOnReadyStateChangeHandler);
+
+ +

Bạn nên sử dụng các string literals đơn thuần nếu không cần thiết phải sử dụng đến String object. Xem {{jsxref("String")}} để biết chi tiết về String objects.

+ +

Sử dụng các ký tự đặc biệt trong chuỗi

+ +

Ngoài các ký tự thông thường, bạn cũng có thể thêm vào các ký tự đặc biệt trong chuỗi, ví dụ:

+ +
"one line \n another line"
+
+ +

Bảng dưới đây liệt kê danh sách các ký tự đặc biệt có thể sử dụng trong các chuỗi JavaScript.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table: JavaScript special characters
CharacterMeaning
\0Null Byte
\bBackspace - Khoảng cách
\fForm feed -
\nNew line - Dòng mới
\rCarriage return
\tTab - Tab một tab
\vVertical tab - Tab dọc
\'Apostrophe or single quote - trích dẫn đơn
\"Double quote - ngoặc kép.
\\Backslash character
\XXXThe character with the Latin-1 encoding specified by up to three octal digits XXX between 0 and 377. For example, \251 is the octal sequence for the copyright symbol.
\xXXThe character with the Latin-1 encoding specified by the two hexadecimal digits XX between 00 and FF. For example, \xA9 is the hexadecimal sequence for the copyright symbol.
\uXXXXThe Unicode character specified by the four hexadecimal digits XXXX. For example, \u00A9 is the Unicode sequence for the copyright symbol. See {{anch("Unicode escape sequences")}}.
+ +

Escaping characters

+ +

Đối với các ký tự không có trong bảng trên, dấu backslash \ được thêm vào trước sẽ bị bỏ qua, nhưng cách dùng này đã không còn nữa và nên được tránh dùng.

+ +

Bạn có thể chèn vào một dấu ngoặc kép bên trong một chuỗi bằng cách đặt phía trước nó một dấu backslash \. Điều này tức là escaping dấu trích dẫn. Ví dụ:

+ +
var quote = "He read \"The Cremation of Sam McGee\" by R.W. Service.";
+console.log(quote);
+
+ +

Kết quả của đoạn code trên là:

+ +
He read "The Cremation of Sam McGee" by R.W. Service.
+
+ +

Để thêm một dấu backslash thật sự vào trong chuỗi, bạn phải escape dấu backslash đó. Ví dụ, để gán dường dẫn file c:\temp vào một chuỗi:

+ +
var home = "c:\\temp";
+
+ +

Bạn cũng có thể escape các line breaks bằng cách đặt vào trước chúng một dấu backslash. Backslash và line break đều sẽ được remove khỏi giá trị của chuỗi.

+ +
var str = "this string \
+is broken \
+across multiple\
+lines."
+console.log(str);   // this string is broken across multiplelines.
+
+ +

Mặc dù JavaScript không có cú pháp "heredoc", bạn có thể gần đạt được điều này bằng cách thêm vào một linebreak escape và một escaped linebreak ở cuối mỗi dòng:

+ +
var poem =
+"Roses are red,\n\
+Violets are blue.\n\
+I'm schizophrenic,\n\
+And so am I."
+
+ +

Thông tin thêm

+ +

Chương này tập trung vào cú pháp cơ bản cho các việc khai báo và các kiểu dữ liệu. Để biết rõ hơn về cấu trúc ngôn ngữ JavaScript, xem các chương tiếp theo trong bộ hướng dẫn này:

+ + + +

Trong chương tiếp theo, chúng ta sẽ tìm hiểu về control flow constructs và error handling.

+ +

{{PreviousNext("Web/JavaScript/Guide/Introduction", "Web/JavaScript/Guide/Control_flow_and_error_handling")}}

diff --git a/files/vi/web/javascript/guide/details_of_the_object_model/index.html b/files/vi/web/javascript/guide/details_of_the_object_model/index.html new file mode 100644 index 0000000000..12101a6ddf --- /dev/null +++ b/files/vi/web/javascript/guide/details_of_the_object_model/index.html @@ -0,0 +1,742 @@ +--- +title: Details of the object model +slug: Web/JavaScript/Guide/Details_of_the_Object_Model +translation_of: Web/JavaScript/Guide/Details_of_the_Object_Model +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Working_with_Objects", "Web/JavaScript/Guide/Using_promises")}}
+ +

JavaScript là một ngôn ngữ dựa trên đối tượng và nguyên mẫu, hơn là dựa trên class. Bởi vì điều này, làm nó kém rõ ràng về cách mà JavaScript cho phép chúng ta tạo cấu trúc cây của đối tượng cũng như tạo sự kế thừa cho thuộc tính và giá trị của chúng. Chương này cố gắng làm rõ đặc điểm này.

+ +

Chương này giả định rằng bạn đã hiểu căn bản JavaScript và bạn đã sử dụng hàm của JavaScript để tạo đối tượng đơn giản.

+ +

Ngôn ngữ Class-based vs. Ngôn ngữ prototype-based

+ +

Ngôn ngữ dựa theo class, ví dụ Java và C++, tách biệt 2 thực thể chính: lớp (class) và thực thể (instance).

+ + + +

Ngôn ngữ dựa trên nguyên mẫu, như JavaScript, không tách biệt điều này, nó đơn giản chỉ là những object. Một ngôn ngữ dựa trên nguyên mẫu có ký hiệu của một prototypical object, một object được dùng làm mẫu mà từ mẫu đó nó tạo ra thuộc tính cho object mới. Hơn nữa, bất kỳ object nào cũng có thể được liên kết để dùng làm nguyên mẫu của một object khác, điều này cho phép object thứ hai chia sẽ thuộc tính với object thứ nhất.

+ +

Định nghĩa một class

+ +

Ngôn ngữ dựa trên class, bạn định nghĩa một class bằng từ khóa định nghĩa class riêng biệt rõ ràng. Trong đó bạn có thể chỉ định phương thức đặc biệt, gọi là hàm dựng, để khởi tạo thực thể của class. Một hàm dựng có thể khởi tạo giá trị cho thuộc tính của thực thể và thực hiện những xử lý phù hợp tại thời điểm đó. Bạn có thể dùng toán tử new cùng với tên class để tạo thực thể của class.

+ +

JavaScript cũng dùng mô hình tương tự, nhưng không có từ khóa riêng biệt để định nghĩa class (với phiên bản trước ES6). Thay vào đó, bạn có thể định nghĩa hàm dựng để tạo object với tập hợp các giá trị và thuộc tính ban đầu. Bất kỳ hàm JavaScript nào cũng có thể dùng như là constructor. Bạn có thể dùng từ toán tử new với hàm dựng để tạo object mới.

+ +

Class con và sự kế thừa

+ +

Trong ngôn ngữ dựa trên class, bạn tạo cây thứ tự của class thông qua những định nghĩa class. Khi định nghĩa một class, bạn có thể chỉ định class đang tạo là class con của một class khác đang tồn tại. Class con kế thừa tất cả các thuộc tính của class cha và có thể thêm vào những thuộc tính mới hoặc chỉnh sửa thuộc tính được kế thừa. Ví dụ, giả sử class Employee chỉ có thuộc tính namedept, và Manager là class con của Employee và định nghĩa thêm thuộc tính reports. Trong trường hợp này thực thể của class Manager sẽ có ba thuộc tính: name, dept, and reports

+ +

JavaScript cài đặt tính kế thừa bằng cách cho phép bạn liên kết một object mẫu với bất kỳ hàm dựng nào. Vì vậy, bạn có thể tạo chính xác như ví dụ EmployeeManager trên, nhưng thuật cú pháp sẽ nhưng hơi khác một tí. Trước tiên bạn định nghĩa hàm dựng Employee, và trong thân hàm khởi tạo thuộc tính namedept. Tiếp theo bạn định nghĩa hàm dựng Manager, mà nó gọi hàm dựng Employee và khởi tạo thuộc tính reports. Cuối cúng bạn gán object mới kế thừa từ Employee.prototype như prototype cho hàm được dựng Manager. Sau đó, khi tạo một thực thể Manager mới, nó sẽ kế thừa thuộc tính namedept của Employee.

+ +

Việc thêm và xóa những thuộc tính

+ +

Trong ngôn ngữ dựa trên class, bạn thông thường tạo một class lúc biên dịch và rồi bạn khởi tạo thực thể của class tại lúc biên dịch hoặc lúc thực thi. Bạn không thể thay đổi số lượng và kiểu của thuộc tính của class sau khi bạn định nghĩa class. Trong JavaScript, bạn có thể thêm hoặc xóa thuộc tính của bất kỳ object nào. Nếu bạn thêm một thuộc tính của một object mà được sử dụng như prototype của một tập các object, những object đó cũng sẽ có những thuộc tính mới.

+ +

Tóm tắt lại những khác biệt

+ +

Sau đây là bản tóm tắt ngắn những khác biệt. Phần còn lại của chương mô tả chi tiết việc sử dụng hàm dựng của JavaScript và prototype để tạo cấu trúc cây đối tượng và so sánh với phương pháp tương tự trong Java.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
So sánh class-based (Java) và prototype-based (JavaScript)
Class-based (Java)Prototype-based (JavaScript)
Class và thực thể của nó là hai đối tượng riêng biệt.Tất cả đối tượng có thể kế thừa từ một đối tượng khác.
Dùng cú pháp riêng của class để định nghĩa class; khởi tạo thực thể của class dùng hàm dựng của class.Định nghĩa và tạo một tập các đối tượng chỉ với hàm dựng.
Tạo object đơn với toán tử  new.Cùng cách như class-based.
Xây dựng cấu trúc cây object bằng cách sử dụng định nghĩa class để tạo class con của class đang tồn tại.Xây dựng cấu trúc cây object bằng cách gán một object như prototype của hàm dựng.
Kế thừa thuộc tính theo cơ chế class nối tiếp.Kế thừa những thuộc tính theo cơ chế prototype nối tiếp.
Khi định nghĩa class, bạn chỉ định tất cả các thuộc tính của các thực thể của class. Và không thể thêm thuộc tính mới lúc thực thi.Hàm dựng và prototype chỉ định giá trị ban đầu của thuộc tính. Và có thể thêm hoặc xoá thuộc tính động trên từng đối tượng hoặc toàn bộ các object.
+ +

Ví dụ Employee

+ +

Phần còn lại của chương này sử dụng cấu trúc cây nhân viên được trình bày như hình bên dưới.

+ +
+
+

Cấu trúc cây object đơn giản:

+ +

+
+ +
+
    +
  • Employee có những thuộc tính name (mà có giá trị mặc định là một chuỗi rỗng) và dept (mà giá trị mặc định là chuỗi "general").
  • +
  • Manager kế thừa từ Employee. Nó thêm thuộc tính reports (mà giá trị mặc định là một mảng rỗng sẽ dùng để lưu một mảng các Employee object)
  • +
  • WorkerBee cũng kế thừa từ Employee. Nó thêm thuộc tính projects (mà có giá trị mặc định là một mảng rỗng sẽ dùng để lưu trữ một mảng các giá trị kiểu chuỗi).
  • +
  • SalesPerson kế thừa từ WorkerBee. Nó thêm thuộc tính quota (mà có giá trị mặc định là 100). Nó cũng ghi đè giá trị của thuộc tính dept với giá trị "sales", để chỉ ra rằng tất cả salespersons thuộc cùng phòng ban.
  • +
  • Engineer kế thừa WorkerBee. Nó thêm vào thuộc tính machine (mà có giá trị mặc định là chuỗi rỗng) và cũng ghi đè thuộc tính dept với giá trị "engineering".
  • +
+
+
+ +

Tạo hệ thống cấp bậc

+ +

Có một vài cách để định nghĩa hàm dựng thích hợp để cài đặt hệ thống cấp bậc của nhân viên. Cách bạn muốn chọn để định nghĩa phụ thuộc phần lớn vào những gì bạn muốn làm trong ứng dụng của bạn.

+ +

Phần này sẽ trình bày cách định nghĩa rất đơn giản để minh họa cách thực hiện sự kế thừa. Trong định nghĩa này, bạn không thể chỉ định giá trị nào cho thuộc tính khi bạn tạo một object. Một cách đơn giản một object mới được ra và nhận những thuộc tính với giá trị mặc định, sau đó bạn có thể thay đổi giá trị của những thuộc tính đó.

+ +

Trong ứng dụng thực tế, bạn có thể định nghĩa hàm dựng mà cho phép bạn cung cấp những giá trị của thuộc tính tại thời điểm tạo object (xem thêm More flexible constructors). Bây giờ, ta chỉ dùng cách đơn giản để minh họa sự kế thừa.

+ +

Việc định nghĩa Employee trong Java và JavaScript thì khá tương tự. Sự khác biệt chỉ là bạn cần chỉ định kiểu của mỗi thuộc tính trong Java nhưng không cần trong JavaScript (bởi vì Java là ngôn ngữ kiểu ràng buộc mạnh trong khi JavaScript là ngôn ngữ kiểu rành buộc yếu.

+ +
+

JavaScript

+ +
function Employee() {
+  this.name = '';
+  this.dept = 'general';
+}
+
+ +


+ Java

+ +
public class Employee {
+   public String name = "";
+   public String dept = "general";
+}
+
+
+ +

Việc định nghĩa ManagerWorkerBee chỉ ra sự khác nhau trong cách chỉ định đối tượng cấp cao hơn trong chuỗi kế thừa nối tiếp. Trong JavaScript, bạn có thể thêm nguyên mẫu (trở thành class cha) như là thuộc tính prototype của hàm dựng, rồi ghi đè prototype.constructor lên hàm dựng. Bạn có thể làm điều này bất kỳ thời điểm nào sau khi đã định nghĩa hàm dựng. Trong Java, bạn chỉ định class cha trong khi định nghĩa class. Bạn không thể thay đổi class cha sau khi định nghĩa class.

+ +
+

JavaScript

+ +
function Manager() {
+  Employee.call(this);
+  this.reports = [];
+}
+Manager.prototype = Object.create(Employee.prototype);
+Manager.prototype.constructor = Manager;
+
+function WorkerBee() {
+  Employee.call(this);
+  this.projects = [];
+}
+WorkerBee.prototype = Object.create(Employee.prototype);
+WorkerBee.prototype.constructor = WorkerBee;
+
+ +


+ Java

+ +
public class Manager extends Employee {
+   public Employee[] reports =
+       new Employee[0];
+}
+
+
+
+public class WorkerBee extends Employee {
+   public String[] projects = new String[0];
+}
+
+
+
+
+ +

 

+ +

Việc định nghĩa EngineerSalesPerson giúp tạo objects theo thứ tự giảm dần từ Employee rồi xuống WorkerBee. Một object được tạo ra từ lớp con sẽ có tất cả thuộc tính của lớp cha ở trên trong chuỗi nối tiếp đó. Hơn nữa những định nghĩa ở lớp con có thể ghi đè những giá trị kế thừa từ lớp cha.

+ +
+

JavaScript

+ +
function SalesPerson() {
+   WorkerBee.call(this);
+   this.dept = 'sales';
+   this.quota = 100;
+}
+SalesPerson.prototype = Object.create(WorkerBee.prototype);
+SalesPerson.prototype.constructor = SalesPerson;
+
+function Engineer() {
+   WorkerBee.call(this);
+   this.dept = 'engineering';
+   this.machine = '';
+}
+Engineer.prototype = Object.create(WorkerBee.prototype)
+Engineer.prototype.constructor = Engineer;
+
+ +


+ Java

+ +
public class SalesPerson extends WorkerBee {
+   public String dept = "sales";
+   public double quota = 100.0;
+}
+
+
+public class Engineer extends WorkerBee {
+   public String dept = "engineering";
+   public String machine = "";
+}
+
+
+
+ +

Với việc sử dụng cách định nghĩa này, bạn có thể tạp hóa việc khởi tạo giá trị cho thuộc tính của thực thể với những giá trị mặc định cho các thuộc tính. Hình tiếp theo sẽ minh họa việc sử dụng cách định nghĩa trong JavaScript để tạo những object mới và hiển thị giá trị của thuộc tính của những object đó.

+ +
+

Lưu Ý: Thuật ngữ thực thể (instance) có ý nghĩa kỹ thuật cụ thể trong ngôn ngữ class-based. Trong ngôn ngữ này, một thực thể (instance) là một thực thể riêng biệt của class và là tách biệt với class. Trong JavaScript, "thực thể" không có khái niệm kỹ thuật riêng bởi vì JavaScript không tách biệt sự khác nhau giữa class và instance. Tuy nhiên, khi nói về JavaScript, "thực thể" có thể được sử dụng để ám chỉ những object được tạo ra bằng hàm dựng. Vì vậy, trong ví dụ này, bạn có thể nói jane là một thực thể của Engineer. Tương tự, mặc dù những thuật ngữ parent, child, ancestor, và descendant không có ý nghĩa chính thống trong JavaScript, bạn có thể sử dụng chúng để ám chỉ những đối tượng cấp cao hơn hoặc thấp hơn trong cây thừa kế.

+
+ +

Việc tạo những đối tượng bằng cách đơn giản

+ +
+

Cây object

+ +

Cây sau được tạo ra bằng những câu lệnh ở bên phải.

+ +

+ +

 

+ +

Individual objects = Jim, Sally, Mark, Fred, Jane, etc.
+ "Instances" created from constructor

+ +
var jim = new Employee;
+// Parentheses can be omitted if the
+// constructor takes no arguments.
+// jim.name is ''
+// jim.dept is 'general'
+
+var sally = new Manager;
+// sally.name is ''
+// sally.dept is 'general'
+// sally.reports is []
+
+var mark = new WorkerBee;
+// mark.name is ''
+// mark.dept is 'general'
+// mark.projects is []
+
+var fred = new SalesPerson;
+// fred.name is ''
+// fred.dept is 'sales'
+// fred.projects is []
+// fred.quota is 100
+
+var jane = new Engineer;
+// jane.name is ''
+// jane.dept is 'engineering'
+// jane.projects is []
+// jane.machine is ''
+
+
+ +

Thuộc tính của Object

+ +

Phần này mô tả cách mà những thuộc tính của những object có thể được kế thừa từ object khác trong chuỗi prototype và điều gì xãy ra khi bạn thêm một thuộc tính lúc thực thi.

+ +

Việc kế thừa thuộc tính

+ +

Giả định bạn tạo mark object như là một WorkerBee với câu lệnh sau:

+ +
var mark = new WorkerBee;
+
+ +

khi JavaScript thấy toán tử new, nó sẽ tạo một object mới và âm thầm đặt những giá trị của thuộc tính bên trong [[Prototype]] cho WorkerBee.prototype và truyền đối tượng mới tạo như là giá trị của từ khóa this cho hàm dựng WorkerBee. Thuộc tính [[Prototype]] xác định chuỗi prototype được dùng để trả về cho những giá trị của thuộc tính. Một khi những thuộc tính được thiết lập, JavaScript trả về đối tượng mới và những câu lệnh gán thiết lập giá trị thuộc tính của đối tượng mark.

+ +

Quy trình này không đặt giá trị cho những thuộc tính kế thừa từ chuỗi prototype trực tiếp vào trong đối tượng mark. Khi bạn lấy giá trị của một thuộc tính, JavaScript đầu tiên sẽ kiểm tra xem nếu giá trị tồn tại trực tiếp trên chính đối tượng đó. Nếu có, giá trị sẽ được trả về. Nếu không tồn tại trên chính đối tượng đó, JavaScript kiểm tra chuỗi prototype (sử dụng thuộc tính [[Prototype]]). Nếu một object trong chuỗi prototype có giá trị cho thuộc tính, giá trị sẽ được trả về. Nếu không tồn tại thuộc tính, JavaScript sẽ trả lời là không có thuộc tính. Bằng cách này, đối tượng mark có những thuộc tính và những giá trị sau:

+ +
mark.name = '';
+mark.dept = 'general';
+mark.projects = [];
+
+ +

Đối tượng mark được gán những giá trị cục bộ cho thuộc tính name và  dept bằng hàm dựng Employee. Nó được gán giá trị cục bộ cho thuộc tính projects bằng hàm dựng WorkerBee. Điều này giúp tạo ra sự kế thừa của các thuộc tính trong JavaScript. Một vài điểm tinh tế trong qui trình này được mô tả trong Property inheritance revisited.

+ +

Bởi vì hàm dựng không cho phép bạn chỉ định những giá trị riêng của thực thể, những thông tin này là chung. Những giá trị của thuộc tính là mặc định được chia sẻ cho tất cả các đối tượng được tạo ra từ WorkerBee. Tất nhiên bạn có thể thay đổi giá trị của bất kỳ thuộc tính nào. Bạn có thể làm điều đó trên mark object bằng những câu lệnh sau:

+ +
mark.name = 'Doe, Mark';
+mark.dept = 'admin';
+mark.projects = ['navigator'];
+ +

Thêm thuộc tính

+ +

Trong JavaScript, bạn có thể thêm thuộc tính cho object lúc thực thi. Bạn không bị rành buộc phải sử dụng chỉ những thuộc tính được cung cấp bởi hàm dựng. Để thêm một thuộc tính cụ thể cho một object riêng lẻ, bạn có thể gán giá trị cho thuộc tính của object đó như sau:

+ +
mark.bonus = 3000;
+
+ +

Bây giờ object mark có thuộc tính bonus, những thực thể của WorkerBee không có thuộc tính này.

+ +

Nếu bạn thêm thuộc tính mới vào một object mà đang được sử dụng làm prototype cho một hàm dựng, bạn đang thêm thuộc tính đó cho tất cả các object mà kế thừa từ prototype. Ví dụ, bạn có thể thêm thuộc tính specialty cho tất cả employees bằng câu lệnh sau:

+ +
Employee.prototype.specialty = 'none';
+
+ +

Ngay sau khi thực thi câu lệnh này, đối tượng mark cũng có thuộc tính specialty với giá trị "none". Hình sau minh họa việc thêm thuộc tính vào prototype của Employee và sau đó ghi đè nó lên prototype của Engineer.

+ +


+ Adding properties

+ +

Những hàm dựng linh hoạt hơn

+ +

Những hàm dựng đã được trình bày ở trên không cho phép chúng ta chỉ định giá trị cho thuộc tính khi chúng ta tạo thực thể. Với Java, bạn có thể cung cấp đối số cho hàm dựng để khởi tạo giá trị của thuộc tính cho những thực thể. Hình sau minh họa cách để thực hiện điều này.
+  

+ +


+ Việc chỉ định thuộc tính trong hàm dựng, cách 1

+ +

Bảng minh họa sau trình bày cách định nghĩa những đối tượng này trong Java và JavaScript.

+ +
+

JavaScript

+ +

Java

+
+ +
+
function Employee(name, dept) {
+  this.name = name || '';
+  this.dept = dept || 'general';
+}
+
+ +

 

+ +

 

+ +

 

+ +

 

+ +

 

+ +
public class Employee {
+   public String name;
+   public String dept;
+   public Employee () {
+      this("", "general");
+   }
+   public Employee (String name) {
+      this(name, "general");
+   }
+   public Employee (String name, String dept) {
+      this.name = name;
+      this.dept = dept;
+   }
+}
+
+
+ +
+
function WorkerBee(projs) {
+
+ this.projects = projs || [];
+}
+WorkerBee.prototype = new Employee;
+
+ +

 

+ +

 

+ +

 

+ +
public class WorkerBee extends Employee {
+   public String[] projects;
+   public WorkerBee () {
+      this(new String[0]);
+   }
+   public WorkerBee (String[] projs) {
+      projects = projs;
+   }
+}
+
+
+ +
+
+function Engineer(mach) {
+   this.dept = 'engineering';
+   this.machine = mach || '';
+}
+Engineer.prototype = new WorkerBee;
+
+ +

 

+ +

 

+ +

 

+ +
public class Engineer extends WorkerBee {
+   public String machine;
+   public Engineer () {
+      dept = "engineering";
+      machine = "";
+   }
+   public Engineer (String mach) {
+      dept = "engineering";
+      machine = mach;
+   }
+}
+
+
+ +

Những định nghĩa này trong JavaScript sử dụng cú pháp đặc biệt để thiết lập giá trị mặc định:

+ +
this.name = name || '';
+
+ +

Trong JavaScript toán tử luận lý OR (||) ước lượng giá trị đầu tiên của đối số. Nếu đối số được chuyển đổi thành true, toán tử trả về giá trị của đối số đó. Ngược lại toán tử trả về đối số thứ hai. Vì vậy, đoạn mã trên kiểm tra xem nếu name có giá trị cho thuộc tính name. Nếu có, nó thiết lập giá trị đó cho this.name. Ngược lại, nó đặt giá trị chuỗi rỗng vào this.name. Chương này sử dụng cú pháp vắn tắt, tuy nhiên nó có thể làm chúng ta khó hiểu lúc ban đầu.

+ +
+

Lưu Ý: Cách này có thể không hoạt động như mong đợi nếu hàm dựng được gọi với đối số mà được chuyển đổi thành false (ví dụ như 0 (zero) và chuỗi rỗng (""). trong trường hợp này giá trị mặc định sẽ được chọn)

+
+ +

Với những định nghĩa này, khi bạn tạo một thực thể của một object, bạn có thể chỉ định giá trị cho những thuộc tính được định nghĩa cục bộ. Bạn có thể sử dụng những câu lệnh sau để tạo một Engineer:

+ +
var jane = new Engineer('belau');
+
+ +

Thuộc tính của Jane bây giờ là:

+ +
jane.name == '';
+jane.dept == 'engineering';
+jane.projects == [];
+jane.machine == 'belau';
+
+ +

Lưu ý là với những định nghĩa này, bạn không thể chỉ định giá trị khởi tạo cho một thuộc tính được kế thừa như thuộc tính name. Nếu bạn muốn chỉ định một giá trị khởi tạo cho thuộc tính được kế thừa trong JavaScript, bạn cần để thêm câu lệnh cho hàm khởi tạo.

+ +

Hàm tạo đã tạo một đối tượng chung và rồi sau đó những giá trị được khởi tạo cho những thuộc tính cục bộ trực tiếp của đối tượng mới. Bạn có thể khởi tạo thêm những thuộc tính bằng cách trực tiếp gọi những hàm dựng khác của chuỗi prototype. Hình sau sẽ minh họa điều đó.

+ +


+ Những giá trị đang được chỉ định trong hàm dựng, hình 2

+ +

Hãy xem xét một trong những định nghĩa một cách chi tiết. Đây là định nghĩa mới cho hàm dựng Engineer:

+ +
function Engineer(name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, 'engineering', projs);
+  this.machine = mach || '';
+}
+
+ +

Giả định rằng bạn tạo một đối tượng Engineer mới như sau:

+ +
var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+
+ +

JavaScript thực thi theo những bước sau:

+ +
    +
  1. Toán tử new tạo một object và thiết lập giá trị __proto__ của object mới tạo với giá trị là Engineer.prototype.
  2. +
  3. Toán tử mới sẽ truyền object mới cho hàm dựng Engineer như là giá trị của từ khóa this.
  4. +
  5. Hàm dựng tạo một thuộc tính mới được gọi là base cho đối tượng đó và gán giá trị của hàm dựng WorkerBee cho thuộc tính base. Điều này làm hàm dựng trở thành một phương thức của đối tượng Engineer. Tên của thuộc tính base thì không có gì đặc biệt. Bạn có thể dùng bất kỳ tên hợp lệ nào như là classBase, _base,... base đơn giản chỉ là một con trỏ đến hàm.
  6. +
  7. Hàm dựng gọi phương thức base, truyền vào hàm dựng như những đối số ("Doe, Jane" and ["navigator", "javascript"]"engineering"). Việc sử dụng tường minh "engineering" trong hàm dựng mà tất cả đối tượng Engineer có cùng giá trị cho thuộc tính được kế thừa dept, và giá trị ghi đè giá trị mà được kế thừa từ Employee.
  8. +
  9. Bởi vì base là phương thức của Engineer, JavaScript gắn từ khóa this với object được tạo ra ở bước 1. Vì thế hàm WorkerBee truyền các giá trị "Doe, Jane""engineering" cho hàm dựng Employee. Dựa trên giá trị trả về của hàm dựng Employee, hàm WorkerBee sử dụng những đối số còn lại để thiết lập giá trị cho thuộc tính projects.
  10. +
  11. Dựa trên giá trị trả về của phương thức base, hàm dựng Engineer khởi tạo thuộc tính machine của đối tượng với giá trị "belau".
  12. +
  13. Dựa trên giá trị trả về của hàm dựng, JavaScript gán đối tượng cho biến jane.
  14. +
+ +

Bạn có thể nghĩ rằng, việc phải gọi hàm dựng WorkerBee bên trong hàm dựng Engineer, để đảm bảo việc cài đặt sự kế thừa cho tất cả đối tượng Engineer. Điều này không quá quan trọng. Việc gọi hàm dựng WorkerBee để đảm bảo tất cả đối tượng Engineer được tạo ra với các thuộc tính được chỉ định trong tất cả các hàm dựng được gọi. Tuy nhiên nếu bạn thêm thuộc tính vào prototype của Employee hoặc WorkerBee, những thuộc tính đó không được kế thừa bởi đối tượng Engineer. Ví dụ, giả sử bạn có đoạn mã sau:

+ +
function Engineer(name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, 'engineering', projs);
+  this.machine = mach || '';
+}
+var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+Employee.prototype.specialty = 'none';
+
+ +

Đối tượng jane không kế thừa thuộc tính specialty. Bạn vẫn cần cài đặt tường minh prototype để đảm bảo sự kế thừa động. Giả sử bạn có đoạn mã sau:

+ +
function Engineer(name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, 'engineering', projs);
+  this.machine = mach || '';
+}
+Engineer.prototype = new WorkerBee;
+var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+Employee.prototype.specialty = 'none';
+
+ +

Bây giờ thuộc tính specialty của đối tượng jane là "none".

+ +

Một cách khác để kế thừa là dùng hàm phương thức call() / apply(). Hai đoạn mã sau là tương đương:

+ +
+
function Engineer(name, projs, mach) {
+  this.base = WorkerBee;
+  this.base(name, 'engineering', projs);
+  this.machine = mach || '';
+}
+
+ +
function Engineer(name, projs, mach) {
+  WorkerBee.call(this, name, 'engineering', projs);
+  this.machine = mach || '';
+}
+
+
+ +

Việc sử dụng phương thức call() làm cho code rõ ràng hơn và base không còn cần thiết nữa.

+ +

Xem lại sự kế thừa thuộc tính

+ +

Đoạn trên đã mô tả cách hàm dựng và prototype trong JavaScript cung cấp cơ chế kế thừa và cây kế thừa. Trong phần này, chúng ta sẽ nói về điểm tinh tế mà thật sự chưa phù hợp để mô tả ở những phần trước.

+ +

Dùng Giá Trị Cục Bộ hay là Giá Trị Được Kế  Thừa

+ +

Khi bạn truy xuất thuộc tính của object, JavaScript thực hiện những bước sau:

+ +
    +
  1. Kiểm tra nếu tồn tại thuộc tính trực tiếp, nó trả về giá trị của thuộc tính đó.
  2. +
  3. Nếu không có thuộc tính trực tiếp, kiểm tra trong chuỗi prototype (sử dụng thuộc tính __proto__).
  4. +
  5. Nếu một object trong chuỗi prototype chứa giá trị cho một thuộc tính đang tìm thì trả về giá trị của thuộc tính đó.
  6. +
  7. Nếu không tồn tại thuộc tính như vậy, thì object đó không có thuộc tính đang truy xuất.
  8. +
+ +

Kết quả nhận được của những bước trên phụ thuộc vào cách bạn định nghĩa đối tượng. Ví dụ sau minh họa những cách định nghĩa đối tượng:

+ +
function Employee() {
+  this.name = '';
+  this.dept = 'general';
+}
+
+function WorkerBee() {
+  this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+ +

Với cách định nghĩa này, giả định rằng bạn tạo đối tượng amy là thực thể của WorkerBee với đoạn lệnh sau:

+ +
var amy = new WorkerBee;
+
+ +

Đối tượng amy có một biến cục bộ (riêng) là projects. Các thuộc tính namedept thì không là thuộc tính riêng của amy mà được lấy ra từ thuộc tính __proto__. Vì vậy, đối tượng amy có những thuộc tính với những giá trị sau:

+ +
amy.name == '';
+amy.dept == 'general';
+amy.projects == [];
+
+ +

Giả định rằng bạn thay đổi giá trị của thuộc tính name trong prototype của Employee:

+ +
Employee.prototype.name = 'Unknown';
+
+ +

Bạn mong muốn rằng giá trị mới sẽ được thiết lập cho tất cả các thực thể của Employee. Nhưng điều nay không xãy ra.

+ +

Khi bạn tạo bất kỳ thực thể nào của đối tượng từ Employee, thì thực thể đó sẽ tạo thuộc tính name riêng trực tiếp trên chính thực thể đó (cụ thể đó là giá trị rỗng). Điều này có nghĩa là khi bạn thiết lập giá trị cho prototype của  WorkerBee bằng một đối tượng kiểu Employee, thì WorkerBee.prototype có lưu giá trị riêng cho thuộc tính name. Vì vậy, khi JavaScript tìm kiếm thuộc tính name của đối tượng amy (tạo ra từ WorkerBee), JavaScript tìm kiếm giá riêng cho thuộc tính đó trong WorkerBee.prototype. Do đó nó không tìm kiếm xa hơn trong chuỗi prototype.

+ +

Nếu bạn muốn thay đổi giá trị của một thuộc tính lúc thực thi và muốn giá trị mới đó áp dụng cho tất cả các thực thể được tạo ra, thì bạn không thể khởi tạo thuộc tính trong hàm dựng. Thay vào đó, ban thêm nó vào hàm vào trong thuộc tính prototype của hàm dựng. Ví dụ sau minh họa cách chúng ta đạt được mục đích này:

+ +
function Employee() {
+  this.dept = 'general';    // Note that this.name (a local variable) does not appear here
+}
+Employee.prototype.name = '';    // A single copy
+
+function WorkerBee() {
+  this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+var amy = new WorkerBee;
+
+Employee.prototype.name = 'Unknown';
+
+ +

Trong trường hợp này thuộc tính name của amy nhận giá trị "Unknown".

+ +

Tóm lại, nếu bạn muốn đặt giá trị mặc định cho thuộc tính và bạn muốn thay đổi giá trị mặc định lúc thực thi, bạn nên cài đặt thuộc tính trên prototype của hàm dựng, không phải tạo trực tiếp thuộc tính lúc thực thi hàm dựng.

+ +

Xác định mối quan hể của thực thể

+ +

Việc tìm kiếm thuộc tính trong JavaScript sẽ được thực hiện trên chính danh sách các thuộc tính riêng trực tiếp của object trước, nếu thuộc tính không được tìm thấy, nó sẽ tìm trên object đặc biệt là __proto__. Việc kiểm tra sẽ được thực thi đệ qui, qui trình này được gọi là "Tìm kiếm trong chuỗi prototype".

+ +

Thuộc tính đặc biệt __proto__ được tạo ra khi một đối tượng được tạo ra. Nó chính là giá trị được gán cho thuộc tính prototype của hàm dựng. Ví vậy biểu thức new Foo() tạo ra một object với __proto__ == Foo.prototype. Tiếp theo đó, những thay đổi trên thuộc tính Foo.prototype sẽ làm thay đổi thuộc tính cho tất cả đối tượng mà được tạo ra bởi new Foo().

+ +

Mỗi object đều cho thuộc tính __proto__ và mỗi hàm đều có thuộc tính prototype. Vì vậy những đối tượng có mối liên hệ thông qua cơ chế 'thừa kế prototype' với những đối tượng khác. Bạn có thể kiểm tra sự thừa kế bằng cách so sánh thuộc tính __proto__ của object với prototype của một hàm. JavaScript cung cấp một toán tử để thực hiện điều này là instanceof, Toán tử sẽ trả về kết quả true nếu đối tượng được kết thừa từ prototype của hàm. Như ví dụ sau:

+ +
var f = new Foo();
+var isTrue = (f instanceof Foo);
+ +

Để minh họa cụ thể hơn, chúng ta xem ví dụ sau. Giả định bạn đã có đoạn mã như định nghĩa trong Kế thừa thuộc tính. Tạo một đối tượng Engineer như sau:

+ +
var chris = new Engineer('Pigman, Chris', ['jsd'], 'fiji');
+
+ +

Với đối tượng nay, những câu lệnh sau tất cả đều đúng:

+ +
chris.__proto__ == Engineer.prototype;
+chris.__proto__.__proto__ == WorkerBee.prototype;
+chris.__proto__.__proto__.__proto__ == Employee.prototype;
+chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
+chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
+
+ +

Dựa trên những điểm trên, bạn có thể viết hàm instanceOf như sau:

+ +
function instanceOf(object, constructor) {
+   object = object.__proto__;
+   while (object != null) {
+      if (object == constructor.prototype)
+         return true;
+      if (typeof object == 'xml') {
+        return constructor.prototype == XML.prototype;
+      }
+      object = object.__proto__;
+   }
+   return false;
+}
+
+ +
Lưu Ý: Đoạn lệnh trên dùng toán tử typeof để kiểm tra object để kiểm tra đó là XML object, đây là cách khắc phục lỗi của JavaScript cho đối tượng XML.
+ +

Dùng hàm instanceOf trên, biểu thức luôn đúng:

+ +
instanceOf(chris, Engineer)
+instanceOf(chris, WorkerBee)
+instanceOf(chris, Employee)
+instanceOf(chris, Object)
+
+ +

Nhưng biểu thức sau thì sai:

+ +
instanceOf(chris, SalesPerson)
+
+ +

Thông tin toàn cục trong hàm dựng

+ +

Khi bạn định nghĩa hàm dựng, bạn cần cẩn thận nếu bạn thiết lập giá trị cho biến toàn cục. Giả sử bạn muốn giá trị ID duy nhất được tự động gán cho mỗi employee mới. Bạn có thể sử dụng cách định nghĩa sau cho Employee sau:

+ +
var idCounter = 1;
+
+function Employee(name, dept) {
+   this.name = name || '';
+   this.dept = dept || 'general';
+   this.id = idCounter++;
+}
+
+ +

Với định nghĩa này, khi bạn tạo một Employee mới, hàm dựng gán giá trị ID kế tiếp và rồi tăng biến đếm toàn cục ID lên một đơn vị. Vì vậy, những câu lệnh tiếp theo sau đây, victoria.id là 1 và harry.id là 2:

+ +
var victoria = new Employee('Pigbert, Victoria', 'pubs');
+var harry = new Employee('Tschopik, Harry', 'sales');
+
+ +

Thoạt nhìn mọi thứ có vẽ tốt. Tuy nhiên, idCounter được tăng lên mỗi lần một đối tượng Employee được tạo ra cho mọi trường hợp cả những trường hợp không mong muốn. Nếu bạn tạo toàn bộ cây cấp bậc Employee như hình trong chương này, hàm dựng Employee được gọi mỗi là bạn cài đặt một prototype. Giả sử bạn có đoạn lệnh sau:

+ +
var idCounter = 1;
+
+function Employee(name, dept) {
+   this.name = name || '';
+   this.dept = dept || 'general';
+   this.id = idCounter++;
+}
+
+function Manager(name, dept, reports) {...}
+Manager.prototype = new Employee;
+
+function WorkerBee(name, dept, projs) {...}
+WorkerBee.prototype = new Employee;
+
+function Engineer(name, projs, mach) {...}
+Engineer.prototype = new WorkerBee;
+
+function SalesPerson(name, projs, quota) {...}
+SalesPerson.prototype = new WorkerBee;
+
+var mac = new Engineer('Wood, Mac');
+
+ +

Giả sử ta không quan tâm những câu lệnh bên trong hàm dựng, thuộc tính base và gọi hàm base trong chuỗi prototype. Trong trường hợp này, ngay khi đối tượng mac được tạo, mac.id là 5.

+ +

Tuy theo yêu cầu của ứng dụng, nó có thể là vấn đề quan trọng hoặc không khi biến đếm được tăng lên. Nếu bạn quan tâm tính chính xác của giá trị tăng thêm của biến đếm, Một giải pháp cho vấn đề này là:

+ +
function Employee(name, dept) {
+   this.name = name || '';
+   this.dept = dept || 'general';
+   if (name)
+      this.id = idCounter++;
+}
+
+ +

Khi bạn tạo một thực thể của Employee để sử dụng như prototype, bạn không cung cấp đối số cho hàm dựng. Việc sử dụng định nghĩa của hàm dựng, khi bạn không cung cấp đối số, hàm dựng không gán giá trị cho id và không cập nhật biến đếm. Vì vậy, để một đối tượng Employee được gán id, bạn phải chỉ định tên cho employee. Trong ví dụ này, mac.id sẽ là 1.

+ +

Một cách khác, bạn có thể tạo một bản sao của đối tượng prototype của Employee để gán cho WorkerBee:

+ +
WorkerBee.prototype = Object.create(Employee.prototype);
+// instead of WorkerBee.prototype = new Employee
+
+ +

Không hỗ trợ đa kế thừa

+ +

Một vài ngôn ngữ hướng đối tượng cho phép đa kế thừa. Nghĩa là một object có thể kế thừa những thuộc tính và giá rị từ những đối tượng cha không liên quan.

+ +

Việc kế thừa những thuộc tính xảy ra lúc thực thi bởi JavaScript trong khi tìm kiếm chuỗi prototype của object cho một giá trị. Bởi vì một object chỉ có một prototype đơn, JavaScript không thể kế thừa động từ nhiều hơn một chuỗi prototype.

+ +

Trong JavaScript, bạn có thể áp dụng nhiều hàm dựng trong hàm dựng. Điều này chứng minh tính khả thi của đa kế thừa. Xem xét ví dụ sau:

+ +
function Hobbyist(hobby) {
+   this.hobby = hobby || 'scuba';
+}
+
+function Engineer(name, projs, mach, hobby) {
+   this.base1 = WorkerBee;
+   this.base1(name, 'engineering', projs);
+   this.base2 = Hobbyist;
+   this.base2(hobby);
+   this.machine = mach || '';
+}
+Engineer.prototype = new WorkerBee;
+
+var dennis = new Engineer('Doe, Dennis', ['collabra'], 'hugo');
+
+ +

Giả sử WorkerBee được định nghĩa như trên. Trong trường hợp này, đối tượng dennis có những thuộc tính sau:

+ +
dennis.name == 'Doe, Dennis';
+dennis.dept == 'engineering';
+dennis.projects == ['collabra'];
+dennis.machine == 'hugo';
+dennis.hobby == 'scuba';
+
+ +

Vì vậy dennis có thuộc tính từ hàm dựng Hobbyist. Tuy nhiên, giả sử sau đó bạn thêm một thuộc tính vào prototype của hàm dựng của Hobbyist:

+ +
Hobbyist.prototype.equipment = ['mask', 'fins', 'regulator', 'bcd'];
+
+ +

Đối tượng dennis không kế thừa thuộc tính mới này.

+ +
{{PreviousNext("Web/JavaScript/Guide/Working_with_Objects", "Web/JavaScript/Guide/Using_promises")}}
diff --git a/files/vi/web/javascript/guide/expressions_and_operators/index.html b/files/vi/web/javascript/guide/expressions_and_operators/index.html new file mode 100644 index 0000000000..a843b8d769 --- /dev/null +++ b/files/vi/web/javascript/guide/expressions_and_operators/index.html @@ -0,0 +1,920 @@ +--- +title: Biểu thức và toán tử +slug: Web/JavaScript/Guide/Expressions_and_Operators +translation_of: Web/JavaScript/Guide/Expressions_and_Operators +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Functions", "Web/JavaScript/Guide/Numbers_and_dates")}}
+ +

Chương này mô tả biểu thức và toán tử của JavaScript, bao gồm toán tử gán, toán tử so sánh, arithmetic, thao tác bit, toán tử luận lý, chuỗi, toán tử ba ngôi và nhiều hơn nữa.

+ +

Để xem danh sách đầy đủ và chi tiết các toán tử và biểu thức, mời truy cập vào reference.

+ +

Toán tử

+ +

JavaScript có các loại toán tử như sau. Phần này mô tả về các toán tử và có bao gồm thông tin về thứ tự ưu tiên của chúng.

+ + + +

JavaScript có cả toán tử hai ngôimột ngôi, và một toán tử đặc biệt ba ngôi, toán tử quan hệ. Toán tử hai ngôi yêu cầu hai toán hạng, một toán hạng ở trước và một toán hạng ở sau toán tử:

+ +
toán_hạng_1 toán_tử toán_hạng_2
+
+ +

Chẳng hạn, 3+4 hoặc x*y.

+ +

Toán tử một ngôi yêu cầu một toán tử, ở trước hoặc ở sau toán tử:

+ +
toán_tử toán_hạng
+
+ +

hoặc

+ +
toán_hạng toán_tử
+
+ +

Chẳng hạn, x++ hoặc ++x.

+ +

Toán tử gán

+ +

Toán tử gán gán giá trị cho toán hạng bên trái nó dựa theo giá trị của toán hạng bên phải nó. Toná tử gán đơn giản là toán tử bằng (=), gán giá trị cho toán hạng bên trái nó bằng giá trị của toán hạng bên phải nó. Vậy, x = y tức là gán giá trị của y cho x.

+ +

Ngoài ra còn có các toán tử gán kết hợp được liệt kê trong bảng dưới:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử gán kết hợp
TênViết tắtÝ nghĩa
Toán tử gánx = yx = y
Cộng xong rồi gánx += yx = x + y
Trừ xong rồi gánx -= yx = x - y
Nhân xong rồi gánx *= yx = x * y
Chia xong rồi gánx /= yx = x / y
Chia lấy dư xong rồi gánx %= yx = x % y
Luỹ thừa rồi gán{{experimental_inline}}x **= yx = x ** y
Dịch bit trái rồi gánx <<= yx = x << y
Dịch bit phải rồi gánx >>= yx = x >> y
Dịch phải kiểu unsigned rồi gánx >>>= yx = x >>> y
AND bit rồi gánx &= yx = x & y
XOR bit rồi gánx ^= yx = x ^ y
OR bit rồi gánx |= yx = x | y
+ +

Phân rã

+ +

Với phép gán phức tạp hơn, cú pháp phân rã rồi gán là một biểu thức JavaScript cho phép phân tách dữ liệu từ mảng hoặc object sử dụng cú pháp sao chép cấu trúc của mảng hoặc object literal.

+ +
var foo = ['one', 'two', 'three'];
+
+// không dùng phân rã
+var one   = foo[0];
+var two   = foo[1];
+var three = foo[2];
+
+// dùng phân rã
+var [one, two, three] = foo;
+ +

Toán tử so sánh

+ +

Toán tử so sánh so sánh toán hạng của nó và trả về giá trị luận lý dựa theo tính đúng sai của phép so sánh. Toán hạng có thể thuộc là số, chuỗi, luận lý, hoặc object. Chuỗi được so sánh theo thứ tự từ điển, qua giá trị mã Unicode. Trong nhiều trường hợp, nếu hai toán hạng không cùng kiểu, JavaScript sẽ tự động ép kiểu sao cho phù hợp. Hành vi này thường xảy ra khi so sánh dữ liệu kiểu số. Duy có hai toán tử so sánh, bao gồm ===!==, không tự động ép kiểu mà sẽ so sánh bằng chính xác. Bảng sau mô tả các toán tử so sánh bao gồm cả code mẫu:

+ +
var var1 = 3;
+var var2 = 4;
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử so sánh
Toán tửMô tảVí dụ trả về true/th>
Bằng (==)Trả về true nếu các toán hạng bằng nhau.3 == var1 +

"3" == var1

+ 3 == '3'
Không bằng (!=)Trả về true nếu các toán hạng không bằng nhau.var1 != 4
+ var2 != "3"
Bằng chính xác (===)Trả về true nếu các toán hạng bằng nhau và cùng kiểu. Xem thêm tại {{jsxref("Object.is")}} và sameness in JS.3 === var1
Không bằng chính xác (!==)Trả về true nếu các toán hạng không bằng nhau, hoặc khác kiểu.var1 !== "3"
+ 3 !== '3'
Lớn hơn (>)Trả về true nếu toán hạng bên trái lớn hơn toán hạng bên phải.var2 > var1
+ "12" > 2
Lớn hơn hoặc bằng (>=)Trả về true nếu toán hạng bên trái lớn hơn hoặc bằng toán hạng bên phải.var2 >= var1
+ var1 >= 3
Nhỏ hơn (<)Trả về true nếu toán hạng bên trái nhỏ hơn toán hạng bên phải.var1 < var2
+ "2" < 12
Nhỏ hơn hoặc bằng (<=)Trả về true nếu toán hạng bên trái ?nhỏ hơn hoặc bằng toán hạng bên phải.var1 <= var2
+ var2 <= 5
+ +
+

Lưu ý: (=>) không phải là toán tử, mà là cú pháp của Hàm mũi tên.

+
+ +

Toán tử số học

+ +

Toán tử số học nhận giá trị kiểu số (cả literal lẫn biến) là toán hạng của nó và trả về một giá trị kiểu số. Các toán tử số học thông thường là toán tử cộng (+), trừ (-), nhân (*), và chia (/). Những toán tử này hoạt động tương tự như trong các ngôn ngữ lập trình khác khi xử lý với số thực dấu phẩy động (nói chung, ?phép chia cho 0 sẽ trả về {{jsxref("Infinity")}}). Ví dụ:

+ +
1 / 2; // 0.5
+1 / 2 == 1.0 / 2.0; // this is true
+
+ +

Ngoài những toán tử số học thông thường (+, -, * /), JavaScript cung cấp thêm các toán tử số học được liệt kê trong bảng sau:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử số học
Toán tửMô tảVí dụ
Chia lấy dư (%)Toán tử hai ngôi. Trả về phần nguyên trong phép chia của hai toán hạng.12 % 5 trả về 2.
Tăng (++)Toán tử một ngôi. Cộng thêm một đơn vị cho toán hạng. Nếu dùng như tiền tố (++x), trả về giá trị sau khi cộng thêm một; nếu dùng như hậu tố (x++), trả về giá trị trước khi cộng thêm một.Nếu x bằng 3, rồi ++x sẽ thiết lập giá trị của x lên 4 và trả về 4, trong khi x++ trả về 3 và sau đó mới thiết lập giá trị của x lên 4.
Giảm (--)Toán tử một ngôi. Trừ đi một đơn vị cho toán hạng. Giá trị trả về tương tự như toán tử tăng.Nếu x bằng 3, rồi --x sẽ thiết lập giá trị của x về 2 và trả về 2, trong khi x-- trả về 3 và sau đó mới thiết lập giá trị của x về 2.
Phủ định một ngôi (-)Toán tử một ngôi. Trả về giá trị phủ định của toán hạng.Nếu x bằng 3, thì -x trả về -3.
Cộng một ngôi (+)Toán tử một ngôi. Ép kiểu toán hạng về dạng số học, nếu kiểu của toán hạng đó không phải là số.+"3" trả về 3.
+ +true trả về 1.
Toán tử luỹ thừa (**) {{experimental_inline}}Tính toán giá trị cơ số theo luỹ thừa số mũ, tức là, cơ sốsố mũ2 ** 3 trả về 8.
+ 10 ** -1 trả về 0.1.
+ +

Toán tử thao tác bit

+ +

Toán tử thao tác bit coi toán hạng của nó là một tập 32 bit (gồm 0 và 1), thay vì là kiểu thập phân, thập lục phân, hay bát phân. Chẳng hạn, số thập phân 9 được biểu diễn trong hệ nhị phân là 1001. Toán tử thao tác bit xử lý phép toán dựa theo dạng biểu diễn nhị phân ấy, nhưng trả về giá trị kiểu số thông thường trong JavaScript.

+ +

Bảng tóm tắt các toán tử thao tác bit của JavaScript.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử thao tác bit
Toán tửCách dùngMô tả
Toán tử AND bita & btrả về a one in each bit position for which the corresponding bits of both operands are ones.
Toán tử OR bita | btrả về a zero in each bit position for which the corresponding bits of both operands are zeros.
Toán tử XOR bita ^ btrả về a zero in each bit position for which the corresponding bits are the same.
+ [trả về a one in each bit position for which the corresponding bits are different.]
Toán tử NOT bit~ aInverts the bits of its operand.
Dịch tráia << bShifts a in binary representation b bits to the left, shifting in zeros from the right.
Dịch phải giữ nguyên dấua >> bShifts a in binary representation b bits to the right, discarding bits shifted off.
Dịch phải với 0a >>> bShifts a in binary representation b bits to the right, discarding bits shifted off, and shifting in zeros from the left.
+ +

Toán tử luận lý bit

+ +

Về lý thuyết, cơ chế hoạt động của toán tử luận lý bit được giải thích như sau:

+ + + +

Chẳng hạn, biểu diễn nhị phân của 9 là 1001, và biểu diễn nhị phân của 15 là 1111. Vậy nếu áp dụng toán tử luận lý bit vào các giá trị này, thì kết quả trả về sẽ được như trong bảng sau:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Ví dụ về toán tử thao tác bit
Biểu thứcKết quảMô tả nhị phân
15 & 991111 & 1001 = 1001
15 | 9151111 | 1001 = 1111
15 ^ 961111 ^ 1001 = 0110
~15-16~00000000...00001111 = 11111111...11110000
~9-10~00000000...00001001 = 11111111...11110110
+ +

Chú ý: tất cả 32 bit được đảo ngược bởi toán tử luận lý NOT có bit trái nhất đặt thành 1 để biểu diễn số âm (biểu diễn bù hai). ~x thực thi ra cùng một kết quả như -x - 1.

+ +

Toán tử dịch bit

+ +

Toán tử dịch bit nhận hai toán hạng: toán hạng thứ nhất là số lượng bit được dịch, còn toán hạng thứ hai chỉ ra vị trí bit để bắt đầu dịch. Hướng dịch bit phụ thuộc vào toán tử được sử dụng.

+ +

Toán tử dịch bit chuyển đổi toán hạng của nó thành dạng số nguyên 32-bit và trả về kết quả cùng kiểu với toán hạng bên trái.

+ +

Toán tử dịch bit được liệt kê theo danh sách sau.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử dịch bit
Toán tửMô tảVí dụ
Dịch trái
+ (<<)
Dịch toán hạng thứ nhất theo một lượng bằng toán hạng thứ hai sang trái. Các bit dịch trái bị tràn sẽ bị loại bỏ. Các bit 0 được dịch vào từ bên phải.9<<2 trả về 36, bởi vì 1001 dịch 2 bit sang trái sẽ thành 100100, tương ứng với 36.
Dịch phải bit giữ nguyên dấu (>>)Dịch toán hạng thứ nhất theo một lượng bằng toán hạng thứ hai sang phải. Các bit dịch phải bị tràn sẽ bị loại bỏ. Bản sao của bit trái nhất được dịch vào từ trái.9>>2 trả về 2, bởi vì 1001 dịch 2 bit sang phải sẽ thành 10, tương ứng với 2. Tương tự, -9>>2 trả về -3, bởi vì dấu vẫn được giữ nguyên.
Dịch phải bit với 0 (>>>)Dịch toán hạng thứ nhất theo một lượng bằng toán hạng thứ hai sang phải. Các bit dịch phải bị tràn sẽ bị loại bỏ. Bit 0 được dịch vào từ trái sang.19>>>2 trả về 4, bởi vì 10011 dịch 2 bit sang phải sẽ thành 100, tương ứng với 4. Với số không âm, toán tử này tương tự với dịch phải giữ nguyên dấu.
+ +

Toán tử luận lý

+ +

Toán tử logic thường được dùng với giá trị Boolean (kiểu logic); khi đó, chúng trả về giá trị Boolean. Tuy nhiên, toán tử &&|| thực tế trả về giá trị của một trong những toán hạng xác định, nên nếu hai toán tử này được dùng với giá trị không phải kiểu Boolean, chúng có thể trả về một giá trị không phải Boolean. Toán tử logic được mô tả trong bảng dưới.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Toán tử logic
Toán tửCách dùngMô tả
Logic AND (&&)expr1 && expr2Trả về expr1 nếu nó có thể biến đổi thành false; ngược lại, trả về expr2. Như vậy, khi dùng với giá trị Boolean, && trả về true nếu cả hai toán hạng đều là true; ngược lại, trả về false.
Logic OR (||)expr1 || expr2Trả về expr1 nếu nó có thể biến đổi thành true; ngược lại, trả về expr2. Vì vậy, khi dùng với giá trị Boolean, || trả về true nếu cả hai toán hạng đều là true; nếu cả hai false, trả về false.
Logic NOT (!)!exprTrả về false nếu toán hạng đứng ngay sau nó có thể biến đổi thành true; ngược lại, trả về true.
+ +

Ví dụ về các biểu thức có thể biến đổi thành false là những biểu thức khi thực thi trả về null, 0, NaN, chuỗi rỗng (""), hoặc undefined.

+ +

Sau đây là các ví dụ cho toán tử && (luận lý AND).

+ +
var a1 =  true && true;     // t && t trả về true
+var a2 =  true && false;    // t && f trả về false
+var a3 = false && true;     // f && t trả về false
+var a4 = false && (3 == 4); // f && f trả về false
+var a5 = 'Cat' && 'Dog';    // t && t trả về Dog
+var a6 = false && 'Cat';    // f && t trả về false
+var a7 = 'Cat' && false;    // t && f trả về false
+
+ +

Sau đây là các ví dụ cho toán tử || (luận lý OR).

+ +
var o1 =  true || true;     // t || t trả về true
+var o2 = false || true;     // f || t trả về true
+var o3 =  true || false;    // t || f trả về true
+var o4 = false || (3 == 4); // f || f trả về false
+var o5 = 'Cat' || 'Dog';    // t || t trả về Cat
+var o6 = false || 'Cat';    // f || t trả về Cat
+var o7 = 'Cat' || false;    // t || f trả về Cat
+
+ +

Sau đây là các ví dụ cho toán tử ! (luận lý NOT).

+ +
var n1 = !true;  // !t trả về false
+var n2 = !false; // !f trả về true
+var n3 = !'Cat'; // !t trả về false
+
+ +

Thực thi đoản-mạch

+ +

Vì các biểu thức luận lý được thực thi từ trái sang phải, ta có thể dùng chúng để thử tính "đoán-mạch" qua các quy định sau:

+ + + +

Quy định luận lý đảm bảo rằng các thực thi trên luôn đúng. Chú ý phần bất cứ gì trong các biểu thức trên không được thực thi, bởi vậy sẽ không xảy ra bất cứ tác dụng phụ nào.

+ +

Toán tử chuỗi

+ +

Ngoài toán tử so sánh, có thể dùng để so sánh chuỗi, toán tử ghép (+) ghép hai giá trị chuỗi lại với nhau, trả về một chuỗi mới là hợp của hai chuỗi cũ.

+ +

Chẳng hạn,

+ +
console.log('my ' + 'string'); // console logs the string "my string".
+ +

Toán tử gán rút gọn += cũng có thể dùng để ghép chuỗi.

+ +

Chẳng hạn,

+ +
var mystring = 'alpha';
+mystring += 'bet'; // "alphabet" sẽ ghép với giá trị này trước khi gán vào mystring.
+ +

Toán tử điều kiện (ba ngôi)

+ +

Toán tử điều kiện là toán tử duy nhất của JavaScript cần tới ba toán hạng. Kết quả có thể là một trong hai giá trị tuỳ theo điều kiện. Cú pháp:

+ +
điều_kiện ? giá_trị_1 : giá_trị_2
+
+ +

Nếu điều_kiện trả về true, toán tử có giá trị giá_trị_1. Ngược lại toán tử có giá trị giá_trị_2. Bạn có thể dùng toán tử điều kiện ở bất cứ đâu như một toán tử bình thường.

+ +

Chẳng hạn,

+ +
var status = (age >= 18) ? 'adult' : 'minor';
+
+ +

Đoạn code này gán giá trị "adult" cho biến status nếu age lớn hơn hoặc bằng 18. Ngược lại, nó gán giá trị "minor" cho status.

+ +

Toán tử dấu phẩy

+ +

Toán tử dấu phẩy (,) đơn thuần tính toán cả hai giá trị toán hạng của nó và trả về giá trị của toán hạng cuối. Toán tử này được dùng chủ yếu trong vòng lặp for, để cho phép cập nhật nhiều biến cùng lúc sau mỗi lần thực hiện vòng lặp.

+ +

Chẳng hạn, nếu a là một mảng 2-chiều với 10 phần tử mỗi chiều, đoạn code sau dùng toán tử dấu phẩy để cập nhật hai biến cùng một lúc. Console in ra giá trị của các phần tử nằm trong đường chéo:

+ +
var x = [0,1,2,3,4,5,6,7,8,9]
+var a = [x, x, x, x, x];
+
+for (var i = 0, j = 9; i <= j; i++, j--)
+  console.log('a[' + i + '][' + j + ']= ' + a[i][j]);
+
+ +

Toán tử một ngôi

+ +

Toán tử một ngôi là phép toán chỉ có duy nhất một toán hạng.

+ +

delete

+ +

Toán tử delete xoá một object, một thuộc tính của object, hoặc một phần tử ở chỉ mục xác định trong mảng. Cú pháp là:

+ +
delete objectName;
+delete objectName.property;
+delete objectName[index];
+delete property; // chỉ được khi cài trong lệnh with
+
+ +

Với objectName là tên của object, property là thuộc tính đang tồn tại, và index là giá trị nguyên của chỉ mục tương ứng với vị trí của thuộc tính trong mảng.

+ +

Dạng thứ tư chỉ hoạt động khi được cài trong lệnh with, để xoá một thuộc tính khỏi object.

+ +

Bạn có thể dùng toán tử delete để xoá biến được khởi tạo ngầm nhưng không thể xoá được các biến được khởi tạo bằng lệnh var.

+ +

Nếu toán tử delete thành công, nó đặt thuộc tính hoặc phần tử đó thành undefined. Toán tử delete trả về true nếu phép toán khả thi; nó trả về false nếu phép toán bất khả thi.

+ +
x = 42;
+var y = 43;
+myobj = new Number();
+myobj.h = 4;    // tạo thuộc tính h
+delete x;       // trả về true (có thể xoá nếu khởi tạo ngầm)
+delete y;       // trả về false (không thể xoá nếu khởi tạo bằng var)
+delete Math.PI; // trả về false (không thể xoá thuộc tính tiền định nghĩa)
+delete myobj.h; // trả về true (có thể xoá thuộc tính người dùng định nghĩa)
+delete myobj;   // trả về true (có thể xoá nếu khợi tạo ngầm)
+
+ +
Xoá phần tử mảng
+ +

Khi bạn xoá một phần tử của mảng, chiều dài mảng không bị ảnh hưởng. Chẳng hạn, nếu bạn xoá a[3], a[4] vẫn là a[4]a[3] là undefined.

+ +

Khi toán tử delete loại bỏ một phần tử khỏi mảng, phần tử đó không còn tồn tại trong mảng. Trong ví dụ sau, trees[3] đã được loại bỏ bởi delete. Tuy nhiên, trees[3] vẫn có thể được trỏ tới và trả về undefined.

+ +
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+delete trees[3];
+if (3 in trees) {
+  // sẽ không chạy vào đây
+}
+
+ +

Nếu bạn muốn kiểm tra sự tồn tại của một phần tử trong mảng nhưng có giá trị là undefined, hãy dùng từ khoá undefined thay cho toán tử delete. Trong ví dụ tiếp sau đây, trees[3] được gán giá trị undefined, nhưng phần tử của mảng vẫn tồn tại:

+ +
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+trees[3] = undefined;
+if (3 in trees) {
+  // sẽ chạy vào đây
+}
+
+ +

typeof

+ +

Toán tử typeof có thể dùng theo cả hai cách dưới đây:

+ +
typeof operand
+typeof (operand)
+
+ +

Toán tử typeof trả về một chuỗi ký tự thể hiện kiểu của toán hạng. operand có thể là chuỗi ký tự, biến, từ khoá, hoặc object cần xác định kiểu. Không bắt buộc phải thêm dấu ngoặc tròn.

+ +

Giả sử bạn có các biến sau:

+ +
var myFun = new Function('5 + 2');
+var shape = 'round';
+var size = 1;
+var foo = ['Apple', 'Mango', 'Orange'];
+var today = new Date();
+
+ +

Toán tử typeof trả về kết quả lần lượt cho từng biến:

+ +
typeof myFun;       // trả về "function"
+typeof shape;       // trả về "string"
+typeof size;        // trả về "number"
+typeof foo;         // trả về "object"
+typeof today;       // trả về "object"
+typeof doesntExist; // trả về "undefined"
+
+ +

Với từ khoá truenull, toán tử typeof trả về kết quả sau:

+ +
typeof true; // trả về "boolean"
+typeof null; // trả về "object"
+
+ +

Với số hoặc chuỗi ký tự, toán tử typeof trả về kết quả sau:

+ +
typeof 62;            // trả về "number"
+typeof 'Hello world'; // trả về "string"
+
+ +

Với giá trị thuộc tính, toán tử typeof trả về kiểu dữ liệu mà thuộc tính đó bao hàm:

+ +
typeof document.lastModified; // trả về "string"
+typeof window.length;         // trả về "number"
+typeof Math.LN2;              // trả về "number"
+
+ +

Với phương thức hoặc hàm, toán tử typeof trả về kết quả như sau:

+ +
typeof blur;        // trả về "function"
+typeof eval;        // trả về "function"
+typeof parseInt;    // trả về "function"
+typeof shape.split; // trả về "function"
+
+ +

Với các object tiền định nghĩa, toán tử typeof trả về kết quả như sau:

+ +
typeof Date;     // trả về "function"
+typeof Function; // trả về "function"
+typeof Math;     // trả về "object"
+typeof Option;   // trả về "function"
+typeof String;   // trả về "function"
+
+ +

void

+ +

Toán tử void operator có thể dùng theo cả hai cách dưới đây:

+ +
void (expression)
+void expression
+
+ +

Toán tử void xác định biểu thực cần thực hiện mà không trả về giá trị nào. expression là một biểu thức JavaScript cần thực hiện. Dấu ngoặc tròn bao quanh expression là không bắt buộc, nhưng sẽ rất phong cách nếu dùng chúng.

+ +

Bạn có thể dùng toán tử void để xác định biểu thức cần thực thi trong một siêu liên kết. Biểu thức sẽ được thực hiện nhưng không văn bản hiện tại sẽ không tải lại tại chỗ.

+ +

Đoạn code sau tạo ra một siêu liên kết không thực hiện bất cứ điều gì khi có người dùng nhấn vào. Khi người dùng nhấn vào, void(0) thực hiện thành undefined, vốn không có hiệu ứng gì trong JavaScript.

+ +
<a href="javascript:void(0)">Click here to do nothing</a>
+
+ +

Đoạn code sẽ tiến hành hoàn tất mẫu đơn khi người dùng bấm vào siêu liên kết.

+ +
<a href="javascript:void(document.form.submit())">
+Click here to submit</a>
+ +

Toán tử quan hệ

+ +

Toán tử quan hệ so sánh các toán hạng của nó và trả về giá trị Boolean tuỳ thuộc phép so sánh có true hay không.

+ +

in

+ +

Toán tử in trả về true nếu thuộc tính nhất định có trong object nhất định. Cú pháp là:

+ +
propNameOrNumber in objectName
+
+ +

với propNameOrNumber là chuỗi ký tự hoặc số đại diện cho tên của thuộc tính hoặc chỉ mục mảng, và objectName là tên của object.

+ +

Các ví dụ dưới đây chỉ ra vài cách dùng toán tử in.

+ +
// Arrays
+var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
+0 in trees;        // trả về true
+3 in trees;        // trả về true
+6 in trees;        // trả về false
+'bay' in trees;    // trả về false (bạn phải xác định được chỉ mục của mảng,
+                   // không phải giá trị tại vị trí chỉ mục đó)
+'length' in trees; // trả về true (length là một thuộc tính của Array)
+
+// object dựng sẵn
+'PI' in Math;          // trả về true
+var myString = new String('coral');
+'length' in myString;  // trả về true
+
+// object tự tạo
+var mycar = { make: 'Honda', model: 'Accord', year: 1998 };
+'make' in mycar;  // trả về true
+'model' in mycar; // trả về true
+
+ +

instanceof

+ +

Toán tử instanceof trả về true nếu một object nhất định có kiểu của object nhất định. Cú pháp là:

+ +
objectName instanceof objectType
+
+ +

với objectName là tên của object để so sánh với objectType, và objectType là kiểu của object, như là {{jsxref("Date")}} hay {{jsxref("Array")}}.

+ +

Dùng instanceof khi bạn cần xác nhận kiểu của một object trong runtime (thời gian chạy). Chẳng hạn, khi bắt ngoại lệ, bạn có thể tìm tới từng ngoại lệ riêng biện tuỳ thuộc vào kiểu ngoại lệ được quăng (throw) ra.

+ +

Chẳng hạn, đoạn code dưới đây dùng instanceof để xác định xem liệu theDay có phải là một Date object hay không. Bởi vì theDay là một Date object, các lệnh trong sau điều kiện if được thực thi.

+ +
var theDay = new Date(1995, 12, 17);
+if (theDay instanceof Date) {
+  // lệnh sẽ được thực thi
+}
+
+ +

Thứ tự thực hiện toán tử

+ +

Thứ tự thực hiện của toán tử xác định thứ tự thực hiện trong một biểu thức. Bạn có thể vượt quyền ưu tiên bằng cách dùng dấu ngoặc tròn.

+ +

Bảng sau chỉ ra thứ tự thực hiện toán tử, từ cao nhất tới thấp nhất.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Thứ tự thực hiện các toán tử
?Loại toán tửIndividual operators
member. []
gọi / tạo mới instance() new
phủ định/tăng! ~ - + ++ -- typeof void delete
nhân/chia* / %
cộng/trừ+ -
dịch bit<< >> >>>
quan hệ< <= > >= in instanceof
bằng== != === !==
bitwise-and&
bitwise-xor^
bitwise-or|
logical-and&&
logical-or||
điều kiện?:
gán= += -= *= /= %= <<= >>= >>>= &= ^= |=
?dấu phẩy,
+ +

Bảng thứ tự thực hiện chi tiết hơn có thể tìm được trong JavaScript Reference.

+ +

Biểu thức

+ +

Biểu thức là đơn vị code hợp lệ mà giải được ra một giá trị.

+ +

Mọi biểu thức đúng cú pháp đều trả về vài giá trị nào đó nhưng về mặt lý thuyết, có hai kiểu biểu thức: kèm tác dụng phụ (chẳng hạn: những biểu thức gán giá trị cho biến nào đó) và những biểu thức thực hiện gì đấy rồi trả về giá trị.

+ +

Biểu thức x = 7 là ví dụ cho kiểu thứ nhất. Biểu thức này dùng toán tử = để gán giá trị cho biến x. Tự biểu thức trả về 7.

+ +

Đoạn code 3 + 4 là ví dụ cho kiểu biểu thức thứ hai. Biểu thức này dùng toán tử + để thêm bốn vào ba mà không gán kết quả, bảy, cho một biến nào.
+
+ JavaScript có các loại biểu thức sau đây:

+ + + +

Biểu thức nguyên thuỷ

+ +

Từ khoá căn bản và biểu thức chung trong JavaScript.

+ +

this

+ +

Dùng từ khoá this để trỏ đến object hiện tại. Tổng quát, this trỏ tới object đang gọi trong một phương thức. Có thể dùng this cùng với dấu chấm hoặc dấu ngoặc vuông:

+ +
this['propertyName']
+this.propertyName
+
+ +

Giả sử hàm validate xác thực thuộc tính value của object, truyền vào cận trên và cận dưới của nó:

+ +
function validate(obj, lowval, hival) {
+  if ((obj.value < lowval) || (obj.value > hival))
+    console.log('Invalid Value!');
+}
+
+ +

Bạn có thể gọi validate trong mỗi bộ xử lý sự kiện onChange trong form, dùng this để truyền vào phần tử của form, như trong ví dụ sau:

+ +
<p>Enter a number between 18 and 99:</p>
+<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">
+
+ +

Toán tử nhóm

+ +

Toán tử nhóm ( ) kiểm soát thứ tự thực hiện trong biểu thức. Chẳng hạn, bạn có thể cho phép nhân và chia thực hiện sau khi cộng và trừ.

+ +
var a = 1;
+var b = 2;
+var c = 3;
+
+// thứ tự mặc định
+a + b * c     // 7
+// mặc định sẽ thực hiện như thế này
+a + (b * c)   // 7
+
+// giờ vượt thứ tự nào
+// cộng trước rồi mới nhân
+(a + b) * c   // 9
+
+// tương tự như
+a * c + b * c // 9
+
+ +

Toán tử vế-trái

+ +

Giá trị bên trái là đích của phép gán.

+ +

new

+ +

Bạn có thể dùng toán tử new để tạo ra một instance thuộc kiểu object mà người-dùng-định-nghĩa hoặc một trong những kiểu object dựng-sẵn. Dùng new như sau:

+ +
var objectName = new objectType([param1, param2, ..., paramN]);
+
+ +

super

+ +

Từ khoá super được dùng để gọi hàm thuộc cha của object. Thường dùng bởi classes để gọi phương thức khởi tạo của lớp cha, chẳng hạn.

+ +
super([arguments]); // gọi phương thức khởi tạo của lớp cha.
+super.functionOnParent([arguments]);
+
+ +

Toán tử Spread

+ +

Toán tử spread cho phép biểu thức mở rộng tại chỗ khi có nhiều tham số (khi gọi hàm) hoặc nhiều phần tử (với array literal).

+ +

Ví dụ: Nếu bạn có một mảng và muốn tạo một mảng mới với mảng cũ là một thành phần trong nó, cú pháp của array literal không bao giờ là đủ và bạn bắt buộc phải code chay, dùng tổ hợp push, splice, concat, vân vân. Với cú pháp spread, việc này súc tích hơn hẳn:

+ +
var parts = ['shoulders', 'knees'];
+var lyrics = ['head', ...parts, 'and', 'toes'];
+ +

Tương tự, toán tử spread cũng hoạt động với lời gọi hàm:

+ +
function f(x, y, z) { }
+var args = [0, 1, 2];
+f(...args);
+ +
{{PreviousNext("Web/JavaScript/Guide/Functions", "Web/JavaScript/Guide/Numbers_and_dates")}}
diff --git a/files/vi/web/javascript/guide/functions/index.html b/files/vi/web/javascript/guide/functions/index.html new file mode 100644 index 0000000000..069a43ef80 --- /dev/null +++ b/files/vi/web/javascript/guide/functions/index.html @@ -0,0 +1,668 @@ +--- +title: Functions +slug: Web/JavaScript/Guide/Functions +tags: + - Bắt đầu + - Hướng dẫn + - JavaScript + - hàm +translation_of: Web/JavaScript/Guide/Functions +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}
+ +

Functions are one of the fundamental building blocks in JavaScript. A function is a JavaScript procedure—a set of statements that performs a task or calculates a value. To use a function, you must define it somewhere in the scope from which you wish to call it.

+ +

See also the exhaustive reference chapter about JavaScript functions to get to know the details.

+ +

Định nghĩa hàm

+ +

Khai báo hàm

+ +

Định nghĩa hàm (hay còn gọi là khai báo hằm, hoặc lệnh hàm) bao gồm từ khóa function, theo sau nó là:

+ + + +

Ví dụ, để định nghĩa một hàm có tên là square:

+ +
function square(number) {
+  return number * number;
+}
+
+ +

Hàm square nhận 1 tham số, có tên là number. Hàm này bao gồm một câu lệnh mà nó sẽ trả về tham số (number) nhân với chính nó. Câu lệnh return chỉ định giá trị được trả lại bới hàm.

+ +
return number * number;
+
+ +

Các tham số nguyên thủy (primitive parameters - ví dụ như một số) được truyền vào hàm bằng giá trị; giá trị được truyền vào hàm, nhưng nếu hàm thay đổi giá trị của tham số, sự thay đổi này sẽ không ánh xạ trên phạm vi global hoặc trên hàm gọi đến nó (nó sẽ không thay đổi giá trị của tham số được truyền vào ở phạm vi bên ngoài hàm).

+ +

Nếu bạn truyền vào hàm một tham số là object (một giá trị non-primitive), ví dụ như một mảng {{jsxref("Array")}} hoặc một object được user tự định nghĩa, và hàm thay đổi các thuộc tính của object, thay đổi đó sẽ có hiệu lực ngay cả ở phạm vi bên ngoài của hàm, giống như ví dụ dưới đây:

+ +
function myFunc(theObject) {
+  theObject.make = "Toyota";
+}
+
+var mycar = {make: "Honda", model: "Accord", year: 1998};
+var x, y;
+
+x = mycar.make; // x nhận giá trị "Honda"
+
+myFunc(mycar);
+y = mycar.make; // y nhận giá trị "Toyota"
+                // (thuộc tính make đã bị thay đổi bởi hàm myFunc)
+
+ +

Biểu thức hàm (hàm trong biến)

+ +

Trong khi việc khai báo hàm ở trên là một câu lệnh về mặt cú pháp, các hàm cũng có thể tạo ra bằng một biểu thức hàm (function expression). Một hàm như vậy có thể nặc danh; nó không cần phải có tên. Ví dụ, hàm square có thể được khai báo như sau:

+ +
const square = function(number) { return number * number }; // square lúc này là một hằng giúp nặc danh cho hàm gán cho nó
+var x = square(4) // x nhận giá trị 16
+ +

Tuy nhiên, một cái tên có thể được cung cấp trong một biểu thức hàm. Việc cung cấp tên cho phép hàm có thể chạy chính nó, hoặc có thể sử dụng hệ thống debug để nhận dạng hàm trong stack traces.

+ +
const factorial = function fac(n) { return n < 2 ? 1 : n * fac(n-1) };
+
+console.log(factorial(3));
+
+ +

Các biểu thức hàm rất tiện lợi trong việc truyền một hàm vào một hàm khác dưới vai trò một đối số (argument). Ví dụ sau cho thấy hàm map sẽ nhận một hàm khác là đối số đầu tiên và đối số thứ hai là một mảng.

+ +
function map(f,a) {
+  var result = [], // Create a new Array
+      i;
+  for (i = 0; i != a.length; i++)
+    result[i] = f(a[i]);
+  return result;
+}
+
+ +

Trong đoạn code dưới đây, hàm map nhận vào một hàm khác đã được định nghĩa bằng một biểu thức hàm, và map sẽ thực thi nó trên mọi phần tử của mảng (được truyền vào như một đối số thứ hai):

+ +
map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]);
+
+ +

Kết quả trả về: [0, 1, 8, 125, 1000].

+ +

Trong JavaScript, một hàm có thể được định nghĩa dựa trên một điều kiện. Ví dụ, việc định nghĩa hàm dưới đây sẽ định nghĩa hàm myFunc chỉ khi num bằng 0.

+ +
var myFunc;
+if (num == 0){
+  myFunc = function(theObject) {
+    theObject.make = "Toyota"
+  }
+}
+ +

Ngoài các cách định nghĩa hàm đã được mô tả, bạn cũng có thể sử dụng {{jsxref("Function")}} constructor to create functions from a string at runtime, much like {{jsxref("eval", "eval()")}}.

+ +

Một phương thức là một hàm mà hàm đó chính là thuộc tính của một object. Xem thêm về object và phương thức tại đây Working with objects.

+ +

Gọi hàm

+ +

Việc định nghĩa một hàm sẽ không thực thi nó. Định nghĩa một hàm đơn giản chỉ là đặt tên cho hàm và chỉ định những việc cụ thể sẽ làm khi hàm đó được gọi.

+ +

Gọi hàm thực chất là thi hành các hành động cụ thể với các tham số được chỉ định. Ví dụ, nếu bạn định nghĩa hàm square, bạn có thể gọi nó như sau:

+ +
square(5);
+
+ +

Câu lệnh bên trên gọi hàm với một đối số của 5. Hàm thực thi các câu lệnh của nó và trả về giá trị 25.

+ +

Các hàm phải đặt trong phạm vi (in scope) khi nó được gọi, nhưng việc khai báo hàm có thể được hoisted (câu lệnh khai báo hàm xuất hiện bên dưới dòng gọi hàm trong đoạn code), như ví dụ này:

+ +
console.log(square(5));
+/* ... */
+function square(n) { return n*n }
+
+ +

Phạm vi (scope) của một hàm là khoảng không gian bên trong hàm mà nó được khai báo (hoặc là cả chương trình, nếu nó được khai bảo ở top level, tức là nó không nằm trong hàm náo khác).

+ +
+

Ghi chú: Điều này chỉ đúng khi định nghĩa một hàm bằng cách sử dụng các cú pháp ở trên (ví dụ function funcName(){}). Đoạn code bên dưới sẽ không hoạt động.

+ +

Điều này có nghĩa rằng function hoisting chỉ hoạt động với cách khai báo hàm thông thường (function declarations) - function hoisting không hoạt động đối với hàm được khai báo bằng biểu thức hàm (function expression).

+
+ +
console.log(square); // ReferenceError: square is not defined
+console.log(square(5)); // ReferenceError: square is not defined
+square = function (n) {
+  return n * n;
+}
+
+ +

Các đối số của một hàm không bị giới hạn trong phạm vi các chuỗi và các số. Bạn có thể truyền các object hoàn chỉnh vào một hàm. Hàm show_props()(được định nghĩa trong Working with objects) là một ví dụ của một hàm mà nó nhận một object như là một đối số.

+ +

Một hàm có thể gọi chính nó. Ví dụ, đây là một hàm tính giai thừa đệ quy:

+ +
function factorial(n){
+  if ((n == 0) || (n == 1))
+    return 1;
+  else
+    return (n * factorial(n - 1));
+}
+
+ +

Bạn có thể tính giai thừa của 1 tới 5 như sau:

+ +
var a, b, c, d, e;
+a = factorial(1); // a gets the value 1
+b = factorial(2); // b gets the value 2
+c = factorial(3); // c gets the value 6
+d = factorial(4); // d gets the value 24
+e = factorial(5); // e gets the value 120
+
+ +

Có những cách khác để gọi hàm. Có nhiều trường hợp mà tại đó một hàm cần phải được gọi một cách tự động, hoặc làm thay đổi số lượng đối số truyền vào một hàm, hoặc trong trường hợp mà việc gọi hàm cần được gắn với một object nhất định được quyết định tại thời điểm runtime.

+ +

Điều đó lại hóa ra là các hàm tự bản thân chúng là các object, và kết quả là, những object này có các phương thức. (Xem {{jsxref("Function")}} object). Một trong số chúng, phương thức {{jsxref("Function.apply", "apply()")}}, có thể được dùng để đạt được mục tiêu này.

+ +

Phạm vi của hàm (function scope)

+ +

Các biến được định nghĩa bên trong một hàm không thể được truy cập từ nơi nào khác bên ngoài hàm, bởi vì biến đó được định nghĩa chỉ trong phạm vi của hàm. Tuy nhiên, một hàm có thể truy cập đến mọi biến và mọi hàm khác trong cùng phạm vi mà nó được định nghĩa.

+ +

Nói cách khác, một hàm được định nghĩa trong phạm vi global có thể truy cập tới tất cả các biến đã được định nghĩa trong phạm vi global. Một hàm được định nghĩa bên trong một hàm khác có thể truy cập đến tất cả biến được định nghĩa bên trong hàm cha của nó, và bất cứ biến nào khác mà hàm cha của nó có quyền truy cập đến.

+ +
// Các biến sau được định nghĩa trong phạm vi global scope
+var num1 = 20,
+    num2 = 3,
+    name = "Chamahk";
+
+// Hàm này được định nghĩa trong phạm vi global scope
+function multiply() {
+  return num1 * num2;
+}
+
+multiply(); // Returns 60
+
+// Một ví dụ hàm lồng nhau
+function getScore () {
+  var num1 = 2,
+      num2 = 3;
+
+  function add() {
+    return name + " scored " + (num1 + num2);
+  }
+
+  return add();
+}
+
+getScore(); // Returns "Chamahk scored 5"
+
+ +

Phạm vi và ngăn xếp của hàm

+ +

Recursion

+ +

Một hàm có thể tham chiếu và gọi chính nó. Có 3 cách để một hàm có thể tham chiếu đến chính nó:

+ +
    +
  1. Dùng tên của hàm
  2. +
  3. arguments.callee
  4. +
  5. Một biến in-scope mà có tham chiếu đến hàm.
  6. +
+ +

Ví dụ, xem xét việc định nghĩa hàm sau đây:

+ +
var foo = function bar() {
+   // statements go here
+};
+
+ +

Bên trong phần body của hàm, các điều sau là tương tự nhau:

+ +
    +
  1. bar()
  2. +
  3. arguments.callee()
  4. +
  5. foo()
  6. +
+ +

Một hàm mà gọi chính nó được gọi là (hàm đệ quy) recursive function. Trong một số cách hiểu, thì đệ quy (recursion) cũng tương tự như một vòng lặp. Cả hai đều là thực thi một đoạn code lặp đi lặp lại nhiều lần, và cả hai đều yêu cầu điều kiện xác định để chạy (để tránh lặp vô tận, hoặc recursion vô tận). Ví dụ, vòng lặp sau đây... 

+ +
var x = 0;
+while (x < 10) { // "x < 10" là điều kiện lặp
+   // thực thi việc sau
+   x++;
+}
+
+ +

...có thể được chuyển đổi sang một hàm đệ quy:

+ +
function loop(x) {
+  if (x >= 10) // "x >= 10" là điều kiện thoát ra (tương đương với "!(x < 10)")
+    return;
+  // do stuff
+  loop(x + 1); // the recursive call
+}
+loop(0);
+
+ +

Tuy nhiên, một số thuật toán không phải là các vòng lặp chỉ đơn giản là được lặp đi lặp lại. Ví dụ, việc lấy tất cả các nodes của một cấu trúc tree (như DOM) sẽ được thực hiện dễ dàng hơn thông qua đệ quy:

+ +
function walkTree(node) {
+  if (node == null) //
+    return;
+  // do something with node
+  for (var i = 0; i < node.childNodes.length; i++) {
+    walkTree(node.childNodes[i]);
+  }
+}
+
+ +

So với hàm loop, mỗi một lần gọi đệ quy sẽ tạo ra nhiều lần gọi đệ quy tại đây.

+ +

Bạn có thể chuyển đổi bất kỳ thuật toán đệ quy nào sang một dạng non-recursive, nhưng logic thường sẽ phức tạp hơn rất nhiều, và làm như vậy cũng đòi hỏi sử dụng một ngăn xếp (a stack).

+ +

Thực tế, việc đệ quy bản thân nó khi đệ quy có sử dụng một ngăn xếp: gọi là ngăn xếp hàm (function stack). Lối thực thi dạng ngăn xếp này có thể được tìm thấy trong ví dụ dưới đây:

+ +
function foo(i) {
+  if (i < 0)
+    return;
+  console.log('begin:' + i);
+  foo(i - 1);
+  console.log('end:' + i);
+}
+foo(3);
+
+// Output:
+
+// begin:3
+// begin:2
+// begin:1
+// begin:0
+// end:0
+// end:1
+// end:2
+// end:3
+ +

Hàm lồng nhau và các closures

+ +

Bạn có thể lồng một hàm bên trong một hàm khác. Hàm con (bên trong) được là private cho hàm chứa nó (hàm bao bên ngoài).

+ +

Điều đó cũng định hình nên một closure. Một closure là một biểu thức (thường thì chính là một hàm) mà biểu thức đó có thể có các biến tự do kết hợp với môi trường trói buộc chúng (hay nói cách khác là môi trường giúp "close" biểu thức).

+ +

Vì một hàm con là một closure, có nghĩa rằng hàm con có thể "thừa kế" các tham số và các biến của hàm cha. Nói cách khác, một hàm con sẽ chứa scope của hàm cha.

+ +

Tóm tắt lại:

+ + + +

Ví dụ sau chỉ ra các hàm được lồng nhau:

+ +
function addSquares(a,b) {
+  function square(x) {
+    return x * x;
+  }
+  return square(a) + square(b);
+}
+a = addSquares(2,3); // returns 13
+b = addSquares(3,4); // returns 25
+c = addSquares(4,5); // returns 41
+
+ +

Vì hàm con định hình nên một closure, bạn có thể gọi hàm cha đồng thời chỉ định các đối số cho cả hàm cha và hàm con.

+ +
function outside(x) {
+  function inside(y) {
+    return x + y;
+  }
+  return inside;
+}
+fn_inside = outside(3); // Kết quả trả về hàm inside(y)
+result = fn_inside(5); // Trả về 8
+
+result1 = outside(3)(5); // Trả về 8, các đối số được thêm đồng thời cùng lúc
+
+ +

Sự bảo tồn của các biến

+ +

Để ý cách mà x được bảo tồn sau khi hàm inside returne. Một closure phải bảo tồn các đối số và các biến bên trong mọi scope mà nó tham chiếu. Vì mỗi lần gọi hàm mang đến các tham số đôi khi khác nhau, nên một closure mới sẽ được tạo ra cho mỗi lần gọi hàm outside. Bộ nhớ bảo tồn này chỉ có thể được giải phóng khi hàm inside được return không còn khả dụng.

+ +

Điều này không khác gì so với lưu trữ các sự tham chiếu bên trong các object khác, nhưng điều này ít rõ ràng hơn vì nó không thiết lập các sự tham chiếu một cách trực tiếp và cũng không thể kiểm tra được chúng.

+ +

Các hàm lồng nhau nhiều cấp

+ +

Các hàm có thể được lồng nhau nhiều cấp. Ví dụ:

+ + + +

Do đó, các closures có thể chứa nhiều scope đa cấp; các closures sẽ bao gồm luôn cả phạm vi scope của các hàm chưa nó, việc bao gồm này có hình thức đệ quy. Đây gọi là scope chaining. (Lí do tại sao gọi là "chaining" sẽ giải thích sau).

+ +

Cân nhắc ví dụ sau:

+ +
function A(x) {
+  function B(y) {
+    function C(z) {
+      console.log(x + y + z);
+    }
+    C(3);
+  }
+  B(2);
+}
+A(1); // logs 6 (1 + 2 + 3)
+
+ +

Trong ví dụ này, C truy cập đến y của  B và x của  A. Điều này có thể đạt được bởi vì: 

+ +
    +
  1. B định hình một closure mà closure này bao gồm A, ví dụ B có thể truy cập đến các đối số và biến của A.
  2. +
  3. C định hình nên một closure mà closure này bao gồm B.
  4. +
  5. Vì closure của B bao gồm A, closure của C bao gồm AC có thể truy cập đến biến và đối số của cả hai hàm A và B. Nói cách khác, C chains the scopes của B và A, theo đúng thứ tự đó.
  6. +
+ +

Tuy nhiên nếu chạy ngược lại thì không đúng. A không thể truy cập đến C, vì A không thể truy cập đén các đối số và biến của B, mà C chính là một biến của B. Vì vậy C duy trì quyền truy cập private chỉ riêng đối với B.

+ +

Xung đột tên gọi

+ +

Khi hai đối số hoặc biến trong một scope của một closure có tên giống nhau, sẽ xảy ra xung đột tên gọi, scope nào nằm ở trong sâu hơn thì được ưu tiên. Cho nên, scope trong cùng sẽ mang ưu tiên cao nhất, trong khi scope ngoài cùng ưu tiên thấp nhất. Đây chính là scope chain (chuỗi xích phạm vi). Mắc xích đầu tiên của chain là scope trong cùng, và mắc xích cuối cùng của chain là scope ngoài cùng. Xem xét ví dụ sau:

+ +
function outside() {
+  var x = 10;
+  function inside(x) {
+    return x;
+  }
+  return inside;
+}
+result = outside()(20); // returns 20 thay vì 10
+
+ +

Xung đột tên gọi xảy ra tại câu lệnh return x giữa tham số x của hàm inside và tham số x của hàm outside. Scope chain ở đây là {inside, outside, global object}. Vì vậy tham số x của inside được ưu tiên hơn x của outside, và 20 (giá trị x của insisde) được trả về thay vì 10.

+ +

Closures

+ +

Closures là một trong những chức năng quyền lực nhất của JavaScript. JavaScript cho phép lồng các function vào nhau, và cấp quyền cho function con, để function con có toàn quyền truy cập vào tất cả các biến và function được định nghĩa bên trong function cha (và tất cả biến và function mà function cha được cấp quyền truy cập đến).

+ +

Tuy nhiên, function cha không có quyền truy cập đến các biến và function được định nghĩa bên trong function con. Điều này tạo nên một dạng bảo mật khép kín cho các biến của function con.

+ +

Bên cạnh đó, vì function con có quyền truy cập đến scope của function cha, các biến và function được định nghĩa bên trong function cha sẽ vẫn tồn tại dù việc thực thi function cha đã kết thúc, nếu function con xoay sở để tồn tại lâu hơn thời gian sống của function cha. Một closure được tạo ra khi một function con bằng cách nào đó trở nên khả dụng với bất kỳ scope nào bên ngoài function cha.

+ +
var pet = function(name) {   // Function cha định nghĩa một biến tên là "name"
+  var getName = function() {
+    return name;             // Function con có quyền truy cập đến biến "name" của function cha
+  }
+  return getName;            // Trả về function con, theo đó làm function con bị phơi bày ra phạm vi scope bên ngoài (không còn bị giới hạn bên trong function cha nữa)
+},
+myPet = pet("Vivie");
+
+myPet();                     // Returns "Vivie"
+
+ +

Thực tế sẽ phức tạp hơn nhiều so với đoạn code bên trên. Nó có thể trả về một object bao gồm các phương thức phục vụ cho việc điều khiển các biến bên trong một function cha.

+ +
var createPet = function(name) {
+  var sex;
+
+  return {
+    setName: function(newName) {
+      name = newName;
+    },
+
+    getName: function() {
+      return name;
+    },
+
+    getSex: function() {
+      return sex;
+    },
+
+    setSex: function(newSex) {
+      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
+        sex = newSex;
+      }
+    }
+  }
+}
+
+var pet = createPet("Vivie");
+pet.getName();                  // Vivie
+
+pet.setName("Oliver");
+pet.setSex("male");
+pet.getSex();                   // male
+pet.getName();                  // Oliver
+
+ +

Tron đoạn code trên, các function con có thể truy cập vào biến name của function cha, và không có cách nào khác để truy cập vào các biến của function con ngoại trừ thông qua function con. Các biến bên trong của function con đóng vai trò như kho lưu trữ an toàn cho các đối số và biến bên ngoài. Chúng giữ dữ liệu một cách kiên định, và nội bộ, để function con xử lý. Các functions thậm chí không cần phải gán vào bất kỳ biến nào, và cũng không cần tên.

+ +
var getCode = (function(){
+  var secureCode = "0]Eal(eh&2";    // Đoạn code chúng ta không muốn những thứ bên ngoài có thể thay đổi nó...
+
+  return function () {
+    return secureCode;
+  };
+})();
+
+getCode();    // Returns the secureCode
+
+ +

Lưu ý: Có một vài cạm bẫy cần đề phòng khi sử dụng các closures!

+ +

Nếu một function bị bọc kín định nghĩa một biến với tên trùng với tên của function bên ngoài, từ đó sẽ không có cách nào khác để tham chiếu đến biến của function bên ngoài nữa. (Lúc này biến của function bên trong đã ghi đè lên biến bên ngoài, cho đến khi chương trình thoát khỏi scope bên trong.)

+ +
var createPet = function(name) {  // Outer function defines a variable called "name"
+  return {
+    setName: function(name) {    // Enclosed function also defines a variable called "name"
+      name = name;               // ??? How do we access the "name" defined by the outer function ???
+    }
+  }
+}
+
+ +

The magical this variable is very tricky in closures. They have to be used carefully, as what this refers to depends completely on where the function was called, rather than where it was defined.

+ +

Sử dụng arguments object

+ +

Các đối số của một function được giữ dưới dạng một object dạng mảng. Trong phạm vi function, bạn có thể định vị các đối số được truyền vào function bằng cách sau:

+ +
arguments[i]
+
+ +

trong đó i là số thứ tự của đối số, bắt đầu từ 0. Vì vậy, đối số đầu tiên được truyền vào một function sẽ là arguments[0]. Tổng số đối số được xác định bằng arguments.length.

+ +

Sử dụng các arguments object, bạn có thể gọi một function với số đối số nhiều hơn số đối số được chấp nhận thông qua khai báo chính thức. Điều này sẽ hữu ích khi bạn không biết trước có bao nhiêu đối số sẽ được truyền vào function. Bạn có thể sử dụng arguments.length để xác định số lượng đối số thực tế được truyền vào function, và sau đó truy cập đến từng đối số bằng cách dùng arguments object. 

+ +

Ví dụ, xem xét một function có chức năng nối các string với nhau. Đối số chính thức duy nhất cho function là một string, và string này xác định những ký tự nào sẽ tách các phần tử ra để nối. Function được định nghĩa như sau:

+ +
function myConcat(separator) {
+   var result = "", // initialize list
+       i;
+   // iterate through arguments
+   for (i = 1; i < arguments.length; i++) {
+      result += arguments[i] + separator;
+   }
+   return result;
+}
+
+ +

Bạn có thể truyền vào bao nhiêu đối số vào function này cũng được, và nó sẽ nối từng đối số với nhau tạo thành một "list" có kiểu string.

+ +
// returns "red, orange, blue, "
+myConcat(", ", "red", "orange", "blue");
+
+// returns "elephant; giraffe; lion; cheetah; "
+myConcat("; ", "elephant", "giraffe", "lion", "cheetah");
+
+// returns "sage. basil. oregano. pepper. parsley. "
+myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
+
+ +
+

Ghi chú: Biến arguments nhìn giống mảng, nhưng nó không phải là một mảng. Nó giống mảng ở chỗ bên trong nó có các index được đánh số và nó có một thuộc tính length. Tuy nhiên, nó không sở hữu bất kỳ phương thức nào để thao tác sử dụng mảng.

+
+ +

Xem {{jsxref("Function")}} object trong JavaScript reference để biết thêm.

+ +

Các tham số của function

+ +

Kể từ ES6, xuất hiện 2 dạng tham số mới: default parameters và rest parameters

+ +

Default parameters

+ +

Trong JavaScript, các tham số của function được mặc định là undefined. Tuy nhiên, trong một số trường hợp nó có thể hữu ích để thiết lập một giá trị mặc định khác. Đây chính xác là điều mà default parameters sẽ làm.

+ +

Khi không có default parameters (trước ES6)

+ +

Trong quá khứ, chiến thuật thông thường để thiết lập các giá trị mặc định là kiểm định giá trị của các tham số bên trong body của function và gán giá trị cho nó nếu nó là undefined.

+ +

Trong ví dụ sau, nếu không có giá trị nào được truyền cho b, giá trị của nó sẽ là undefined khi thực hiện tính toán a*b, và việc gọi hàm multiply sẽ trả về NaN. Tuy nhiên, điều này bị ngăn chặn bởi dòng thứ 2 trong ví dụ này:

+ +
function multiply(a, b) {
+  b = typeof b !== 'undefined' ?  b : 1;
+
+  return a*b;
+}
+
+multiply(5); // 5
+
+ +

Khi có default parameters (sau ES6)

+ +

Với default parameters, việc kiểm tra thủ công bên trong body của function không còn cần thiết. Bạn có thể đơn giản chỉ là đặt 1 vào làm giá trị mặc định cho b ngay tại head của function:

+ +
function multiply(a, b = 1) {
+  return a*b;
+}
+
+multiply(5); // 5
+ +

Để chi tiết hơn, xem default parameters trong phần tham khảo.

+ +

Rest parameters

+ +

Cú pháp rest parameter cho phép chúng ta dùng 1 mảng để đại diện cho số lượng vô hạn các đối số.

+ +

Trong ví dụ sau, hàm multiply sử dụng rest parameters để thu thập các đối số kể từ đối số hứ hai trở về đến hết. Hàm này sau đó sẽ nhân những đối số này với đối số đầu tiên.

+ +
function multiply(multiplier, ...theArgs) {
+  return theArgs.map(x => multiplier * x);
+}
+
+var arr = multiply(2, 1, 2, 3);
+console.log(arr); // [2, 4, 6]
+ +

Arrow functions

+ +

Một arrow function expression (trước đây, và hiện tại được biết đến một cách không còn đúng là fat arrow function) có một cú pháp ngắn hơn function expressions và không có thisargumentssuper, or new.target của chính nó. Các arrow function luôn luôn là nặc danh. Xem "ES6 In Depth: Arrow functions".

+ +

Có 2 yếu tố dẫn đến việc giới thiệu arrow function: các function ngắn hơn và sự non-binding của this (lexical this).

+ +

Các function ngắn hơn

+ +

Trong một mẫu function, các function ngắn hơn được khuyến khích. So sánh:

+ +
var a = [
+  'Hydrogen',
+  'Helium',
+  'Lithium',
+  'Beryllium'
+];
+
+var a2 = a.map(function(s) { return s.length; });
+
+console.log(a2); // logs [8, 6, 7, 9]
+
+var a3 = a.map(s => s.length);
+
+console.log(a3); // logs [8, 6, 7, 9]
+ +

No separate this (Lexical this)

+ +

Trước khi có arrow functions, mọi function mới sẽ tự định nghĩa giá trị this của nó (a new object in the case of a constructor, undefined in strict mode function calls, the base object if the function is called as an "object method", etc.). Điều này đã được chứng minh là không lý tưởng đối với phong cách lập trình hướng đối tượng.

+ +
function Person() {
+  // Constructor của Person() tự định nghĩa `this`.
+  this.age = 0;
+
+  setInterval(function growUp() {
+    // Trong nonstrict mode, hàm growUp() định nghĩa `this`
+    // như là một global object, và global object này khác với `this`
+    // được định nghĩa bởi Person() constructor.
+    this.age++;
+  }, 1000);
+}
+
+var p = new Person();
+ +

Trong ECMAScript 3/5, vấn đề này được sửa chữa bằng cách gán giá trị bên trong this cho một biến mà biến đó có thể được đóng hoàn toàn.

+ +
function Person() {
+  var self = this; // Some choose `that` instead of `self`.
+                   // Choose one and be consistent.
+  self.age = 0;
+
+  setInterval(function growUp() {
+    // The callback refers to the `self` variable of which
+    // the value is the expected object.
+    self.age++;
+  }, 1000);
+}
+
+var p = new Person();
+
+ +

Predefined functions

+ +

JavaScript has several top-level, built-in functions:

+ +
+
{{jsxref("Global_Objects/eval", "eval()")}}
+
+

The eval() method evaluates JavaScript code represented as a string.

+
+
{{jsxref("Global_Objects/uneval", "uneval()")}} {{non-standard_inline}}
+
+

The uneval() method creates a string representation of the source code of an {{jsxref("Object")}}.

+
+
{{jsxref("Global_Objects/isFinite", "isFinite()")}}
+
+

The global isFinite() function determines whether the passed value is a finite number. If needed, the parameter is first converted to a number.

+
+
{{jsxref("Global_Objects/isNaN", "isNaN()")}}
+
+

The isNaN() function determines whether a value is {{jsxref("Global_Objects/NaN", "NaN")}} or not. Note: coercion inside the isNaN function has interesting rules; you may alternatively want to use {{jsxref("Number.isNaN()")}}, as defined in ECMAScript 6, or you can use typeof to determine if the value is Not-A-Number.

+
+
{{jsxref("Global_Objects/parseFloat", "parseFloat()")}}
+
+

The parseFloat() function parses a string argument and returns a floating point number.

+
+
{{jsxref("Global_Objects/parseInt", "parseInt()")}}
+
+

The parseInt() function parses a string argument and returns an integer of the specified radix (the base in mathematical numeral systems).

+
+
{{jsxref("Global_Objects/decodeURI", "decodeURI()")}}
+
+

The decodeURI() function decodes a Uniform Resource Identifier (URI) previously created by {{jsxref("Global_Objects/encodeURI", "encodeURI")}} or by a similar routine.

+
+
{{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent()")}}
+
+

The decodeURIComponent() method decodes a Uniform Resource Identifier (URI) component previously created by {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} or by a similar routine.

+
+
{{jsxref("Global_Objects/encodeURI", "encodeURI()")}}
+
+

The encodeURI() method encodes a Uniform Resource Identifier (URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two "surrogate" characters).

+
+
{{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent()")}}
+
+

The encodeURIComponent() method encodes a Uniform Resource Identifier (URI) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character (will only be four escape sequences for characters composed of two "surrogate" characters).

+
+
{{jsxref("Global_Objects/escape", "escape()")}} {{deprecated_inline}}
+
+

The deprecated escape() method computes a new string in which certain characters have been replaced by a hexadecimal escape sequence. Use {{jsxref("Global_Objects/encodeURI", "encodeURI")}} or {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} instead.

+
+
{{jsxref("Global_Objects/unescape", "unescape()")}} {{deprecated_inline}}
+
+

The deprecated unescape() method computes a new string in which hexadecimal escape sequences are replaced with the character that it represents. The escape sequences might be introduced by a function like {{jsxref("Global_Objects/escape", "escape")}}. Because unescape() is deprecated, use {{jsxref("Global_Objects/decodeURI", "decodeURI()")}} or {{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent")}} instead.

+
+
+ +

{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}

diff --git a/files/vi/web/javascript/guide/gioi-thieu/index.html b/files/vi/web/javascript/guide/gioi-thieu/index.html new file mode 100644 index 0000000000..f8fddcf666 --- /dev/null +++ b/files/vi/web/javascript/guide/gioi-thieu/index.html @@ -0,0 +1,137 @@ +--- +title: Giới thiệu +slug: Web/JavaScript/Guide/Gioi-thieu +tags: + - Guide + - JavaScript +translation_of: Web/JavaScript/Guide/Introduction +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide", "Web/JavaScript/Guide/Grammar_and_types")}}
+ +

Trong phần này: giới thiệu về JavaScript và thảo luận về một số khái niệm cơ bản của JavaScript.

+ +

Kiến thức nền tảng cần có

+ +

Để đến với JavaScript, chúng tôi giả sử rằng bạn đã có một số hiểu biết nền tảng:

+ + + +

Bạn có thể tìm thêm thông tin về JavaScript ở đâu?

+ +

Tài liệu về JavaScript tại MDN bao gồm:

+ + + +

Nếu bạn mới tìm hiểu JavaScript, hãy bắt đầu bằng cách đọc các bài viết tại learning areaJavaScript Guide. Một khi bạn đã nắm vững các nền tảng cơ bản, bạn có thể sử dụng JavaScript Reference để lấy những thông tin chi tiết của từng object và các câu lệnh (statements).

+ +

JavaScript là gì?

+ +

JavaScript là một ngôn ngữ lập trình đa nền tảng (cross-platform), ngôn ngữ lập trình kịch bản, hướng đối tượng. JavaScript là một ngôn ngữ nhỏ và nhẹ (small and lightweight). Khi nằm bên trong một môi trường (host environment), JavaScript có thể kết nối tới các object của môi trường đó và cung cấp các cách quản lý chúng (object).

+ +

JavaScript chứa các thư viện tiêu chuẩn cho các object, ví dụ như:  Array, Date, và Math, và các yếu tố cốt lõi của ngôn ngữ lập trình như: toán tử (operators), cấu trúc điều khiển (control structures), và câu lệnh. JavaScript có thể được mở rộng cho nhiều mục đích bằng việc bổ sung thêm các object; ví dụ:

+ + + +

JavaScript và Java

+ +

JavaScript và Java thì giống nhau ở những cái này nhưng lại khác nhau ở cái khác. Ngôn ngữ JavaScript có lẽ giống giống với ngôn ngữ Java nhưng JavaScript không có khai báo static cũng như không có "tính mạnh về kiểu" (strong type checking) như Java. Cú pháp (syntax) lập trình, đặt tên công thức và xây dựng điều khiển lưu lượng (control-flow) cơ bản của JavaScript phần lớn dựa theo ngôn ngữ lập trình Java, đó cũng là lý do tại sao JavaScript được đổi tên từ LiveScript thành JavaScript.

+ +

Ngược lại với hệ thống thời gian biên dịch (compile-time) Java của các lớp được xây dựng bởi các khai báo, JavaScript hỗ trợ nền tảng hệ thống thời gian chạy dựa trên một số lượng nhỏ các loại dữ liệu đại diện cho số, boolean và dữ liệu các chuỗi. JavaScript có một mô hình ít phổ biến hơn là mô hình đối tượng dựa trên nguyên mẫu (prototype-based) thay vì các mô hình đối tượng dựa trên lớp (class-based). Các mô hình dựa trên nguyên mẫu cung cấp khả năng thừa kế năng động; nghĩa là, những gì được kế thừa có thể khác nhau cho các đối tượng khác nhau. JavaScript cũng hỗ trợ các phương thức (function) không khai báo bất cứ gì ở trỏng. Phương thức có thể là một trong các thuộc tính (property) của các đối tượng, thực thi như là một phương thức đã được định kiểu (loosely typed methods).

+ +

JavaScript là một ngôn ngữ rất tự do so với Java. Bạn có thể không cần khai báo tất cả biến (variable), lớp (class) và cả phương thức (method). Bạn không cần quan tâm cho dù phương thức đó là public, private hoặc protected, và bạn không cần phải implement interfaces. Biến, tham số (parameters), và kiểu trả về của phương thức (function return) cũng không cần phải rõ ràng.

+ +

Java is a class-based programming language designed for fast execution and type safety. Type safety means, for instance, that you can't cast a Java integer into an object reference or access private memory by corrupting Java bytecodes. Java's class-based model means that programs consist exclusively of classes and their methods. Java's class inheritance and strong typing generally require tightly coupled object hierarchies. These requirements make Java programming more complex than JavaScript programming.

+ +

In contrast, JavaScript descends in spirit from a line of smaller, dynamically typed languages such as HyperTalk and dBASE. These scripting languages offer programming tools to a much wider audience because of their easier syntax, specialized built-in functionality, and minimal requirements for object creation.

+ + + + + + + + + + + + + + + + + + + + + + + +
JavaScript so sánh với Java
JavaScriptJava
Hướng đối tượng (Object-oriented). Không phân biệt giữa kiểu (type) của các đối tượng (object). Tính kế thừa thông qua cơ chế nguyên mẫu (prototype), và các thuộc tính (property) cũng như phương thức có thể thêm vào bất cứ đối tượng nào một cách năng động.Class-based (nền tảng lớp.).Đối tượng được thành các lớp với tất cả kế thừa thông qua hệ thống phân cấp lớp. Các lớp không thể thêm vào các thuộc tính và phương thức mới một cách năng động.
Không khai báo kiểu dữ liệu cho biến (dynamic typing).Phải khai báo kiểu dữ liệu cho biến (static typing).
Không thể tự động ghi vào ổ đĩa cứng.Có thể tự động ghi dữ liệu vào đĩa cứng.
+ +

Thêm thông tin về sự khác nhau giữa JavaScript và Java, xem chương: Details of the object model.

+ +

JavaScript và tiêu chuẩn ECMAScript

+ +

JavaScript được tiêu chuẩn hóa tại Ecma International — the European association for standardizing information and communication systems, Liên kết Châu Âu cho các tiêu chuẩn hóa hệ thống thông tin và truyền thông (ECMA trước đây là viết tắt cho the European Computer Manufacturers Association) cung cấp một tiêu chuẩn hóa, nền tảng ngôn ngữ lập trình mở quốc tế lên JavaScript. Phiên bản đã tiêu chuẩn hóa của JavaScript được gọi là ECMAScript, làm việc giống với cái cách mà tất cả ứng dụng đã được hỗ trợ theo tiêu chuẩn. Các công ty có thể sử dụng tiêu chuẩn ngôn ngữ mở (open standard language) để phát triển các implementation của JavaScript riêng cho họ. Tiêu chuẩn ECMAScript là tài liệu nằm trong tiêu chuẩn ECMA-262 (ECMA-262 specification) . Xem New in JavaScript để biết thêm về sự khác nhau giữa các phiên bản JavaScript cũng như sự khác nhau của phiên bản tiêu chuẩn ECMAScript (ECMAScript specification editions).

+ +

Tiêu chuẩn ECMA-262 cũng đã được phê duyệt bởi ISO (International Organization for Standardization) tại ISO-16262. Bạn cũng có thể tìm tiêu chuẩn trên the Ecma International website. Tiêu chuẩn ECMAScript không bao gồm các mô tả cho Document Object Model (DOM), nó được tiêu chuẩn hóa bởi World Wide Web Consortium (W3C). DOM định nghĩa cách mà các đối tượng trong HTML tiếp xúc với các đoạn script của bạn. Để có được một cảm nhận tốt hơn về các công nghệ khác nhau được sử dụng khi lập trình với JavaScript, hãy tham khảo bài viết tổng quan về công nghệ JavaScript.

+ +

Tài liệu JavaScript và tiêu chuẩn ECMAScript

+ +

Tiêu chuẩn ECMAScript là một tập hợp yêu cầu các việc cần thực hiện khi triển khai ECMAScript; nó rất là hữu ích nếu bạn muốn tạo ra một trình biên dịch tiêu chuẩn các tính năng của ngôn ngữ trong ECMAScript implementation hoặc bộ máy biên dịch của bạn (giống như SpiderMonkey của Firefox, hoặc v8 của Chrome).

+ +

Tài liệu ECMAScript được tạo ra không dự định hỗ trợ các lập trình viên script; sử dụng tài liệu JavaScript để lấy thông tin cho việc viết scripts của bạn.

+ +

Tiêu chuẩn ECMAScript sử dụng các thuật ngữ và cú pháp có thể các lập trình viên JavaScript chưa được làm quen. Mặc dù sự mô tả của ngôn ngữ có lẽ khác nhau trong ECMAScript, nhưng bản thân ngôn ngữ vẫn giữ nguyên, không thay đổi. JavaScript hỗ trợ tất cả chức năng được nêu trong tiêu chuẩn ECMAScript.

+ +

Tài liệu JavaScript mô tả các khía cạnh của ngôn ngữ lập trình JavaScript, thích hợp cho các lập trình viên JavaScript sử dụng.

+ +

Bắt đầu với JavaScript

+ +

Bắt đầu với JavaScript rất đơn giản: tất cả những gì bạn cần là một trình duyệt Web hiện đại. Trong các bài hướng dẫn có kèm theo một số tính năng JavaScript, mà nó chỉ chạy được ở các phiên bản trình duyệt mới nhất của Firefox, hoặc... cách tốt nhất là sử dụng một số phiên bản trình duyệt gần đây nhất của Firefox..

+ +

Có 2 công cụ được xây dựng trong Firefox, nó rất hữu ích để chạy các 'thí nghiệm' với JavaScript, đó là: Web ConsoleScratchpad.

+ +

Web Console

+ +

Web Console cho phép bạn thấy thông tin về trang Web đang chạy, và kèm theo một command line, với nó bạn có thể sử dụng để chạy một đoạn lệnh JavaScript trên trang Web hiện tại.

+ +

Mở Web Console bằng cách chọn "Web Console" từ menu "Web Developer" (Ctrl + Shift + I),  "Web Developer" nằm trong Menu chính của Firefox, nó có hình cờ lê, tên: Developer (nếu vẫn không thấy nó, bạn có thể mở menu và nhấn Customize để kéo nó ra ngoài). Sau khi mở lên, nó sẽ là 1 bảng hiển thị nằm phía dưới của cửa sổ trình duyệt. Có 1 ô nhập chạy dài dưới cùng của cửa sổ Web Console (khoanh vùng màu đỏ trong mình), nó chính là command line, với nó bạn có thể nhập vào đoạn JavaScript mà bạu muốn thực thi, và sau khi Enter thì trình duyệt sẽ chạy và trả về kết quả lên bảng Web Console nằm trên nó:

+ +

+ +

Scratchpad

+ +

Web Console có thể rất hiệu quả cho việc chạy đơn lẻ từng dòng lệnh của JavaScript, bạn cũng có thể chạy những đoạn lệnh nhiều dòng với nó (Ctrl + Enter)... Nhưng có vẻ nó không được tiện lợi cho lắm! Bạn không thể lưu lại code khi sử dụng Web Console. Với các 'thí nghiệm' dài và phức tạp thì Scratchpad là một công cụ hiệu quả.

+ +

Để mở Scratchpad, chọn "Scratchpad" từ menu "Web Developer" (Ctrl + Shift + I),  "Web Developer" nằm trong Menu chính của Firefox, nó có hình cờ lê, tên: Developer (nếu vẫn không thấy nó, bạn có thể mở menu và nhấn Customize để kéo nó ra ngoài).  Nó sẽ mở lên trong một cửa sổ window riêng với trình duyệt và là một trình soạn thảo mà bạn có thể sử dụng để viết và chạy JavaScript trong trình duyệt. Bạn cũng có thế lưu lại hoặc mở lên các đoạn script đó lên từ ổ đĩa.

+ +

Nếu bạn chọn "Inspect", đoạn code trong cửa sổ nãy sẽ chạy trong trình duyệt và xuất kết quả trở về bảng dưới dạng comment:

+ +

+ +

Hello world

+ +

Bắt đầu với JavaScript, mở Web Console hoặc Scarthpad và viết code JavaScript hiển thị "Hello world" đầu tiên của bạn:

+ +
function greetMe(user) {
+  return "Hi " + user;
+}
+
+greetMe("Alice"); // "Hi Alice"
+
+ +

Trong trang tiếp theo, chúng tôi sẽ giới thiệu cho bạn về cú pháp và các đặc tính của ngôn ngữ JavaScript, với nó, bạn sẽ có thể viết các ứng dụng phức tạp.

+ +

{{PreviousNext("Web/JavaScript/Guide", "Web/JavaScript/Guide/Grammar_and_types")}}

diff --git a/files/vi/web/javascript/guide/index.html b/files/vi/web/javascript/guide/index.html new file mode 100644 index 0000000000..d81ff019a4 --- /dev/null +++ b/files/vi/web/javascript/guide/index.html @@ -0,0 +1,124 @@ +--- +title: JavaScript Guide +slug: Web/JavaScript/Guide +tags: + - Hướng dẫn + - JavaScript +translation_of: Web/JavaScript/Guide +--- +

{{jsSidebar("JavaScript Guide")}}

+ +
+

Tài liệu về JavaScript cung cấp cho bạn cách sử dụng JavaScript và đưa ra cái nhìn tổng quan về ngôn ngữ này. Nếu muốn bắt đầu ngay với Javascript hay lập trình nói chung, hãy tham khảo các bài viết tại learning area.  Nếu cần thông tin đầy đủ về tính năng của ngôn ngữ này, hãy tham khảo JavaScript reference.

+
+ + + + + + + + + +

{{Next("Web/JavaScript/Guide/Introduction")}}

diff --git a/files/vi/web/javascript/guide/iterators_and_generators/index.html b/files/vi/web/javascript/guide/iterators_and_generators/index.html new file mode 100644 index 0000000000..e283bbb21d --- /dev/null +++ b/files/vi/web/javascript/guide/iterators_and_generators/index.html @@ -0,0 +1,187 @@ +--- +title: Iterators and generators +slug: Web/JavaScript/Guide/Iterators_and_Generators +translation_of: Web/JavaScript/Guide/Iterators_and_Generators +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}
+ +

Việc xử lý mỗi phần tử trong một tập hợp là thao tác rất phổ biến. JavaScript cung cấp một số cách để duyệt qua một tập hợp, từ đơn giản với lệnh lặp {{jsxref("Statements/for","for")}} đến {{jsxref("Global_Objects/Array/map","map()")}} và {{jsxref("Global_Objects/Array/filter","filter()")}}. Iterators và Generators đem đến khái niệm của iteration vào nhân của ngôn ngữ và cung cấp cách để tùy biến cơ chế của vòng lặp {{jsxref("Statements/for...of","for...of")}}.

+ +

Tham khảo thêm:

+ + + +

Iterators

+ +

Trong JavaScript một iterator là một object mà nó định nghĩa một trình tự và giá trị có thể trả về tiếp theo trước khi kết thúc. Một cách cụ thể hơn một iterator là một object bất kỳ mà nó cài đặt giao thức Iterator với việc cài đặt phương thức next() mà trả về một object với thuộc tính:  value, giá trị kế tiếp in chuỗi giá trị; và done, mà là true nếu giá trị  cuối cùng trong chuỗi giá trị đã được sử dụng. Nếu value trả về cùng với thuộc tính done, nó là giá trị trả về bởi lệnh return trong iterator đó.

+ +

Mỗi lần được tạo, một iterator có thể được duyệt tường minh bởi việc gọi lại nhiều lần phương thức next(). Việc duyệt qua iterator được gọi là sử dụng iterator, bởi vì nó chỉ có thể làm một lần. Sau khi giá trị kết thúc được trả về thì lần gọi next() luôn trả về {done: true}.
+
+ Iterator thông dụng nhất trong JavaScript là iterator mãng, mà đơn giản là trả về mỗi giá trị trong associated array theo thứ tự. Trong khi nó thì dễ dàng tưởng tượng rằng tất cả iterators được biểu diễn như mãng, điều này không đúng. Mảng phải được cấp phát toàn bộ, nhưng iterators được sử dụng chỉ khi cần thiết và vì vậy có thể biểu diễn một chuỗi không có giới hạn kích thước, như là một dãy các số nguyên từ 0 cho đến vô cực.
+
+ Sau đây là một ví dụ minh họa cho điều đó. Bạn có thể tạo ra một iterator khoảng giá trị đơn giản mà nó định nghĩa một dãy các số nguyên từ start (đã bao gồm) đến end (không bao gồm) với bước nhảy step. Giá trị trả về cuối cùng là kích thước của dãy giá trị mà nó đã tạo, ghi nhận trong biến iterationCount.

+ +
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
+    let nextIndex = start;
+    let iterationCount = 0;
+
+    const rangeIterator = {
+       next: function() {
+           let result;
+           if (nextIndex <= end) {
+               result = { value: nextIndex, done: false }
+               nextIndex += step;
+               iterationCount++;
+               return result;
+           }
+           return { value: iterationCount, done: true }
+       }
+    };
+    return rangeIterator;
+}
+ +

Việc sử dụng iterator được minh họa như trong đoạn mã sau:

+ +
let it = makeRangeIterator(1, 10, 2);
+
+let result = it.next();
+while (!result.done) {
+ console.log(result.value); // 1 3 5 7 9
+ result = it.next();
+}
+
+console.log("Iterated over sequence of size: ", result.value); // 5
+
+
+ +
+

Bạn không biết chắc một đối tượng cụ thể là một iterator hay không. Nếu bạn cần làm điều này, thì hãy dùng Iterables.

+
+ +

Generator functions

+ +

Iterator tùy biến là công cụ hữu ích, tuy nhiên việc tạo chúng đòi hỏi sự cẩn thận vì chúng ta cần phải tự quản lý trạng thái của chúng. Hàm Generator cung cấp một cách thức tốt hơn để thay thế việc đó: chúng cho phép bạn định nghĩa giải thuật duyệt bằng cách viết một hàm đơn mà sự thực thi của nó không diễn ra liên tục. Hàm Generator được viết bằng cách sử dụng cú pháp {{jsxref("Statements/function*","function*")}}. Khi gọi lần đầu, hàm generator không thực thi bất kỳ đoạn mã nào bên trong, thay vào đó nó trả về kiểu của iterator được gọi là generator. Khi một giá trị được dùng để trả về bởi lời gọi phương thức next() của generator, hàm generator thực thi cho đến khi nó bắt gặp từ khóa  yield.

+ +

Hàm có thể được gọi bao nhiêu lần cũng được và nó trả về một generator cho mỗi lần gọi, tuy nhiên generator có thể chỉ được duyệt một lần.
+
+ Chúng ta có thể điều chỉnh ví dụ trên. Chúng ta thấy đoạn mã sau cũng thực thi cùng chức năng, nhưng nó dễ viết và dễ đọc hơn.

+ +
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
+    let iterationCount = 0;
+    for (let i = start; i < end; i += step) {
+        iterationCount++;
+        yield i;
+    }
+    return iterationCount;
+}
+ +

Iterables

+ +

Một đối tượng là khả duyệt nếu nó định nghĩa cơ chế duyệt của nó, chẳng hạn như giá trị gì được duyệt qua trong hàm dựng {{jsxref("Statements/for...of", "for...of")}}. Một vài kiểu dựng sẳn, như  {{jsxref("Array")}} hoặc {{jsxref("Map")}}, có cơ chế duyệt mặc định, trong khi những kiểu khác (như là {{jsxref("Object")}}) thì không có.

+ +

Để có thể khả duyệt, một object phải cái đặt phương thức của @@iterator, nghĩa là object (hoặc đối tượng cha trong chuỗi  prototype) phải có một thuộc tính {{jsxref("Symbol.iterator")}}.
+ It may be possible to iterate over an iterable more than once, or only once. It is up to the programmer to know which is the case. Iterables which can iterate only once (e.g. Generators) customarily return this from their @@iterator method, where those which can be iterated many times must return a new iterator on each invocation of @@iterator.
+ Bạn có thể cho phép duyệt qua nhiều lần, hoặc chỉ một lần. Điều này phụ thuộc vào theo yêu cầu của mỗi ứng dụng. Đối tượng khả duyệt mà chỉ duyệt qua một lần (ví dụ Generators) đơn giản là nó chỉ trả về this từ phương thức  @@iterator, trong khi để khả duyệt nhiều lần bạn phải trả về một iterator mới cho mỗi lần gọi @@iterator.

+ +

Tự định nghĩa đối tượng khả duyệt

+ +

Chúng ta có thể tự tạo đối tượng khả duyệt như sau:

+ +
var myIterable = {
+    *[Symbol.iterator]() {
+        yield 1;
+        yield 2;
+        yield 3;
+    }
+}
+
+for (let value of myIterable) {
+    console.log(value);
+}
+// 1
+// 2
+// 3
+
+or
+
+[...myIterable]; // [1, 2, 3]
+
+ +

Những iterable tạo sẵn

+ +

{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}} and {{jsxref("Set")}} là những iterable đã được tạo sẵn, bởi vì đối tượng prototype của chúng đã có phương thức {{jsxref("Symbol.iterator")}}.

+ +

Cú pháp đòi hỏi đối tượng khả duyệt

+ +

Một vài câu lệnh và biểu thức đòi hỏi toán hạng hoặc đối số phải là đối tượng khả duyệt, ví dụ lệnh lặp {{jsxref("Statements/for...of","for-of")}}, {{jsxref("Operators/yield*","yield*")}}

+ +
for (let value of ['a', 'b', 'c']) {
+    console.log(value);
+}
+// "a"
+// "b"
+// "c"
+
+[...'abc']; // ["a", "b", "c"]
+
+function* gen() {
+  yield* ['a', 'b', 'c'];
+}
+
+gen().next(); // { value: "a", done: false }
+
+[a, b, c] = new Set(['a', 'b', 'c']);
+a; // "a"
+
+
+ +

Generator Cao Cấp

+ +

Generator tính toán giá trị trả ra tùy biến theo nhu cầu, nó cho phép biển diễn chuỗi giá trị một cách hiệu quả mà tốn kém để tính toán, hoặc thậm chí cho phép chuỗi giá trị vô tận như đã minh họa ở trên.
+  

+ +

The {{jsxref("Global_Objects/Generator/next","next()")}} method also accepts a value which can be used to modify the internal state of the generator. A value passed to next() will be treated as the result of the last yield expression that paused the generator.
+ Phương  thức {{jsxref("Global_Objects/Generator/next","next()")}} cũng chấp nhận một đối số mà được sử dụng để thay đổi trạng thái bên trong của generator. Một giá trị được truyền vào next() sẽ được dùng như kết quả của biểu thức yield cuối cùng khi tạm dừng generator.

+ +

Here is the fibonacci generator using next(x) to restart the sequence:

+ +
function* fibonacci() {
+  var fn1 = 0;
+  var fn2 = 1;
+  while (true) {
+    var current = fn1;
+    fn1 = fn2;
+    fn2 = current + fn1;
+    var reset = yield current;
+    if (reset) {
+        fn1 = 0;
+        fn2 = 1;
+    }
+  }
+}
+
+var sequence = fibonacci();
+console.log(sequence.next().value);     // 0
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 2
+console.log(sequence.next().value);     // 3
+console.log(sequence.next().value);     // 5
+console.log(sequence.next().value);     // 8
+console.log(sequence.next(true).value); // 0
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 1
+console.log(sequence.next().value);     // 2
+ +

Bạn có thể ép một generator phát sinh lỗi bằng cách gọi phương thức {{jsxref("Global_Objects/Generator/throw","throw()")}} của generator và chỉ định giá trị lỗi mà nó cần phát sinh. Lỗi này sẽ được phát sinh từ ngữ cảnh hiện tại của generator, tạo điểm mà yield đang tam dừng, thay vì lệnh throw value như cách thông thường.

+ +

Nếu lỗi không được bắt từ bên trong generator, nó sẽ được đẩy lên cấp cao hơn thông qua lời gọi hàm throw(), khi đó lời gọi tiếp theo tới next() sẽ trả về done với giá trị true.

+ +

Generator có một phương thức {{jsxref("Global_Objects/Generator/return","return(value)")}} mà giá trị được truyền vào và kết thúc chính nó.

+ +

{{PreviousNext("Web/JavaScript/Guide/Using_promises", "Web/JavaScript/Guide/Meta_programming")}}

diff --git a/files/vi/web/javascript/guide/keyed_collections/index.html b/files/vi/web/javascript/guide/keyed_collections/index.html new file mode 100644 index 0000000000..491efcf1da --- /dev/null +++ b/files/vi/web/javascript/guide/keyed_collections/index.html @@ -0,0 +1,153 @@ +--- +title: Keyed collections +slug: Web/JavaScript/Guide/Keyed_collections +translation_of: Web/JavaScript/Guide/Keyed_collections +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Indexed_Collections", "Web/JavaScript/Guide/Working_with_Objects")}}
+ +

Chương này sẽ giới thiệu về bộ sưu tập dữ liệu được sắp xếp theo key; Map và Set objects chứa các phần tử có thể được lặp lại theo thứ tự được thêm vào.

+ +

Maps

+ +

Map object

+ +

ECMAScript 2015 giới thiệu một cấu trúc dữ liệu mới để ánh xạ giá trị thành giá trị. Một đối tượng {{jsxref("Map")}} là một ánh xạ đơn giản key/value và có thể lặp lại các phần tử theo thứ tự thêm vào.

+ +

Đoạn code bên dưới sẽ thể hiện các thao tác cơ bản với Map. Xem thêm {{jsxref("Map")}} để có thêm nhiều ví dụ và toàn bộ API. Bạn có thể sử dụng một vòng lặp {{jsxref("Statements/for...of","for...of")}} để trả về một mảng của [key, value] trong mỗi lần lặp.

+ +
let sayings = new Map();
+sayings.set('dog', 'woof');
+sayings.set('cat', 'meow');
+sayings.set('elephant', 'toot');
+sayings.size; // 3
+sayings.get('fox'); // undefined
+sayings.has('bird'); // false
+sayings.delete('dog');
+sayings.has('dog'); // false
+
+for (let [key, value] of sayings) {
+  console.log(key + ' goes ' + value);
+}
+// "cat goes meow"
+// "elephant goes toot"
+
+sayings.clear();
+sayings.size; // 0
+
+ +

Object and Map compared

+ +

Thông thường, {{jsxref("Object", "objects", "", 1)}} được sử dụng để ánh xạ string thành value. Objects cũng cho phép bạn đặt key thành values, truy xuất các value, xóa các key, và kiểm tra xe có dữ liệu nào được lưu trữ trong một key. Tuy nhiên Map objects có một vài lợi thế hơn giúp chúng tốt hơn trong việc ánh xạ.

+ + + +

Ba mẹo dưới đây có thể giúp bạn quyết định khi nào thì sử dụng Map hoặc Object:

+ + + +

WeakMap object

+ +

Đối tượng {{jsxref("WeakMap")}} là một bộ sưu tập của cặp key/value trong đó các key chỉ là các đối tượng và các value có thể là bất kỳ giá trị nào. Tham chiếu đối tượng trong các key được giữ yếu ớt (weakly), có nghĩa là chúng là mục tiêu của garbage collection (GC) nếu không có tham chiếu nào khác đến đối tượng nữa. Các WeakMap API giống với các Map API.

+ +

Một điểm khác biệt so với các Map objects là các key của WeakMap không đếm được (tức là không có phương thức nào cung cấp cho bạn danh sách các key). Nếu có, danh sách key sẽ phụ thuộc vào trạng thái của garbage collection, đưa ra tính không xác định.

+ +

Một số thông tin và ví dụ khác, xem tại "Why WeakMap?" trong tài liệu tham khảo {{jsxref("WeakMap")}}.

+ +

Một trường hợp sử dụng các WeakMap objec là lưu trữ dữ liệu riêng tư cho một đối tượng hoặc ấn chi tiết thực hiện. Ví dụ sau đây từ bài đăng trên blog của Nick Fitzgerald's  "Hiding Implementation Details with ECMAScript 6 WeakMaps". Các dữ liệu và phương thức riêng bên trong object sẽ được lưu trữ trong đối tượng privates. Tất cả các instance và prototype lộ ra được công khai; mọi thứ khác không thể truy cập từ bên ngoài bởi vì privates không được xuất từ mô-đun.

+ +
const privates = new WeakMap();
+
+function Public() {
+  const me = {
+    // Private data goes here
+  };
+  privates.set(this, me);
+}
+
+Public.prototype.method = function () {
+  const me = privates.get(this);
+  // Do stuff with private data in `me`...
+};
+
+module.exports = Public;
+ +

Sets

+ +

Set object

+ +

Các {{jsxref("Set")}} object là bộ sưu tập các giá trị. Bạn có thể lặp các phần tử theo thứ tự được thêm vào. Một giá trị trong một Set chỉ có thể xuất hiện một lần; nó là duy nhất trong Set.

+ +

Đoạn code dưới đây là các thao tác cơ bản với Set. Xem thêm tại tài liệu tham khảo {{jsxref("Set")}} để có thêm nhiều ví dụ và toàn bộ các API.

+ +
let mySet = new Set();
+mySet.add(1);
+mySet.add('some text');
+mySet.add('foo');
+
+mySet.has(1); // true
+mySet.delete('foo');
+mySet.size; // 2
+
+for (let item of mySet) console.log(item);
+// 1
+// "some text"
+
+ +

Converting between Array and Set

+ +

Bạn có thể tạo một {{jsxref("Array")}} từ một Set bằng cách sử dụng {{jsxref("Array.from")}} hoặc spread operator. Ngoài ra, hàm khởi tạo Set cho phép chuyển đổi một Array theo một hướng khác.

+ +
+

Note: Hãy nhớ rằng các Set object lưu trữ các giá trị duy nhất—vì thế bất kỳ phần tử nào trùng lặp trong Array sẽ bị loại bỏ khi chuyển đổi!

+
+ +
Array.from(mySet);
+[...mySet2];
+
+mySet2 = new Set([1, 2, 3, 4]);
+
+ +

Array and Set compared

+ +

Thông thường, một tập hợp các phần tử được lưu trữ trong mảng trong JavaScript trong nhiều tính huống. Tuy nhiên, Set object có một vài điểm lợi thế sau:

+ + + +

WeakSet object

+ +

Các {{jsxref("WeakSet")}} object là bộ sưu tập các đối tượng. Một đối tượng trong WeakSet chỉ xuất hiện một lần. Nó là duy nhất trong bộ sưu tập WeakSet, và các đối tượng đó không thể đếm được.

+ +

Những điểm khác biệt chính so với {{jsxref("Set")}} object:

+ + + +

Các trường hợp sử dụng các  WeakSet object bị hạn chế. Chúng sẽ không rò rỉ bộ nhớ, vì thế nó có thể an toàn khi sử dụng các phần tử DOM làm khóa và đánh dấu chúng cho mục đích theo dõi.

+ +

Key and value equality of Map and Set

+ +

So sánh bằng nhau của các key của các Map object và value của các Set object dựa trên "same-value-zero algorithm":

+ + + +

{{PreviousNext("Web/JavaScript/Guide/Indexed_Collections", "Web/JavaScript/Guide/Working_with_Objects")}}

diff --git a/files/vi/web/javascript/guide/loops_and_iteration/index.html b/files/vi/web/javascript/guide/loops_and_iteration/index.html new file mode 100644 index 0000000000..5ad3581fba --- /dev/null +++ b/files/vi/web/javascript/guide/loops_and_iteration/index.html @@ -0,0 +1,348 @@ +--- +title: Loops and iteration +slug: Web/JavaScript/Guide/Loops_and_iteration +translation_of: Web/JavaScript/Guide/Loops_and_iteration +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}
+ +

Vòng lặp giúp thực hiện một hành động nhiều lần một cách nhanh chóng và đơn giản. Chương Hướng dẫn JavaScript này giới thiệu về các câu lệnh lặp trong JavaScript.

+ +

Hãy tưởng tượng vòng lặp giống như phiên bản vi tính của trò chơi mà bạn bảo một người đi X bước sang một hướng rồi đi Y bước sang một hướng khác; chẳng hạn, ý tưởng trò "Tiến 5 bước về phía Đông" có thể được biểu diễn dưới dạng vòng lặp như sau:

+ +
var step;
+for (let step = 0; step < 5; step++) {
+  // Chạy 5 lần, với giá trị chạy từ 0 đến 4.
+  console.log('Walking east one step');
+}
+
+ +

Có nhiều kiểu vòng lặp khác nhau, nhưng tất cả đều thực hiện cùng một việc: làm lại một hành động trong số lần nhất định (số lần đó có thể là 0). Mỗi vòng lặp có các cơ chế khác nhau, tương ứng với đó là sự khác nhau giữa cách xác định điểm bắt đầu và kết thúc của vòng lặp. Tuỳ theo trường hợp xác định mà lựa kiểu vòng lặp cho phù hợp..

+ +

Các lệnh lặp trong JavaScript là:

+ + + +

Lệnh for

+ +

Vòng lặp {{jsxref("statements/for","for")}} thực hiện liên tục cho tới khi điều kiện ban đầu trả về false. Vòng for trong JavaScript tương tự như vòng for trong Java hoặc C. Lệnh for có dạng như sau:

+ +
for ([biểu_thức_khởi_tạo]; [điều_kiện]; [biểu_thức_tăng_tiến])
+  lệnh
+
+ +

Vòng lặp for thực thi như sau:

+ +
    +
  1. Biểu thức khởi tạo (biểu_thức_khởi_tạo), nếu có, được thực thi. Biểu thức này thường khởi tạo một hoặc nhiều biến đếm cho vòng lặp, nhưng cú pháp của nó vẫn có thể tạo ra biểu thức có độ phức tạp. Biểu thức này còn có thể khai báo biến.
  2. +
  3. Biểu thức điều kiện (điều_kiện) được tính toán. Nếu giá trị của điều_kiện trả về true, các lệnh trong vòng lặp được thực thi. Nếu giá trị của điều_kiện trả về false, vòng lặp for bị dừng lại. Nếu biểu thức điều_kiện bị khuyết (bỏ qua không đặt), điều kiện sẽ được gán giả định là true.
  4. +
  5. lệnh sẽ được thực thi. Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các lệnh này.
  6. +
  7. Nếu không có lỗi nào xảy ra sau khi thực thi lệnh, biểu thức cập nhật (biểu_thức_tăng_tiến) được thực thi.
  8. +
  9. Quay lại bước 2.
  10. +
+ +

Ví dụ

+ +

Hàm dưới đây chứa lệnh for đếm số option được chọn trong danh sách cuộn (phần tử {{HTMLElement("select")}} cho phép chọn nhiều). Lệnh for khai báo biến i và khởi tạo cho nó là 0. Điều kiện kiểm tra i có nhỏ hơn số option mà phần tử <select> sở hữu hay không, nếu thoả mãn điều kiện, chương trình sẽ chạy vào lệnh if, và tăng i thêm một.

+ +
<form name="selectForm">
+  <p>
+    <label for="musicTypes">Choose some music types, then click the button below:</label>
+    <select id="musicTypes" name="musicTypes" multiple="multiple">
+      <option selected="selected">R&B</option>
+      <option>Jazz</option>
+      <option>Blues</option>
+      <option>New Age</option>
+      <option>Classical</option>
+      <option>Opera</option>
+    </select>
+  </p>
+  <p><input id="btn" type="button" value="How many are selected?" /></p>
+</form>
+
+<script>
+function howMany(selectObject) {
+  var numberSelected = 0;
+  for (var i = 0; i < selectObject.options.length; i++) {
+    if (selectObject.options[i].selected) {
+      numberSelected++;
+    }
+  }
+  return numberSelected;
+}
+
+var btn = document.getElementById('btn');
+btn.addEventListener('click', function() {
+  alert('Number of options selected: ' + howMany(document.selectForm.musicTypes));
+});
+</script>
+
+
+ +

Lệnh do...while

+ +

Lệnh {{jsxref("statements/do...while", "do...while")}} thực hiện liên tục cho tới khi điều kiện xác định trả về false. Lệnh do...while có dạng như sau:

+ +
do
+  lệnh
+while (điều_kiện);
+
+ +

lệnh luôn được thực thi một lần trước khi kiểm tra điều kiện (và rồi cứ thế cho tới khi điều kiện trả về false). Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các câu lệnh. Nếu điều_kiện trả về true, lệnh lại được thực thi một lần nữa. Sau mỗi lần thực thi, chương trình kiểm tra lại điều kiện. Khi điều kiện trả về false, chương trình dừng thực thi vòng lặp và truyền điều khiển xuống lệnh liền sau vòng do...while.

+ +

Ví dụ

+ +

Trong ví dụ sau, lặp do lặp ít nhất một lần và tái duyệt cho tới khi i không nhỏ hơn 5.

+ +
var i = 0;
+do {
+  i += 1;
+  console.log(i);
+} while (i < 5);
+ +

Lệnh while

+ +

Lệnh {{jsxref("statements/while","while")}} thực thi các lệnh bên trong nó ngay khi điều kiện xác định trả về true. Lệnh while có dạng như sau:

+ +
while (điều_kiện)
+  lệnh
+
+ +

Nếu điều kiện trả về false, chương trình sẽ ngừng thực thi lệnh bên trong vòng lặp và truyền điều khiển xuống lệnh liền sau vòng lặp.

+ +

Chương trình kiểm tra điều kiện trước khi thực thi lệnh bên trong. Nếu điều kiện trả về true, lệnh sẽ được thực thi và kiểm tra lại điều kiện. Nếu điều kiện trả về false, ngừng thực thi và truyền điều khiển xuống lệnh liền sau vòng lặp while.

+ +

Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các câu lệnh.

+ +

Ví dụ 1

+ +

Vòng while lặp lại cho tới khi n nhỏ hơn 3:

+ +
var n = 0;
+var x = 0;
+while (n < 3) {
+  n++;
+  x += n;
+}
+
+ +

Ứng với mỗi lần lặp, tăng n thêm một và cộng giá trị đó vào x. Bởi vậy, xn có giá trị như sau:

+ + + +

Sau khi hoàn thành lần lặp thứ ba, điều kiện n < 3 không còn trả về true nữa nên vòng lặp bị kết thúc.

+ +

Ví dụ 2

+ +

Hãy tránh lặp vô hạn. Hãy đảm bảo rằng điều kiện của vòng lặp sẽ dần trả về false; bằng không, vòng lặp sẽ không bao giờ kết thúc. Lệnh trong vòng lặp while dưới đây được thực thi mãi mãi vì điều kiện không bao giờ trả về false:

+ +
while (true) {
+  console.log('Hello, world!');
+}
+ +

Lệnh gán nhãn

+ +

{{jsxref("statements/label","label")}} tạo ra một lệnh đi kèm với một định danh, thông qua định danh giúp bạn tham chiếu tới nó từ những nơi khác trong chương trình của bạn. Chẳng hạn, bạn có thể dùng nhãn để định danh một vòng lặp, rồi dùng lệnh break hoặc continue để chương trình xác định có nên dừng hay tiếp tục thực thi vòng lặp hay không.

+ +

Cú pháp của lệnh gán nhãn có dạng như sau:

+ +
nhãn :
+   lệnh
+
+ +

Giá trị của nhãn có thể là định danh JavaScript bất kỳ nhưng không phải từ dành riêng (tên biến, tên hàm hoặc nhãn khác). lệnh được gán với nhãn có thể là bất kỳ lệnh nào.

+ +

Ví dụ

+ +

Trong ví dụ này, nhãn markLoop giúp định danh cho một vòng lặp while.

+ +
markLoop:
+while (theMark == true) {
+   doSomething();
+}
+ +

Lệnh break

+ +

Dùng lệnh {{jsxref("statements/break","break")}} để kết thúc vòng lặp, kết thúc switch, hoặc liên hợp với một lệnh được gán nhãn.

+ + + +

Cú pháp lệnh break có dạng như sau:

+ +
break;
+break [nhãn];
+
+ +

Kiểu cú pháp đầu tiên kết thúc vòng lặp trong cục bao bọc hoặc switch; kiểu thứ hai kết thúc lệnh gắn với nhãn.

+ +

Ví dụ 1

+ +

Trong ví dụ sau, lệnh lặp sẽ lặp qua từng phần tử của mảng (thông qua chỉ mục của từng phần tử) cho tới khi gặp phần tử có giá trị bằng theValue:

+ +
for (var i = 0; i < a.length; i++) {
+  if (a[i] == theValue) {
+    break;
+  }
+}
+ +

Ví dụ 2: Áp dụng break cho nhãn

+ +
var x = 0;
+var z = 0;
+labelCancelLoops: while (true) {
+  console.log('Outer loops: ' + x);
+  x += 1;
+  z = 1;
+  while (true) {
+    console.log('Inner loops: ' + z);
+    z += 1;
+    if (z === 10 && x === 10) {
+      break labelCancelLoops;
+    } else if (z === 10) {
+      break;
+    }
+  }
+}
+
+ +

Lệnh continue

+ +

Lệnh {{jsxref("statements/continue","continue")}} có thể dùng để tái duyệt các vòng while, do-while, for, hoặc label.

+ + + +

Cú pháp của lệnh continue có dạng như sau:

+ +
continue [label];
+
+ +

Ví dụ 1

+ +

Trong ví dụ sau, vòng while với lệnh continue thực thi khi giá trị của i bằng 3. Thế nên, n sẽ có giá trị là 1, 3, 7 và 12.

+ +
var i = 0;
+var n = 0;
+while (i < 5) {
+  i++;
+  if (i == 3) {
+    continue;
+  }
+  n += i;
+  console.log(n);
+}
+//1,3,7,12
+
+
+var i = 0;
+var n = 0;
+while (i < 5) {
+  i++;
+  if (i == 3) {
+     // continue;
+  }
+  n += i;
+  console.log(n);
+}
+// 1,3,6,10,15
+
+ +

Ví dụ 2

+ +

Lệnh nhãn checkiandj chứa nhãn checkj. Nếu chương trình chạy tới continue, chương trình sẽ ngừng thực hiện lần lặp hiện tại của checkj và bắt đầu thực hiện lần lặp kế tiếp. Mỗi khi chạy tới continue, checkj tái duyệt cho tới khi biểu thức điều kiện của nó trả về false. Khi false được trả về, phần còn lại của lệnh checkiandj được hoàn thành, và checkiandj tái duyệt cho tới khi điều kiện của nó trả về false. Khi false được trả về, chương trình tiếp tục thực thi lệnh liền sau checkiandj.

+ +

Nếu continue gắn với nhãn checkiandj, chương trình sẽ tiếp tục ở đầu lệnh checkiandj .

+ +
var i = 0;
+var j = 10;
+checkiandj:
+  while (i < 4) {
+    console.log(i);
+    i += 1;
+    checkj:
+      while (j > 4) {
+        console.log(j);
+        j -= 1;
+        if ((j % 2) == 0) {
+          continue checkj;
+        }
+        console.log(j + ' is odd.');
+      }
+      console.log('i = ' + i);
+      console.log('j = ' + j);
+  }
+ +

Lệnh for...in

+ +

Lệnh {{jsxref("statements/for...in","for...in")}} lặp một biến (variable) đã được xác định trước, qua mọi thuộc tính có thể đếm được (enumerable properties) của đối tượng (object). Với từng phần tử riêng biệt, JavaScript thực thi các câu lệnh mà bạn định sẵn. Lệnh for...in có dạng như sau:

+ +
for (variable in object)
+  statement
+
+ +

Ví dụ

+ +

Hàm sau nhận vào tham số là một object và tên của object đó. Sau đó nó lặp qua mọi thuộc tính của object, với mỗi lần lặp nó trả về một chuỗi ký tự liệt kê các tên thuộc tính và giá trị của chúng.

+ +
function dump_props(obj, obj_name) {
+  var result = '';
+  for (var i in obj) {
+    result += obj_name + '.' + i + ' = ' + obj[i] + '<br>';
+  }
+  result += '<hr>';
+  return result;
+}
+
+ +

Chẳng hạn với object car có thuộc tính là makemodel, result sẽ là:

+ +
car.make = Ford
+car.model = Mustang
+
+ +

Mảng

+ +

Mặc dù có thể dùng cách này để lặp qua từng phần tử của mảng {{jsxref("Array")}}, lệnh for...in sẽ trả về tên của thuộc tính mà người dùng tự định nghĩa kèm theo chỉ mục của mảng.

+ +

Bởi vậy, tốt hơn hết hãy sử dụng cách truyền thống là dùng vòng lặp {{jsxref("statements/for","for")}} với chỉ mục dạng số để lặp qua từng phần tử của mảng, bởi lệnh for...in còn lặp qua cả thuộc tính mà người dùng tự định nghĩa (chẳng hạn như khi thêm một thuộc tính vào mảng, sẽ có ví dụ ở phía dưới).

+ +

for...of statement

+ +

Lệnh {{jsxref("statements/for...of","for...of")}} tạo ra một vòng lặp, áp dụng với các đối tượng khả duyệt (iterable object) (bao gồm {{jsxref("Array")}}, {{jsxref("Map")}}, {{jsxref("Set")}}, {{jsxref("functions/arguments","arguments")}} object, v.v...), và gọi lên một hook lặp tùy chỉnh (custom iteration hook) với các câu lệnh sẽ được thực thi cho giá trị của từng thuộc tính.

+ +
for (variable of object)
+  statement
+
+ +

Ví dụ sau chỉ ra sự khác biệt giữa hai vòng lặp: for...of và {{jsxref("statements/for...in","for...in")}}. Trong khi for...in lặp qua tên thuộc tính, for...of lặp qua giá trị thuộc tính:

+ +
var arr = [3, 5, 7];
+arr.foo = 'hello';
+
+for (var i in arr) {
+   console.log(i); // logs "0", "1", "2", "foo"
+}
+
+for (var i of arr) {
+   console.log(i); // logs 3, 5, 7
+}
+
+ +

{{PreviousNext("Web/JavaScript/Guide/Control_flow_and_error_handling", "Web/JavaScript/Guide/Functions")}}

diff --git a/files/vi/web/javascript/guide/numbers_and_dates/index.html b/files/vi/web/javascript/guide/numbers_and_dates/index.html new file mode 100644 index 0000000000..a48afbbb89 --- /dev/null +++ b/files/vi/web/javascript/guide/numbers_and_dates/index.html @@ -0,0 +1,385 @@ +--- +title: Numbers and dates +slug: Web/JavaScript/Guide/Numbers_and_dates +translation_of: Web/JavaScript/Guide/Numbers_and_dates +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Expressions_and_Operators", "Web/JavaScript/Guide/Text_formatting")}}
+ +

Chương này giới thiệu những khái niệm, đối tượng và hàm được sử dụng để làm việc và tính toán với giá trị số và ngày tháng trong JavaScript. Nó bao gồm việc sử dụng những con số được viết dưới dạng những hệ cơ số khác nhau như là hệ thập phân, hệ nhị phân và hệ thập lục phân, cũng như việc sử dụng đối tượng toàn cục {{jsxref("Math")}} để thực hiện các phép toán số học.

+ +

Con Số (Numbers)

+ +

Trong JavaScript, tất cả các con số được cài đặt theo double-precision 64-bit binary format IEEE 754 (ví dụ: một số bắt đầu từ ±2−1022 and ±2+1023, hoặc là khoảng ±10−308 to ±10+308, với độ chính xác 53 bits sau dấu phẩy). Số nguyên lớn nhất có thể là ±253 − 1. Để có thể biểu diễn số thực dấu chấm động, ta có thể dùng ba ký hiệu +{{jsxref("Infinity")}}, -{{jsxref("Infinity")}}, and {{jsxref("NaN")}} (not-a-number). Xem thêm trong JavaScript data types and structures về những kiểu sơ cấp trong JavaScript.

+ +
+

Lưu Ý: Không có kiểu cụ thể cho số nguyên trong JavaScript, mặc dù kiểu {{jsxref("BigInt")}} cho phép biểu diễn số nguyên mà có giá trị rất lớn. Nhưng cần cẩn thận khi dùng BigInt, ví dụ, bạn có không thể kết hợp giá trị BigInt và {{jsxref("Number")}} trong cùng một biểu thức, và bạn cũng không thể sử dụng {{jsxref("Math")}} với giá trị BigInt.

+
+ +

Bạn có thể sử dụng bốn loại literal để tạo số: thập phân, nhị phân, bát phân và thập lục phân.

+ +

Số thập phân

+ +
1234567890
+42
+
+// Cẩn thận với số bắt đầu bằng số không:
+
+0888 // 888 trong hệ thập phân
+0777 // được xem như là số bát phân trong chế độ non-strict (và có giá trị là 511 trong hệ thập phân)
+
+ +

Lưu ý literal thập phân có thể bắt đầu bằng số zero (0) được theo sau là những số thập phân, nhưng nếu mọi chữ số sau số 0 nhỏ hơn 8, thì nó hiểu nhầm như là hệ bát phân.

+ +

Số nhị phân

+ +

Cú pháp của hệ nhị phân bắt đầu bằng chữ số 0 theo sau là "B" hoặc "b" (0b or 0B). Nếu các chữ số theo sau 0b không phải là 0 hoặc 1, thì lỗi  SyntaxError sẽ phát sinh như sau "Missing binary digits after 0b".

+ +
var FLT_SIGNBIT  = 0b10000000000000000000000000000000; // 2147483648
+var FLT_EXPONENT = 0b01111111100000000000000000000000; // 2139095040
+var FLT_MANTISSA = 0B00000000011111111111111111111111; // 8388607
+ +

Số bát phân

+ +

Số bát phân bắt đầu bằng số 0. Nếu chữ số theo sau số 0 không nắm trong khoảng từ 0 đến 7, thì số đó sẽ được hiểu là số thập phân.

+ +
var n = 0755; // 493
+var m = 0644; // 420
+
+ +

Trong chế độ strict trong ECMAScript 5 ngăn cấm cú pháp hệ bát phân. Cú pháp hệ bát phân không nằm trong đặc tả của ECMAScript 5, nhưng nó được hỗ trợ bởi tất cả các trình duyệt bằng cách đặt tiền tố 0:  0644 === 420 and"\045" === "%". Trong ECMAScript 2015 (ES6), số bát phân được hỗ trợ nếu chúng bắt đầu bằng 0o, ví dụ:

+ +
var a = 0o10; // ES2015: 8
+
+ +

Số thập lục phân

+ +

Cú pháp của số thập lục phân được bắt đầu bằng 0 và theo sau là "x" hoặc "X" (0x or 0X). Nếu các chữ số theo sau 0x nằm ngoài khoảng giá trị  (0123456789ABCDEF), Lỗi  SyntaxError sẽ phát sinh: "Identifier starts immediately after numeric literal".

+ +
0xFFFFFFFFFFFFFFFFF // 295147905179352830000
+0x123456789ABCDEF   // 81985529216486900
+0XA                 // 10
+
+ +

Số mũ

+ +
1E3   // 1000
+2e6   // 2000000
+0.1e2 // 10
+ +

Number object

+ +

{{jsxref("Number")}} object có những thuộc tính có giá trị là những hằng kiểu số, như giá trị lớn nhất, not-a-number, và vô cực. Bạn không thể thay đổi những giá trị của những thuộc tính này và bạn sử dụng nó như sau:

+ +
var biggestNum = Number.MAX_VALUE;
+var smallestNum = Number.MIN_VALUE;
+var infiniteNum = Number.POSITIVE_INFINITY;
+var negInfiniteNum = Number.NEGATIVE_INFINITY;
+var notANum = Number.NaN;
+
+ +

Bạn luôn phải tham chiếu đến những thuộc tính được định nghĩa sẳn của Number object như trên, và không thể truy xuất qua giá trị số mà bạn tạo ra.

+ +

Bảng sau tổng hợp những thuộc tính của Number object.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Thuộc tính của Number
Thuộc tínhMô tả
{{jsxref("Number.MAX_VALUE")}}Số lớn nhất có thể gán (±1.7976931348623157e+308)
{{jsxref("Number.MIN_VALUE")}}Số nhỏ nhất có thể gán (±5e-324)
{{jsxref("Number.NaN")}}Special "not a number" value
+ Giá trị đặc biệt để mô tả "không phải là số"
{{jsxref("Number.NEGATIVE_INFINITY")}}Giá trị số âm vô định; được về khi vượt quá giá trị có thể tính toán
{{jsxref("Number.POSITIVE_INFINITY")}}Giá trị số dương vô định; được về khi vượt quá giá trị có thể tính toán
{{jsxref("Number.EPSILON")}}Sự khác nhau giữa 1 và giá trị nhỏ nhất lớn hơn 1 mà có thể được biểu diễn là một số
+ {{jsxref("Number")}} (2.220446049250313e-16)
{{jsxref("Number.MIN_SAFE_INTEGER")}}Số nguyên được an toàn nhỏ nhất trong JavaScript (−253 + 1, or −9007199254740991)
{{jsxref("Number.MAX_SAFE_INTEGER")}}Số nguyên dương an toàn lớn nhất trong JavaScript (+253 − 1, or +9007199254740991)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Phương thức của Number
Phương thứcMô tả
{{jsxref("Number.parseFloat()")}}Phân tích giá trị chuỗi và trả vể một giá trị số thực.
+ Giống như hàm toàn cục {{jsxref("parseFloat", "parseFloat()")}}.
{{jsxref("Number.parseInt()")}}Phân tích giá trị kiểu chuỗi và trả về giá trị số nguyên dựa trên cơ số chỉ định.
+ Cũng giống như hàm toàn cục {{jsxref("parseInt", "parseInt()")}} function.
{{jsxref("Number.isFinite()")}}Xác định giá trị được truyền vào có phải là một số xác đinh.
{{jsxref("Number.isInteger()")}}Xác định giá trị truyền vào có phải là một số nguyên.
{{jsxref("Number.isNaN()")}}Xác định giá trị truyền vào là {{jsxref("Global_Objects/NaN", "NaN")}}. Đây là phiên bản cải tiến của {{jsxref("Global_Objects/isNaN", "isNaN()")}}.
{{jsxref("Number.isSafeInteger()")}}Xác định giá trị truyền vào là một số nguyên an toàn.
+ +

Number prototype cung cấp những phương thức để lấy thông tin từ những đối tượng Number dưới nhiều định dạng. Bảng sau đây tổng hợp những phương thức của Number.prototype.

+ + + + + + + + + + + + + + + + + + + + + + + +
Những phương thức của Number.prototype
Phương thứcMô tả
{{jsxref("Number.toExponential", "toExponential()")}}Trả về một chuỗi biểu diễn một số theo dạng lũy thừa.
{{jsxref("Number.toFixed", "toFixed()")}}Trả về một chuỗi biểu diễn một số theo dạng số thập phân, với phần thập phân cố định.
{{jsxref("Number.toPrecision", "toPrecision()")}}Trả về một chuỗi biểu diễn một số thập phân với độ chính xác được chỉ định.
+ +

Math object

+ +

{{jsxref("Math")}} object cung cấp thuộc tính và những phương thức để lấy giá trị của các hằng số và những hàm toán học. Ví dụ, thuộc tính PI của Math có giá trị pi (3.141...), mà bạn có thể sử dụng trong ứng dụng như

+ +
Math.PI
+
+ +

Tương tự, những hàm chuẩn của toán học là phương thức của Math. Bao gồm lượng giác, logarit, hàm mũ, và những hàm khác nữa. Ví dụ, nếu bạn muốn sử dụng hàm lượng giác sine, bạn có thể viết như sau

+ +
Math.sin(1.56)
+
+ +

Lưu ý rằng phương thức lượng giác của Math nhận đổi số kiểu radians.

+ +

Bảng sau tóm tắt những phương thức của đối tượng Math.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Phương thức của Math
Phương thứcMô tả
{{jsxref("Math.abs", "abs()")}}Giá trị tuyệt đối
{{jsxref("Math.sin", "sin()")}}, {{jsxref("Math.cos", "cos()")}}, {{jsxref("Math.tan", "tan()")}}Những hàm lượng giác chuẩn, với đối số đơn vị radians.
{{jsxref("Math.asin", "asin()")}}, {{jsxref("Math.acos", "acos()")}}, {{jsxref("Math.atan", "atan()")}}, {{jsxref("Math.atan2", "atan2()")}}Những hàm lượng giác ngược; trả về giá trị theo đơn vị radians.
{{jsxref("Math.sinh", "sinh()")}}, {{jsxref("Math.cosh", "cosh()")}}, {{jsxref("Math.tanh", "tanh()")}}Hàm Hyperbolic; đối số là giá trị theo đơn vị hyperbolic.
+  
{{jsxref("Math.asinh", "asinh()")}}, {{jsxref("Math.acosh", "acosh()")}}, {{jsxref("Math.atanh", "atanh()")}}Nghịch đảo của hàm hyperbolic; đối số là giá trị theo đơn vị hyperbolic.
+

{{jsxref("Math.pow", "pow()")}}, {{jsxref("Math.exp", "exp()")}}, {{jsxref("Math.expm1", "expm1()")}}, {{jsxref("Math.log10", "log10()")}}, {{jsxref("Math.log1p", "log1p()")}}, {{jsxref("Math.log2", "log2()")}}

+
Hàm mũ và hàm logarit
{{jsxref("Math.floor", "floor()")}}, {{jsxref("Math.ceil", "ceil()")}}Trả về số nguyên lớn nhất và nhỏ nhất mà nhỏ hơn hoặc lớn hơn hoặc bằng đối số truyền vào.
{{jsxref("Math.min", "min()")}}, {{jsxref("Math.max", "max()")}}Trả về giá trị lớn nhất hoặc nhỏ nhất trong dãy các giá trị truyền vào.
{{jsxref("Math.random", "random()")}}Trả về số ngẫu nhiên trong khoảng 0 và 1.
{{jsxref("Math.round", "round()")}}, {{jsxref("Math.fround", "fround()")}}, {{jsxref("Math.trunc", "trunc()")}},Những hàm làm tròn hoặc cắt bớt.
{{jsxref("Math.sqrt", "sqrt()")}}, {{jsxref("Math.cbrt", "cbrt()")}}, {{jsxref("Math.hypot", "hypot()")}} +

Căn bậc hai, cube root, căn bậc hai của tổng đối số bình phương.

+
{{jsxref("Math.sign", "sign()")}}Xác dịnh số âm hay số dương hay số không
{{jsxref("Math.clz32", "clz32()")}},
+ {{jsxref("Math.imul", "imul()")}}
Số bit 0 ở đầu trong số nhị phân 32 bit. Kết quả của phép nhân 32 bit.
+ +

Không giống như những object khác, bạn không bao giờ tạo Math riêng. Bạn luôn dùng đối tượng Math có sẳn

+ +

Date object

+ +

JavaScript không có kiểu dữ liệu ngày tháng. Tuy nhiên, bạn có thể sử dụng {{jsxref("Date")}} object và phương thức của nó để làm việc với ngày giờ trong ứng dụng của mình. Date object cung cấp nhiều phương thức trực tiếp để thao tác trên trên giá trị ngày giờ. Nó không có bất kỳ thuộc tính nào.

+ +

JavaScript xử lý ngày giờ tương tự Java. Hai ngôn ngữ có nhiều tên phương thức giống nhau, và cả hai ngôn ngữ đều lưu trữ ngày giờ như là một số theo mili giây tính từ ngày 1 tháng 1 năm 1970, 00:00:00, với một Unix Timestamp là một số theo giây tính từ ngày 1 tháng 1, 1970, 00:00:00.

+ +

Dãy giá trị của Date object là -100,000,000 ngày đến 100,000,000 ngày so với 01 tháng 1, 1970 UTC.

+ +

Để tạo một Date object:

+ +
var dateObjectName = new Date([parameters]);
+
+
+ +

dateObjectName là biến lưu trữ Date object được  tạo;

+ +

Nếu chúng ta bỏ qua từ khóa new, giá trị nhận được là một chuỗi biểu diễn giá trị kiểu ngày giờ.

+ +

parameters trong cú pháp trên có thể là một trong những giá trị sau:

+ + + +

Những phương thức của Date object

+ +

Những phương thức của Date object để xử lý thuộc một trong những loại sau:

+ + + +

Với phương thức "get" và "set" bạn có thể nhận và gán giây, phút, giờ và ngày của tháng, ngày của tuần, tháng, và năm. Phương thức getDay trả về ngày của tuần, tháng, nhưng không có phương thức setDay tương ứng, bởi vì ngày của tuần được xác định tự động. Những phương thức này sử dụng số nguyên để biểu diễn những giá trị sau:

+ + + +

Ví dụ, giả sử bạn định nghĩa giá trị ngày như sau:

+ +
var Xmas95 = new Date('December 25, 1995');
+
+ +

Phương thức Xmas95.getMonth() trả về 11, và Xmas95.getFullYear() trả về 1995.

+ +

Phương thức getTimesetTime dùng để so sánh hai giá trị ngày. Phương thức getTime trả về số milli giây tính từ 0 giờ 0 phút 0 giây ngày 1 tháng 1 năm 1970.

+ +

Ví dụ đoạn mã sau hiển thị số ngày còn lại trong năm hiện tại:

+ +
var today = new Date();
+var endYear = new Date(1995, 11, 31, 23, 59, 59, 999); // Set day and month
+endYear.setFullYear(today.getFullYear()); // Set year to this year
+var msPerDay = 24 * 60 * 60 * 1000; // Number of milliseconds per day
+var daysLeft = (endYear.getTime() - today.getTime()) / msPerDay;
+var daysLeft = Math.round(daysLeft); //returns days left in the year
+
+ +

Đầu tiên một Date object được tạo ra với tên là today chứ giá trị là ngày hiện tại. Sau đó, một Date object nữa được tạo ra với tên là endYear và được gán giá trị của ngày cuối cùng của năm 1995. Nhưng sau đó endYear được gán lại với năm hiện tại cho đồng nhất về năm. Rồi ta tính toán độ chênh lệch theo ngày giữa endYeartoday giá trị phải được làm tròn.

+ +

Phương thức parse dùng để chuyển đổi chuỗi kiểu  Date thành Date object. Ví dụ, Đoạn mã sau sử dụng parsesetTime để gán giá trị ngày cho biến IPOdate:

+ +
var IPOdate = new Date();
+IPOdate.setTime(Date.parse('Aug 9, 1995'));
+
+ +

Ví dụ

+ +

Trong ví dụ sau, hàm JSClock() trả về thời gian trong định dạng của đồng hồ số.

+ +
function JSClock() {
+  var time = new Date();
+  var hour = time.getHours();
+  var minute = time.getMinutes();
+  var second = time.getSeconds();
+  var temp = '' + ((hour > 12) ? hour - 12 : hour);
+  if (hour == 0)
+    temp = '12';
+  temp += ((minute < 10) ? ':0' : ':') + minute;
+  temp += ((second < 10) ? ':0' : ':') + second;
+  temp += (hour >= 12) ? ' P.M.' : ' A.M.';
+  return temp;
+}
+
+ +

Hàm JSClock đầu tiên tạo ra Date object với tên time; vì không có đối số được cung cấp, time được tạo ra có giá trị là ngày giờ hiện tại. Sau đó các hàm getHours, getMinutes, và getSeconds để gán giá trị giờ, phút, giây hiện tai cho các biến hour, minute, và second.

+ +

Bốn câu lệnh kế tiếp tạo giá trị chuỗi từ thời gian. Câu lệnh đầu tiên tạo một biến temp, dùng biểu thức điều kiện để gán 1 giá trị cho temp; nếu hour lớn hơn 12, thì gán (hour - 12), ngược lại thì gán giá trị của hour.  Nếu giá trị của hour là 0, thì giá trị 12 cho temp.

+ +

Câu lệnh tiếp theo chèn gía trị minute cho temp. Nếu giá trị của minute kém hơn 10, ta thêm ':0' vào trước giá trị chuỗi; ngược lại ta chỉ thêm ':'. Sau đó chèn giá trị giây vào temp với cùng cách trên.

+ +

Cuối cùng, chèn "P.M" vào temp nếu hour lớn hơn 12, ngược lại ta chèn "A.M.".

+ +

{{PreviousNext("Web/JavaScript/Guide/Expressions_and_Operators", "Web/JavaScript/Guide/Text_formatting")}}

diff --git a/files/vi/web/javascript/guide/regular_expressions/index.html b/files/vi/web/javascript/guide/regular_expressions/index.html new file mode 100644 index 0000000000..acddfd5184 --- /dev/null +++ b/files/vi/web/javascript/guide/regular_expressions/index.html @@ -0,0 +1,666 @@ +--- +title: Biểu thức chính quy +slug: Web/JavaScript/Guide/Regular_Expressions +tags: + - Guide + - Intermediate + - JavaScript + - Regular Expression +translation_of: Web/JavaScript/Guide/Regular_Expressions +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Text_formatting", "Web/JavaScript/Guide/Indexed_collections")}}
+ +

Biểu thức chính quy (regular expressions ) là các mẫu dùng để tìm kiếm các bộ kí tự được kết hợp với nhau trong các chuỗi kí tự. Trong JavaScript thì biểu thức chính quy cũng đồng thời là các đối tượng, tức là khi bạn tạo ra một biểu thức chính quy là bạn có một đối tượng tương ứng. Các mẫu này được sử dụng khá nhiều trong JavaScript như phương thức exec và test của RegExp, hay phương thức match, replace, search, và split của String. Trong chương này, ta cùng tìm hiểu chi tiết hơn về biểu thức chính quy trong JavaScript.

+ +

Tạo một biểu thức chính quy

+ +

Bạn có thể tạo ra một biểu thức chính quy bằng 1 trong 2 cách sau:

+ + + +

Cách viết một mẫu biểu thức chính quy

+ +

Một mẫu biểu thức chính quy là một tập các kí tự thường, như /abc/, hay một tập kết hợp cả kí tự thường và kí tự đặc biệt như /ab*c/ hoặc /Chapter (\d+)\.\d*/. Trong ví dụ cuối, ta thấy rằng nó chứa cả các dấu ngoặc đơn( () )được sử dụng như các thiết bị nhớ, tức là các mẫu trong phần () này sau khi được tìm kiếm có thể được nhớ lại để sử dụng cho các lần sau. Bạn có thể xem chi tiết hơn tại:  Sử dụng dấu ngoặc đơn tìm xâu con.

+ +

Sử dụng mẫu đơn giản

+ +

Các mẫu đơn giản là các mẫu có thể được xây dựng từ các kí tự có thể thể tìm kiếm một cách trực tiếp. Ví dụ, mẫu /abc/ sẽ tìm các các đoạn 'abc' theo đúng thứ tự đó trong các chuỗi. Mẫu này sẽ khớp được với  "Hi, do you know your abc's?" và "The latest airplane designs evolved from slabcraft.", vì cả 2 chuỗi này đều chứa đoạn 'abc'. Còn với  chuỗi 'Grab crab', nó sẽ không khớp vì chuỗi này không chứa 'abc' theo đúng thứ tự, mà chỉ chứa 'ab c'.

+ +

Sử dụng các kí tự đặc biệt

+ +

Các mẫu có thể chứa các kí tự đặc biệt cho các mục đích tìm kiếm nâng cao mà tìm kiếm trực tiếp sẽ khó khăn như tìm một đoạn chứa một hoặc nhiều hơn một kí tự b, hay tìm một hoặc nhiều kí tự dấu cách (while space). Ví dụ, mẫu /ab*c/ có thể tìm các đoạn có chứa: một kí tự 'a', theo sau là không có hoặc có một hoặc có nhiều kí tự 'b', cuối cùng là một kí tự 'c' như chuỗi "cbbabbbbcdebc," sẽ được khớp với xâu con 'abbbbc'.

+ +

Bảng dưới đây mô tả đầy đủ các kí tự đặc biệt có thể dùng với biểu thức chính quy.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bảng 4.1 Các kí tự đặc biệt trong biểu thức chính quy.
Kí tự (kí hiệu, cờ)Ý nghĩa
\ +

Tìm với luật dưới đây:
+
+ Một dấu gạch chéo ngược sẽ biến một kí tự thường liền kế phía sau thành một kí tự đặc biệt, tức là nó không được sử dụng để tìm kiếm thông thường nữa. Ví dụ,  trường hợp kí tự 'b' không có dấu gạch chéo ngược này sẽ được khớp với các kí tự 'b' in thường, nhưng khi nó có thêm dấu gạch chéo ngược, '\b' thì nó sẽ không khớp với bất kì kí tự nào nữa, lúc này nó trở thành kí tự đặc biệt. Xem thêm phần word boundary character để biết thêm chi tiết.
+
+ Tuy nhiên nếu đứng trước một kí tự đặc biệt thì nó sẽ biến kí tự này thành một kí tự thường, tức là bạn có thể tìm kiếm kí tự đặc biệt này trong xâu chuỗi của bạn như các kí tự thường khác. Ví dụ, mẫu /a*/ có '*' là kí tự đặc biệt và mẫu này sẽ bị phụ thuộc vào kí tự này, nên được hiểu là sẽ tìm khớp  với 0 hoặc nhiều kí tự a. Nhưng, với mẫu /a\*/ thì kí tự '*' lúc này được hiểu là kí tự thường nên mẫu này sẽ tìm kiếm xâu con là 'a*'.

+ +

Đừng quên \ cũng là một kí tự đặc biệt, khi cần so khớp chính nó ta cũng phải đánh dấu nó là kí tự đặc biệt bằng cách đặt \ ở trước (\\).

+
^ +

Khớp các kí tự đứng đầu một chuỗi. Nếu có nhiều cờ này thì nó còn khớp được cả các kí tự đứng đầu của mỗi dòng (sau kí tự xuống dòng).
+
+ Ví dụ, /^A/ sẽ không khớp được với 'A' trong "an A" vì 'A' lúc này không đứng đầu chuỗi, nhưng nó sẽ khớp "An E" vì lúc này 'A' đã đứng đầu chuỗi.

+ +

Ý nghĩa của '^' sẽ thay đổi khi nó xuất hiện như một kí tự đầu tiên trong một lớp kí tự, xem phần complemented character sets để biết thêm chi tiết.

+
$ +

So khớp ở cuối chuỗi. Nếu gắn cờ multiline (đa dòng), nó sẽ khớp ngay trước kí tự xuống dòng.

+ +

Ví dụ, /t$/ không khớp với 't' trong chuỗi "eater" nhưng lại khớp trong chuỗi "eat".

+
* +

Cho phép kí tự trước nó lặp lại 0 lần hoặc nhiều lần. Tương đương với cách viết {0,}.

+ +

Ví dụ, /bo*/ khớp với 'boooo' trong chuỗi "A ghost booooed" nhưng không khớp trong chuỗi "A birth warbled".

+
+ +

Cho phép kí tự trước nó lặp lại 1 lần hoặc nhiều lần. Tương đương với cách viết {1,}.

+ +

Ví dụ, /a+/ khớp với 'a' trong chuỗi "candy" và khớp với tất cả kí tự a liền nhau trong chuỗi "caaaaaaandy".

+
? +

Cho phép kí tự trước nó lặp lại 0 lần hoặc 1 lần duy nhất. Tương đương với cách viết {0,1}.

+ +

Ví dụ, /e?le?/ khớp với 'el' trong chuỗi "angel" và 'le' trong chuỗi "angle" hay 'l' trong "oslo".

+ +

Nếu sử dụng kí tự này ngay sau bất kì kí tự định lượng nào trong số *,+,? hay {}, đều làm bộ định lượng "chán ăn" (dừng so khớp sau ngay khi tìm được kí tự phù hợp), trái ngược với đức tính "tham lam" vốn sẵn của chúng (khớp tất cả kí tự chúng tìm thấy). Ví dụ, áp dụng biểu mẫu /\d+/ cho "123abc" ta được "123". Nhưng áp /\d+?/ cho chính chuỗi trên ta chỉ nhận được kết quả là "1".

+ +

Bạn có thể đọc thêm trong mục x(?=y) và x(?!y) của bảng này.

+
. +

Dấu . khớp với bất kì kí tự đơn nào ngoại trừ kí tự xuống dòng.

+ +

Ví dụ, /.n/ khớp với 'an' và 'on' trong chuỗi "no, an apple is on the tree", nhưng không khớp với 'no'.

+
(x) +

Khớp 'x' và nhớ kết quả so khớp này, như ví dụ ở dưới. Các dấu ngoặc tròn được gọi là các dấu ngoặc có nhớ.

+ +

Biểu mẫu /(foo) (bar) \1 \2/ khớp với 'foo' và 'bar' trong chuỗi "foo bar foo bar". \1\2 trong mẫu khớp với 2 từ cuối.

+ +

Chú ý rằng \1, \2, \n được sử dụng để so khớp các phần trong regex, nó đại diện cho nhóm so khớp đằng trước. Ví dụ: /(foo) (bar) \1 \2/ tương đương với biểu thức /(foo) (bar) foo bar/. 

+ +

Cú pháp $1, $2, $n còn được sử dụng trong việc thay thế các phần của một regex. Ví dụ: 'bar foo'.replace(/(...) (...)/, '$2 $1') sẽ đảo vị trí 2 từ 'bar' và 'foo' cho nhau.

+
(?:x) +

Khớp 'x' nhưng không nhớ kết quả so khớp. Những dấu ngoặc tròn được gọi là những dấu ngoặc không nhớ, nó cho phép bạn định nghĩa những biểu thức con cho những toán tử so khớp. Xem xét biểu thức đơn giản /(?:foo){1,2}/. Nếu biểu thức này được viết là /foo{1,2}/{1,2} sẽ chỉ áp dụng cho kí tự 'o' ở cuối chuỗi 'foo'. Với những dấu ngoặc không nhớ, {1,2} sẽ áp dụng cho cả cụm 'foo'.

+
x(?=y) +

Chỉ khớp 'x' nếu 'x' theo sau bởi 'y'.

+ +

Ví dụ, /Jack(?=Sprat)/ chỉ khớp với 'Jack' nếu đằng sau nó là 'Sprat'. /Jack(?=Sprat|Frost)/ chỉ khớp 'Jack' nếu theo sau nó là 'Sprat' hoặc 'Frost'. Tuy nhiên, cả 'Sprat' và 'Frost' đều không phải là một phần của kết quả so khớp trả về.

+
x(?!y) +

Chỉ khớp 'x' nếu 'x' không được theo sau bởi 'y'.

+ +

Ví dụ: /\d+(?!\.)/ chỉ khớp với số không có dấu . đằng sau. Biểu thức /\d+(?!\.)/.exec("3.141")​ cho kết quả là '141' mà không phải '3.141'.

+
x|y +

Khớp 'x' hoặc 'y'

+ +

Ví dụ, /green|red/ khớp với 'green' trong chuỗi "green apple" và 'red' trong chuỗi "red apple".

+
{n} +

Kí tự đứng trước phải xuất hiện n lần. n phải là một số nguyên dương.

+ +

Ví dụ, /a{2}/ không khớp với 'a' trong "candy", nhưng nó khớp với tất cả kí tự 'a' trong "caandy", và khớp với 2 kí tự 'a' đầu tiên trong "caaandy".

+
{n,m} +

Kí tự đứng trước phải xuất hiện từ n đến m lần. n và m là số nguyên dương và n <= m. Nếu m bị bỏ qua, nó tương đương như ∞.

+ +

Ví dụ, /a{1,3}/ không khớp bất kì kí tự nào trong "cndy", kí tự 'a' trong "candy", 2 kí tự 'a' đầu tiên trong "caandy", và 3 kí tự 'a' đầu tiên trong "caaaaaaandy". Lưu ý là "caaaaaaandy" chỉ khớp với 3 kí tự 'a' đầu tiên mặc dù chuỗi đó chứa 7 kí tự 'a'.

+
[xyz] +

Lớp kí tự. Loại mẫu này dùng để so khớp với một kí tự bất kì trong dấu ngoặc vuông, bao gồm cả escape sequences. Trong lớp kí tự, dấu chấm (.) và dấu hoa thị (*) không còn là kí tự đặc biệt nên ta không cần kí tự thoát đứng trước nó. Bạn có thể chỉ định một khoảng kí tự bằng cách sử dụng một kí tự gạch nối (-) như trong ví dụ dưới đây:

+ +

Mẫu [a-d] so khớp tương tự như mẫu [abcd], khớp với 'b' trong "brisket" và 'c' trong "city". Mẫu /[a-z.]+/ và /[\w.]+/ khớp với toàn chuỗi "test.i.ng".

+
[^xyz] +

Lớp kí tự phủ định. Khi kí tự ^ đứng đầu tiên trong dấu ngoặc vuông, nó phủ định mẫu này.

+ +

Ví dụ, [^abc] tương tự như [^a-c], khớp với 'r' trong "brisket" và 'h' trong "chop" là kí tự đầu tiên không thuộc khoảng a đến c.

+
[\b] +

Khớp với kí tự dịch lùi - backspace (U+0008). Bạn phải đặt trong dấu ngoặc vuông nếu muốn so khớp một kí tự dịch lùi. (Đừng nhầm lẫn với mẫu \b).

+
\b +

Khớp với kí tự biên. Kí tự biên là một kí tự giả, nó khớp với vị trí mà một kí tự không được theo sau hoặc đứng trước bởi một kí tự khác. Tương đương với mẫu (^\w|\w$|\W\w|\w\W). Lưu ý rằng một kí tự biên được khớp sẽ không bao gồm trong kết quả so khớp. Nói cách khác, độ dài của một kí tự biên là 0. (Đừng nhầm lẫn với mẫu [\b])

+ +

Ví dụ:
+ /\bm/ khớp với 'm' trong chuỗi "moon";
+ /oo\b/ không khớp  'oo' trong chuỗi "moon", bởi vì 'oo' được theo sau bởi kí tự 'n';
+  /oon\b/ khớp với 'oon' trong chuỗi "moon", bởi vì 'oon' ở cuối chuỗi nên nó không được theo sau bởi một kí tự; 
+ /\w\b\w/ sẽ không khớp với bất kì thứ gì, bởi vì một kí tự không thể theo sau một kí tự biên và một kí tự thường.

+ +
+

Chú ý: Engine biên dịch biểu thức chính quy trong Javascript định nghĩa một lớp kí tự là những kí tự thường. Bất kỳ kí tự nào không thuộc lớp kí tự bị xem như một kí tự ngắt. Lớp kí tự này khá hạn chế: nó bao gồm bộ kí tự La-tinh cả hoa và thường, số thập phân và kí tự gạch dưới. Kí tự có dấu, như "é" hay "ü", không may, bị đối xử như một kí tự ngắt.

+
+
\B +

Khớp với kí tự không phải kí tự biên. Mẫu này khớp tại vị trí mà kí tự trước và kí tự sau nó cùng kiểu: hoặc cả hai là kí tự hoặc cả hai không phải là kí tự. Bắt đầu và kết thúc chuỗi không được xem là những kí tự.

+ +

Ví dụ, /\B../ khớp với 'oo' trong "noonday", và /y\B./ khớp với 'ye' trong "possibly yesterday."

+
\cX +

X là một kí tự trong khoảng A tới Z. Mẫu này khớp với một kí tự điều khiển trong một chuỗi.

+ +

Ví dụ: /\cM/ khớp với control-M (U+000D) trong chuỗi.

+
\d +

Khớp với một kí tự số. Tương đương với mẫu [0-9].

+ +

Ví dụ: /\d/ hoặc /[0-9]/ khớp với '2' trong chuỗi "B2 is the suite number."

+
\D +

Khớp với một kí tự không phải là kí tự số. Tương đương với mẫu [^0-9].

+ +

Ví dụ; /\D/ hoặc /[^0-9]/ khớp với 'B' trong "B2 is the suite number."

+
\fKhớp với kí tự phân trang - form feed (U+000C).
\nKhớp với kí tự xuống dòng - line feed (U+000A).
\rKhớp với kí tự quay đầu dòng -  carriage return (U+000D).
\s +

Khớp với một kí tự khoảng trắng, bao gồm trống - space, tab, phân trang - form feed, phân dòng - line feed. Tương đương với [ \f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​\u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​​\u202f\u205f​\u3000].

+ +

Ví dụ: /\s\w*/ khớp với ' bar' trong "foo bar."

+
\S +

Khớp với một kí tự không phải khoảng trắng. Tương đương với [^ \f\n\r\t\v​\u00a0\u1680​\u180e\u2000​\u2001\u2002​\u2003\u2004​\u2005\u2006​\u2007\u2008​\u2009\u200a​\u2028\u2029​\u202f\u205f​\u3000].

+ +

Ví dụ: /\S\w*/ khớp với 'foo' trong chuỗi "foo bar."

+
\tKhớp với kí tự tab (U+0009).
\vKhớp với kí tự vertical tab (U+000B).
\w +

Khớp với tất cả kí tự là chữ, số và gạch dưới. Tương đương với mẫu [A-Za-z0-9_].

+ +

ví dụ, /\w/ khớp với 'a' trong "apple," '5' trong "$5.28," và '3' trong "3D."

+
\W +

Khớp với tất cả kí tự không phải là chữ. Tương đương với mẫu [^A-Za-z0-9_].

+ +

ví dụ, /\W/ hoặc /[^A-Za-z0-9_]/ khớp với '%' trong "50%."

+
\n +

Trong đó, n là một số nguyên dương, một tham chiếu ngược tới chuỗi khớp thứ n trong biểu thức (đếm từ trái sang, bắt đầu bằng 1).

+ +

ví dụ, /apple(,)\sorange\1/ hay /apple(,)\sorange,/ khớp với 'apple, orange,' trong chuỗi "apple, orange, cherry, peach."

+
\0Khớp với kí tự NULL (U+0000). Lưu ý: không được thêm bất kì một kí tự số nào sau 0, vì \0<các-kí-tự-số> là một biểu diễn hệ bát phân escape sequence.
\xhhKhớp với kí tự với mã code là hh (2 số trong hệ thập lục phân)
\uhhhhKhớp với kí tự có mã hhhh (4 số trong hệ thập lục phân).
+ +

Mã hóa escapse chuỗi người dùng nhập vào bằng một hàm thay thế đơn giản sử dụng biểu thức chính quy:

+ +
function escapeRegExp(string){
+  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+ +

Sử dụng ngoặc tròn

+ +

Ngoặc tròn bao quanh bất kỳ phần nào của biểu thức chính quy sẽ khiến phần kết quả so khớp được nhớ. Mỗi lần nhớ, chuỗi con có thể được gọi lại để sử dụng, mô tả trong Using Parenthesized Substring Matches.

+ +

Ví dụ, mẫu /Chapter (\d+)\.\d*/ khớp đúng với 'Chapter ' theo sau bởi một hoặc nhiều kí tự số, sau nữa là một dấu chấm thập phân, cuối cùng có thể là 0 hoặc nhiều kí tự số. Bên cạnh đó, dấu ngoặc tròn được sử dụng để nhớ một hoặc nhiều kí tự số đầu tiên được khớp.

+ +

Mẫu này được tìm thấy trong chuỗi "Open Chapter 4.3, paragraph 6", nhớ '4' nhưng không được tìm thấy trong chuỗi "Chapter 3 and 4", bởi vì chuỗi đó không có dấu chấm sau kí tự số '3'.

+ +

Để so khớp một chuỗi con không nhớ, đặt ?: ở vị trí đầu tiên trong ngoặc. Ví dụ, (?:\d+) khớp với một hoặc nhiều kí tự số nhưng không nhớ kết quả so khớp.

+ +

Làm việc với biểu thức chính quy

+ +

Biểu thức chính quy được sử dụng với phương thức test và exec của lớp RegExp hoặc phương thức match, replace, search và split của chuỗi. Những phương thức này được giải thích chi tiết trong JavaScript Reference.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bảng 4.2 Những phương thức được sử dụng trong biểu thức chính quy
Phương thứcMô tả
execMột phương thức của RegExp dùng để tìm kiếm chuỗi phù hợp với mẫu so khớp. Nó trả về một mảng chứa kết quả tìm kiếm.
testMột phương thức của RegExp dùng để kiểm tra mẫu có khớp với chuỗi hay không. Nó trả về giá trị true hoặc false.
matchMột phương thức của chuỗi dùng để tìm kiếm chuỗi phù hợp với mẫu so khớp. Nó trả về một mảng chứa kết quả tìm kiếm hoặc null nếu không tìm thấy.
searchMột phương thức của chuỗi dùng để tìm kiếm chuỗi phù hợp với mẫu so khớp và trả về vị trí của chuỗi đó hoặc -1 nếu không tìm thấy.
replaceMột phương thức của chuỗi dùng để tìm kiếm một chuỗi theo mẫu so khớp và thay thế chuỗi con được khớp với một chuỗi thay thế.
splitMột phương thức của chuỗi dùng một biểu mẫu chính quy hoặc một chuỗi bất biến để ngắt chuỗi đó thành một mảng các chuỗi con.
+ +

Khi bạn muốn biết một mẫu có được tìm thấy trong chuỗi hay không, sử dụng phương thức test hoặc search; để lấy nhiều thông tin hơn (nhưng chậm hơn) sử dụng phương thức exec hoặc match.

+ +

Như ví dụ dưới đây, phương thức exec được dùng để tìm chuỗi phù hợp theo mẫu so khớp.

+ +
var myRe = /d(b+)d/g;
+var myArray = myRe.exec("cdbbdbsbz");
+
+ +

Nếu bạn không cần truy cập những thuộc tính khác của biểu thức chính quy, sử dụng cách sau:

+ +
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
+
+ +

Nếu bạn muốn khởi tạo một biểu thức chính quy từ một chuỗi:

+ +
var myRe = new RegExp("d(b+)d", "g");
+var myArray = myRe.exec("cdbbdbsbz");
+
+ +

Với những mã này, so khớp thành công và trả về một mảng kết quả với những thuộc tính được liệt kê trong bảng dưới đây.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bảng 4.3 Kết quả so khớp
Đối tượngThuộc tính hoặc chỉ sốMô tảTrong vd này
myArray Chuỗi kết quả so khớp và toàn bộ chuỗi con được nhớ.["dbbd", "bb"]
indexVị trí của chuỗi tìm thấy, đếm từ 01
inputChuỗi gốc được nhập vào"cdbbdbsbz"
[0]Những kí tự cuối cùng được khớp."dbbd"
myRelastIndexVị trí nơi mà bắt đầu so khớp lần sau. (Thuộc tính này chỉ được gán nếu biểu thức chính quy sử dụng lựa chọn g, được mô tả trong Advanced Searching With Flags ).5
sourceChuỗi biểu thức chính quy, được cập nhật tại thời điểm biểu thức được tạo, không phải tại lúc thực thi."d(b+)d"
+ +

Như dạng thứ 2 của ví dụ trên, bạn có thể dùng một biểu thức chính quy được khởi tạo mà không gán nó tới một biến. Tuy nhiên, nếu bạn làm thế, mỗi lần xuất hiện là một biểu thức chính quy mới. Vì lí do này, nếu bạn không gán nó vào một biến, bạn sẽ không thể truy cập các thuộc tính của biểu thức chính quy đó nữa. Ví dụ bạn có đoạn script sau:

+ +
var myRe = /d(b+)d/g;
+var myArray = myRe.exec("cdbbdbsbz");
+console.log("The value of lastIndex is " + myRe.lastIndex);
+
+ +

Kết quả hiển thị là:

+ +
The value of lastIndex is 5
+
+ +

Tuy nhiên nếu bạn chạy:

+ +
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
+console.log("The value of lastIndex is " + /d(b+)d/g.lastIndex);
+
+ +

Thì kết quả hiển thị sẽ là:

+ +
The value of lastIndex is 0
+
+ +

Sự xuất hiện của /d(b+)d/g trong 2 lệnh trên là những đối tượng biểu thức chính quy khác nhau và vì thế có những giá trị khác nhau cho thuộc tính lastIndex. Nếu bạn cần truy cập những thuộc tính của một biểu thức chính quy, bạn nên gán nó tới một biến.

+ +

Sử dụng nhiều dấu ngoặc tròn

+ +

Sử dụng nhiều ngoặc tròn trong một biểu thức chính quy cho ta nhiều kết quả so khớp tương ứng được nhớ. Cho ví dụ, /a(b)c/ khớp với 'abc' và nhớ 'b'. Để gọi lại những kết quả so khớp, sử dụng những phần tử của mảng [1]..., [n].

+ +

Số lượng các chuỗi con trong những ngoặc tròn là không giới hạn. Mảng trả về giữ lại tất cả mọi thứ được tìm thấy.

+ +

Ví dụ

+ +

Đoạn mã JavaScript dưới đây sử dụng phương thức replace() để giao hoán các từ trong chuỗi. Trong chuỗi thay thế, ta dùng $1 và $2 để chỉ các chuỗi khớp với mẫu trong ngoặc ở vị trí thứ 1 và 2.

+ +
var re = /(\w+)\s(\w+)/;
+var str = "John Smith";
+var newstr = str.replace(re, "$2, $1");
+console.log(newstr);
+
+ +

Kết quả hiển thị là: "Smith, John".

+ +

Tìm kiếm nâng cao với cờ

+ +

Biểu thức chính quy có 4 cờ cho phép tìm kiếm toàn cục và hoa thường. Những cờ này có thể được đứng riêng hoặc đứng gần nhau, và được coi như một phần của biểu thức chính quy.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bảng 4.4 Cờ của biểu thức chính quy
CờMô tả
gTìm kiếm toàn cục.
iTìm kiếm không phân biệt hoa thường.
mTìm đa dòng.
yThực thi một tìm kiếm "dính" - so khớp được bắt đầu ở vị trí hiện tại trong chuỗi mục tiêu.
+ +

{{ Fx_minversion_note(3, "Từ Firefox 3 đã hỗ trợ cờ y. Cờ y thất bại nếu so khớp không thành công ở vị trí hiện tại trong chuỗi mục tiêu.") }}

+ +

Để sử dụng cờ trong biểu thức chính quy, dùng cú pháp này:

+ +
var re = /pattern/flags;
+
+ +

hoặc

+ +
var re = new RegExp("pattern", "flags");
+
+ +

Lưu ý rằng cờ là một phần hợp thành một biểu thức chính quy. Chúng không thể được thêm hoặc gỡ bỏ sau đó.

+ +

Ví dụ, re = /\w+\s/g tạo một biểu thức chính quy dùng để tìm kiếm một hoặc nhiều kí tự theo sau bởi một khoảng trắng, và nó tìm kiếm tổ hợp này xuyên suốt chuỗi mục tiêu.

+ +
var re = /\w+\s/g;
+var str = "fee fi fo fum";
+var myArray = str.match(re);
+console.log(myArray);
+
+ +

Kết quả hiển thị là ["fee ", "fi ", "fo "].

+ +

Trong ví dụ này, bạn cũng có thể thay thế dòng này:

+ +
var re = /\w+\s/g;
+
+ +

bằng:

+ +
var re = new RegExp("\\w+\\s", "g");
+
+ +

và nhận được cùng một kết quả giống nhau.

+ +

Cờ m được sử dụng để xác định rằng một chuỗi đầu vào nên được đối xử như nhiều dòng. Nếu dùng cờ này, so khớp ^ và $ ở đầu và cuối ở mọi dòng trong chuỗi đầu vào thay vì ở đầu và cuỗi của toàn bộ chuỗi.

+ +

Ví dụ minh họa

+ +

Các ví dụ sau đây cho thấy một số cách sử dụng các biểu thức chính quy.

+ +

Thay đổi thứ tự trong một chuỗi

+ +

Ví dụ sau đây minh họa sự cấu thành các biểu thức chính quy và việc sử dụng các phương thức string.split()string.Replace(). Nó làm sạch một chuỗi đầu vào được định dạng có chứa tên (first name ở vị trí đầu tiên) cách nhau bởi khoảng trống, các tab và chỉ một dấu chấm phẩy duy nhất. Cuối cùng, nó đảo ngược thứ tự tên (last name ở vị trí đầu tiên) và sắp xếp lại danh sách.

+ +
// The name string contains multiple spaces and tabs,
+// and may have multiple spaces between first and last names.
+var names = "Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ";
+
+var output = ["---------- Original String\n", names + "\n"];
+
+// Prepare two regular expression patterns and array storage.
+// Split the string into array elements.
+
+// pattern: possible white space then semicolon then possible white space
+var pattern = /\s*;\s*/;
+
+// Break the string into pieces separated by the pattern above and
+// store the pieces in an array called nameList
+var nameList = names.split(pattern);
+
+// new pattern: one or more characters then spaces then characters.
+// Use parentheses to "memorize" portions of the pattern.
+// The memorized portions are referred to later.
+pattern = /(\w+)\s+(\w+)/;
+
+// New array for holding names being processed.
+var bySurnameList = [];
+
+// Display the name array and populate the new array
+// with comma-separated names, last first.
+//
+// The replace method removes anything matching the pattern
+// and replaces it with the memorized string—second memorized portion
+// followed by comma space followed by first memorized portion.
+//
+// The variables $1 and $2 refer to the portions
+// memorized while matching the pattern.
+
+output.push("---------- After Split by Regular Expression");
+
+var i, len;
+for (i = 0, len = nameList.length; i < len; i++){
+  output.push(nameList[i]);
+  bySurnameList[i] = nameList[i].replace(pattern, "$2, $1");
+}
+
+// Display the new array.
+output.push("---------- Names Reversed");
+for (i = 0, len = bySurnameList.length; i < len; i++){
+  output.push(bySurnameList[i]);
+}
+
+// Sort by last name, then display the sorted array.
+bySurnameList.sort();
+output.push("---------- Sorted");
+for (i = 0, len = bySurnameList.length; i < len; i++){
+  output.push(bySurnameList[i]);
+}
+
+output.push("---------- End");
+
+console.log(output.join("\n"));
+
+ +

Sử dụng những kí tự đặc biệt để xác thực đầu vào

+ +

Trong ví dụ dưới đây, ta mong chờ người dùng nhập một số điện thoại. Khi người dùng ấn nút "Check", đoạn script sẽ kiểm tra tính xác thực của số vừa nhập. Nếu số đó hợp lệ (khớp với chuỗi kí tự được xác định bởi biểu thức chính quy), website sẽ hiển thị một tin nhắn cảm ơn người dùng và xác nhận số đó. Nếu số đó không hợp lệ, website sẽ thông báo người dùng rằng số điện thoại vừa nhập không hợp lệ.

+ +

var re = /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/;

+ +

Trong mẫu ngoặc tròn không nhớ (?:, biểu thức chính quy tìm 3 kí tự số \d{3}  hoặc | 3 kí tự số trong cặp ngoặc tròn, (kết thúc mẫu ngoặc tròn không nhớ), sau đó là một kí tự gạch ngang, dấu chéo ngược, hoặc dấu chấm thập phân, và khi tìm thấy, nhớ kí tự vừa tìm được, tìm tiếp 3 kí tự số, theo sau là một so khớp được nhớ ở lần đầu tiên ([-\/\.]), cuối cùng là 4 kí tự số.

+ +

Sự kiện change được kích hoạt khi người dùng nhấp Enter.

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+    <meta http-equiv="Content-Script-Type" content="text/javascript">
+    <script type="text/javascript">
+      var re = /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/;
+      function testInfo(phoneInput){
+        var OK = re.exec(phoneInput.value);
+        if (!OK)
+          window.alert(OK.input + " isn't a phone number with area code!");
+        else
+          window.alert("Thanks, your phone number is " + OK[0]);
+      }
+    </script>
+  </head>
+  <body>
+    <p>Enter your phone number (with area code) and then click "Check".
+        <br>The expected format is like ###-###-####.</p>
+    <form action="#">
+      <input id="phone"><button onclick="testInfo(document.getElementById('phone'));">Check</button>
+    </form>
+  </body>
+</html>
+
+ +
{{PreviousNext("Web/JavaScript/Guide/Text_formatting", "Web/JavaScript/Guide/Indexed_collections")}}
diff --git a/files/vi/web/javascript/guide/text_formatting/index.html b/files/vi/web/javascript/guide/text_formatting/index.html new file mode 100644 index 0000000000..c3a09c6617 --- /dev/null +++ b/files/vi/web/javascript/guide/text_formatting/index.html @@ -0,0 +1,250 @@ +--- +title: Text formatting +slug: Web/JavaScript/Guide/Text_formatting +translation_of: Web/JavaScript/Guide/Text_formatting +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Numbers_and_dates", "Web/JavaScript/Guide/Regular_Expressions")}}
+

Chương này giới thiệu cách làm việc với các chuỗi và văn bản trong JavaScript.

+ +

Dây

+ +

Kiểu chuỗi của JavaScript được sử dụng để thể hiện dữ liệu văn bản. Nó là một tập hợp các "phần tử" của các giá trị nguyên không dấu 16 bit (đơn vị mã UTF-16). Mỗi phần tử trong Chuỗi chiếm một vị trí trong Chuỗi. Phần tử đầu tiên là ở chỉ số 0, phần tử tiếp theo ở chỉ số 1, v.v. Độ dài của Chuỗi là số phần tử trong đó. Bạn có thể tạo chuỗi bằng cách sử dụng chuỗi ký tự hoặc chuỗi đối tượng.

+ + + +

Chuỗi ký tự

+ +

Bạn có thể tạo các chuỗi đơn giản bằng cách sử dụng dấu ngoặc đơn hoặc dấu ngoặc kép:

+ +
'foo'
+"quán ba"
+ +

Các chuỗi nâng cao hơn có thể được tạo bằng các chuỗi thoát:

+ +

Trình tự thoát thập lục phân

+ +

The number after \x is interpreted as a hexadecimal number.

+ +
'\xA9' // "©"
+
+ +

Unicode escape sequences

+ +

The Unicode escape sequences require at least four hexadecimal digits following \u.

+ +
'\u00A9' // "©"
+ +

Unicode code point escapes

+ +

New in ECMAScript 2015. With Unicode code point escapes, any character can be escaped using hexadecimal numbers so that it is possible to use Unicode code points up to 0x10FFFF. With simple Unicode escapes it is often necessary to write the surrogate halves separately to achieve the same result.

+ +

See also {{jsxref("String.fromCodePoint()")}} or {{jsxref("String.prototype.codePointAt()")}}.

+ +
'\u{2F804}'
+
+// the same with simple Unicode escapes
+'\uD87E\uDC04'
+ +

String objects

+ +

The {{jsxref("String")}} object is a wrapper around the string primitive data type.

+ +
const foo = new String('foo'); // Creates a String object
+console.log(foo); // Displays: [String: 'foo']
+typeof foo; // Returns 'object'
+
+ +

You can call any of the methods of the String object on a string literal value—JavaScript automatically converts the string literal to a temporary String object, calls the method, then discards the temporary String object. You can also use the String.length property with a string literal.

+ +

You should use string literals unless you specifically need to use a String object, because String objects can have counterintuitive behavior. For example:

+ +
const firstString = '2 + 2'; // Creates a string literal value
+const secondString = new String('2 + 2'); // Creates a String object
+eval(firstString); // Returns the number 4
+eval(secondString); // Returns the string "2 + 2"
+ +

A String object has one property, length, that indicates the number of UTF-16 code units in the string. For example, the following code assigns helloLength the value 13, because "Hello, World!" has 13 characters, each represented by one UTF-16 code unit. You can access each code unit using an array bracket style. You can't change individual characters because strings are immutable array-like objects:

+ +
const hello = 'Hello, World!';
+const helloLength = hello.length;
+hello[0] = 'L'; // This has no effect, because strings are immutable
+hello[0]; // This returns "H"
+
+ +

Characters whose Unicode scalar values are greater than U+FFFF (such as some rare Chinese/Japanese/Korean/Vietnamese characters and some emoji) are stored in UTF-16 with two surrogate code units each. For example, a string containing the single character U+1F600 "Emoji grinning face" will have length 2. Accessing the individual code units in such a string using brackets may have undesirable consequences such as the formation of strings with unmatched surrogate code units, in violation of the Unicode standard. (Examples should be added to this page after MDN bug 857438 is fixed.) See also {{jsxref("String.fromCodePoint()")}} or {{jsxref("String.prototype.codePointAt()")}}.

+ +

A String object has a variety of methods: for example those that return a variation on the string itself, such as substring and toUpperCase.

+ +

The following table summarizes the methods of {{jsxref("String")}} objects.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Methods of String

+
MethodDescription
{{jsxref("String.charAt", "charAt")}}, {{jsxref("String.charCodeAt", "charCodeAt")}}, {{jsxref("String.codePointAt", "codePointAt")}}Return the character or character code at the specified position in string.
{{jsxref("String.indexOf", "indexOf")}}, {{jsxref("String.lastIndexOf", "lastIndexOf")}}Return the position of specified substring in the string or last position of specified substring, respectively.
{{jsxref("String.startsWith", "startsWith")}}, {{jsxref("String.endsWith", "endsWith")}}, {{jsxref("String.includes", "includes")}}Returns whether or not the string starts, ends or contains a specified string.
{{jsxref("String.concat", "concat")}}Combines the text of two strings and returns a new string.
{{jsxref("String.fromCharCode", "fromCharCode")}}, {{jsxref("String.fromCodePoint", "fromCodePoint")}}Constructs a string from the specified sequence of Unicode values. This is a method of the String class, not a String instance.
{{jsxref("String.split", "split")}}Splits a String object into an array of strings by separating the string into substrings.
{{jsxref("String.slice", "slice")}}Extracts a section of a string and returns a new string.
{{jsxref("String.substring", "substring")}}, {{jsxref("String.substr", "substr")}}Return the specified subset of the string, either by specifying the start and end indexes or the start index and a length.
{{jsxref("String.match", "match")}}, {{jsxref("String.matchAll", "matchAll")}}, {{jsxref("String.replace", "replace")}}, {{jsxref("String.search", "search")}}Work with regular expressions.
{{jsxref("String.toLowerCase", "toLowerCase")}}, {{jsxref("String.toUpperCase", "toUpperCase")}} +

Return the string in all lowercase or all uppercase, respectively.

+
{{jsxref("String.normalize", "normalize")}}Returns the Unicode Normalization Form of the calling string value.
{{jsxref("String.repeat", "repeat")}}Returns a string consisting of the elements of the object repeated the given times.
{{jsxref("String.trim", "trim")}}Trims whitespace from the beginning and end of the string.
+ +

Multi-line template literals

+ +

Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them.

+ +

Template literals are enclosed by the back-tick (` `) (grave accent) character instead of double or single quotes. Template literals can contain place holders. These are indicated by the Dollar sign and curly braces (${expression}).

+ +

Multi-lines

+ +

Any new line characters inserted in the source are part of the template literal. Using normal strings, you would have to use the following syntax in order to get multi-line strings:

+ +
console.log('string text line 1\n\
+string text line 2');
+// "string text line 1
+// string text line 2"
+ +

To get the same effect with multi-line strings, you can now write:

+ +
console.log(`string text line 1
+string text line 2`);
+// "string text line 1
+// string text line 2"
+ +

Embedded expressions

+ +

In order to embed expressions within normal strings, you would use the following syntax:

+ +
const five = 5;
+const ten = 10;
+console.log('Fifteen is ' + (five + ten) + ' and not ' + (2 * five + ten) + '.');
+// "Fifteen is 15 and not 20."
+ +

Now, with template literals, you are able to make use of the syntactic sugar making substitutions like this more readable:

+ +
const five = 5;
+const ten = 10;
+console.log(`Fifteen is ${five + ten} and not ${2 * five + ten}.`);
+// "Fifteen is 15 and not 20."
+ +

For more information, read about Template literals in the JavaScript reference.

+ +

Internationalization

+ +

The {{jsxref("Intl")}} object is the namespace for the ECMAScript Internationalization API, which provides language sensitive string comparison, number formatting, and date and time formatting. The constructors for {{jsxref("Collator")}}, {{jsxref("NumberFormat")}}, and {{jsxref("DateTimeFormat")}} objects are properties of the Intl object.

+ +

Date and time formatting

+ +

The {{jsxref("DateTimeFormat")}} object is useful for formatting date and time. The following formats a date for English as used in the United States. (The result is different in another time zone.)

+ +
const msPerDay = 24 * 60 * 60 * 1000;
+
+// July 17, 2014 00:00:00 UTC.
+const july172014 = new Date(msPerDay * (44 * 365 + 11 + 197));
+
+const options = { year: '2-digit', month: '2-digit', day: '2-digit',
+                hour: '2-digit', minute: '2-digit', timeZoneName: 'short' };
+const americanDateTime = new Intl.DateTimeFormat('en-US', options).format;
+
+console.log(americanDateTime(july172014)); // 07/16/14, 5:00 PM PDT
+
+ +

Number formatting

+ +

The {{jsxref("NumberFormat")}} object is useful for formatting numbers, for example currencies.

+ +
const gasPrice = new Intl.NumberFormat('en-US',
+                        { style: 'currency', currency: 'USD',
+                          minimumFractionDigits: 3 });
+
+console.log(gasPrice.format(5.259)); // $5.259
+
+const hanDecimalRMBInChina = new Intl.NumberFormat('zh-CN-u-nu-hanidec',
+                        { style: 'currency', currency: 'CNY' });
+
+console.log(hanDecimalRMBInChina.format(1314.25)); // ¥ 一,三一四.二五
+
+ +

Collation

+ +

The {{jsxref("Collator")}} object is useful for comparing and sorting strings.

+ +

For example, there are actually two different sort orders in German, phonebook and dictionary. Phonebook sort emphasizes sound, and it’s as if “ä”, “ö”, and so on were expanded to “ae”, “oe”, and so on prior to sorting.

+ +
const names = ['Hochberg', 'Hönigswald', 'Holzman'];
+
+const germanPhonebook = new Intl.Collator('de-DE-u-co-phonebk');
+
+// as if sorting ["Hochberg", "Hoenigswald", "Holzman"]:
+console.log(names.sort(germanPhonebook.compare).join(', '));
+// logs "Hochberg, Hönigswald, Holzman"
+
+ +

Some German words conjugate with extra umlauts, so in dictionaries it’s sensible to order ignoring umlauts (except when ordering words differing only by umlauts: schon before schön).

+ +
const germanDictionary = new Intl.Collator('de-DE-u-co-dict');
+
+// as if sorting ["Hochberg", "Honigswald", "Holzman"]:
+console.log(names.sort(germanDictionary.compare).join(', '));
+// logs "Hochberg, Holzman, Hönigswald"
+
+ +

For more information about the {{jsxref("Intl")}} API, see also Introducing the JavaScript Internationalization API.

+ +
{{PreviousNext("Web/JavaScript/Guide/Numbers_and_dates", "Web/JavaScript/Guide/Regular_Expressions")}}
diff --git a/files/vi/web/javascript/guide/using_promises/index.html b/files/vi/web/javascript/guide/using_promises/index.html new file mode 100644 index 0000000000..8ed6befe37 --- /dev/null +++ b/files/vi/web/javascript/guide/using_promises/index.html @@ -0,0 +1,329 @@ +--- +title: Sử dụng Promise +slug: Web/JavaScript/Guide/Using_promises +tags: + - Hướng dẫn + - JavaScript + - Promise + - bất đồng bộ + - trình độ trung cấp +translation_of: Web/JavaScript/Guide/Using_promises +--- +
{{jsSidebar("JavaScript Guide")}}{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}
+ +

{{jsxref("Promise")}} là một đối tượng thể hiện cho sự hoàn thành hoặc thất bại của một tiến trình bất đồng bộ. Vì đa số chúng ta là người sử dụng Promise được tạo sẵn, bài viết này sẽ hướng dẫn cách sử dụng Promise trước khi hướng dẫn cách tạo ra chúng.

+ +

Về cơ bản, một promise là một đối tượng trả về mà bạn gắn callback vào nó thay vì truyền callback vào trong một hàm.

+ +

Giả sử chúng ta có một hàm, createAudioFileAsync(), mà nó sẽ tạo ra một file âm thanh từ config object và hai hàm callback một cách bất đồng bộ, một hàm sẽ được gọi khi file âm thanh được tạo thành công, và một hàm được gọi khi có lỗi xảy ra.

+ +

Sau đây là code ví dụ sử dụng createAudioFileAsync():

+ +
function successCallback(result) {
+  console.log("Audio file ready at URL: " + result);
+}
+
+function failureCallback(error) {
+  console.log("Error generating audio file: " + error);
+}
+
+createAudioFileAsync(audioSettings, successCallback, failureCallback);
+
+ +

Thay vì như trên, các hàm bất đồng bộ hiện đại sẽ trả về đối tượng promise mà bạn sẽ gắn callback vào nó:

+ +

Nếu hàm createAudioFileAsync() được viết lại sử dụng promise, thì việc sử dụng nó sẽ chỉ đơn giản như sau:

+ +
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
+ +

Nếu viết dài dòng hơn thì sẽ là:

+ +
const promise = createAudioFileAsync(audioSettings);
+promise.then(successCallback, failureCallback);
+ +

Chúng ta gọi đây là một lời gọi hàm bất đồng bộ (asynchronous function call). Cách tiếp cận này có nhiều ưu điểm, mà chúng ta sẽ lần lượt xem xét bên dưới.

+ +

Sự đảm bảo

+ +

Không như cách truyền callback kiểu cũ, một promise có những đảm bảo như sau:

+ + + +

Một trong những đặc tính tuyệt vời của promise là chaining (gọi nối).

+ +

Chaining (gọi nối)

+ +

Có một nhu cầu phổ biến đó là thực thi hai hay nhiều tiến trình bất đồng độ liên tiến nhau, cái sau bắt đầu ngay khi cái trước hoàn tất, với giá trị truyền vào là kết quả từ bước trước đó. Mục tiêu này có thể đạt được với một chuỗi promise (promise chain).

+ +

Sau đây là cách nó hoạt động: hàm then() trả về một promise mới, khác với hàm ban đầu:

+ +
const promise = doSomething();
+const promise2 = promise.then(successCallback, failureCallback);
+
+ +

hoặc

+ +
const promise2 = doSomething().then(successCallback, failureCallback);
+
+ +

Promise thứ hai (promise2) không chỉ đại diện cho việc hoàn thành doSomething() mà còn cho cả successCallback hoặc failureCallback mà bạn đưa vào, mà chúng có thể là những hàm bất đồng bộ khác trả về promise. Trong trường hợp đó, bất kỳ callback nào được thêm vào cho promise2 cũng sẽ được xếp phía sau promise trả về bởi một trong hai successCallback hoặc failureCallback.

+ +

Tóm lại, mỗi promise đại diện cho việc hoàn tất của một bước bất đồng bộ trong chuỗi.

+ +

Trước khi có promise, kết quả của việc thực hiện một chuỗi các thao tác bất đồng bộ theo cách cũ là một "thảm họa" kim tự tháp callback:

+ +
doSomething(function(result) {
+  doSomethingElse(result, function(newResult) {
+    doThirdThing(newResult, function(finalResult) {
+      console.log('Got the final result: ' + finalResult);
+    }, failureCallback);
+  }, failureCallback);
+}, failureCallback);
+
+ +

Thay vào đó, với cách tiếp cận hiện đại, chúng ta sẽ gắn các callback vào các promise trả về, tạo thành một chuỗi promise:

+ +
doSomething().then(function(result) {
+  return doSomethingElse(result);
+})
+.then(function(newResult) {
+  return doThirdThing(newResult);
+})
+.then(function(finalResult) {
+  console.log('Got the final result: ' + finalResult);
+})
+.catch(failureCallback);
+
+ +

Tham số cho then là không bắt buộc, và catch(failureCallback) là cách viết gọn của then(null, failureCallback). Bạn có thể thấy chuỗi promise dùng với arrow function như sau:

+ +
doSomething()
+.then(result => doSomethingElse(result))
+.then(newResult => doThirdThing(newResult))
+.then(finalResult => {
+  console.log(`Got the final result: ${finalResult}`);
+})
+.catch(failureCallback);
+
+ +

Quan trọng: hãy nhớ luôn trả về kết quả, nếu không, callback sẽ không nhận được kết quả từ promise trước đó (với arrow functions () => x được rút gọn từ () => { return x; })

+ +

Gọi nối sau hàm catch

+ +

Bạn có thể tiếp tục gọi chuỗi then sau một hàm bắt lỗi catch. Điều này cho phép code của bạn luôn thực hiện một thao tác nào đó cho dù đã có lỗi xảy ra ở một bước nào đó trong chuỗi. Hãy xem ví dụ sau:

+ +
new Promise((resolve, reject) => {
+    console.log('Initial');
+
+    resolve();
+})
+.then(() => {
+    throw new Error('Something failed');
+
+    console.log('Do this');
+})
+.catch(() => {
+    console.log('Do that');
+})
+.then(() => {
+    console.log('Do this, no matter what happened before');
+});
+
+ +

Đoạn code này sẽ log ra những dòng sau:

+ +
Initial
+Do that
+Do this, no matter what happened before
+
+ +

Ghi chú: Dòng text Do this không hiển thị bởi vì Error Something failed đã xảy ra trước và gây lỗi trong chuỗi promise.

+ +

Xử lý lỗi tập trung

+ +

Bạn hãy nhớ lại đoạn code kim tự tháp thảm họa ở trên, có đến 3 lần hàm failureCallback được sử dụng. Trong khi đó, bạn chỉ cần khai báo một lần vào cuối chuỗi promise:

+ +
doSomething()
+.then(result => doSomethingElse(result))
+.then(newResult => doThirdThing(newResult))
+.then(finalResult => console.log(`Got the final result: ${finalResult}`))
+.catch(failureCallback);
+
+ +

Về căn bản, một chuỗi promise sẽ dừng lại nếu có lỗi xảy ra, và nó sẽ truy xuống dưới cuối chuỗi để tìm và gọi hàm xử lý lỗi catch. Cách hoạt động này khá giống với cách thức hoạt động của try catch của code đồng bộ:

+ +
try {
+  const result = syncDoSomething();
+  const newResult = syncDoSomethingElse(result);
+  const finalResult = syncDoThirdThing(newResult);
+  console.log(`Got the final result: ${finalResult}`);
+} catch(error) {
+  failureCallback(error);
+}
+
+ +

Và vì lý do trên, try catch cũng được sử dụng để bắt lỗi cho code bất đồng bộ khi viết với cú pháp async/await của ECMAScript 2017.

+ +
async function foo() {
+  try {
+    const result = await doSomething();
+    const newResult = await doSomethingElse(result);
+    const finalResult = await doThirdThing(newResult);
+    console.log(`Got the final result: ${finalResult}`);
+  } catch(error) {
+    failureCallback(error);
+  }
+}
+
+ +

Cú pháp trên được xây dựng từ Promise, VD: doSomething() chính là hàm được viết với Promise ở trên. Bạn có thể đọc thêm về cú pháp đó ở đây.

+ +

Promise giúp giải quyết một hạn chế cơ bản của kim tự tháp callback, đó là cho phép bắt được tất cả các loại lỗi, từ những lỗi throw Error cho đến lỗi về cú pháp lập trình. Điều này rất cần thiết cho việc phối hợp các hàm xử lý bất đồng bộ.

+ +

Tạo Promise từ API có kiểu callback cũ

+ +

Một {{jsxref("Promise")}} có thể được tạo mới từ đầu bằng cách sử dụng hàm constructor. Tuy nhiên cách này thường chỉ dùng để bọc lại API kiểu cũ.

+ +

Trong môi trường lý tưởng, tất cả các hàm bất đồng bộ đều trả về promise. Tuy nhiên vẫn còn nhiều API yêu cầu hàm callback được truyền vào theo kiểu cũ. Ví dụ điển hình nhất chính là hàm {{domxref("WindowTimers.setTimeout", "setTimeout()")}}:

+ +
setTimeout(() => saySomething("10 seconds passed"), 10000);
+
+ +

Trộn lẫn callback và promise có nhiều vấn đề tiềm ẩn. Nếu hàm saySomething() xảy ra lỗi bên trong nó, sẽ không có gì bắt được lỗi này. setTimeout là để đổ lỗi cho điều này.

+ +

May mắn là chúng ta có thể bọc setTimeout lại với promise. Cách làm tốt nhất là bọc hàm có vấn đề ở cấp thấp nhất, để rồi sau đó chúng ta không phải gọi nó trực tiếp nữa:

+ +
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
+
+ +

Về căn bản, constructor của Promise nhận vào một hàm thực thi với hai tham số hàm resolve và reject để chúng ta có thể giải quyết hoặc từ chối promise một cách thủ công. Bởi vì hàm setTimeout() không bao giờ gây ra lỗi, chúng ta bỏ qua reject trong trường hợp này.

+ +

Phối hợp các Promise

+ +

{{jsxref("Promise.resolve()")}} và {{jsxref("Promise.reject()")}} là những phương thức để lấy nhanh một promise đã được giải quyết hoặc từ chối sẵn. Những phương thức này có thể hữu dụng trong một số trường hợp.

+ +

{{jsxref("Promise.all()")}} và {{jsxref("Promise.race()")}} là hai hàm tiện ích dùng để phối hợp các thao tác bất đồng bộ chạy song song.

+ +

Chúng ta có thể cho các tiến trình bất đồng bộ bắt đầu song song và chờ cho đến khi tất cả đều hoàn tất như sau:

+ +
Promise.all([func1(), func2(), func3()])
+.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });
+
+ +

Việc phối hợp các tiến trình bất đồng bộ diễn ra tuần tự không có sẵn tiện ích nhưng có thể viết mẹo với reduce như sau:

+ +
[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
+.then(result3 => { /* use result3 */ });
+
+ +

Về cơ bản, chúng ta đang rút gọn (reduce, tạm dịch) một mảng các hàm bất đồng bộ thành một chuỗi promise: Promise.resolve().then(func1).then(func2).then(func3);

+ +

Thao tác trên có thể được viết thành một hàm dùng lại được, như cách chúng ta hay làm trong functional programming:

+ +
const applyAsync = (acc,val) => acc.then(val);
+const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
+ +

Hàm composeAsync() sẽ nhận vào tham số là các hàm xử lý bất đồng bộ với số lượng bất kỳ, và trả và một hàm mới mà khi gọi, nó nhận vào một giá trị ban đầu mà giá trị này sẽ được truyền vào tuần tự qua các hàm xử lý bất đồng bộ:

+ +
const transformData = composeAsync(func1, func2, func3);
+const result3 = transformData(data);
+
+ +

Trong ECMAScript 2017, việc phối hợp tuần tự các promise có thể thực hiện đơn giản hơn với async/await:

+ +
let result;
+for (const f of [func1, func2, func3]) {
+  result = await f(result);
+}
+/* use last result (i.e. result3) */
+
+ +

Thời điểm thực thi

+ +

Để nhất quán và tránh những bất ngờ, các hàm truyền vào cho then() sẽ không bao giờ được thực thi đồng bộ, ngay với  cả những promíe đã được giải quyết:

+ +
Promise.resolve().then(() => console.log(2));
+console.log(1); // 1, 2
+
+ +

Thay vì chạy ngay lập tức, promise callback được đặt vào hàng đợi microtask, điều này có nghĩa là nó sẽ chỉ được thực thi khi hàng đợi được làm rỗng ( các promise đều được giải quy) cuối event loop hiện tại của JavaScript:

+ +
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+wait().then(() => console.log(4));
+Promise.resolve().then(() => console.log(2)).then(() => console.log(3));
+console.log(1); // 1, 2, 3, 4
+
+ +

Lồng cấp

+ +

Chuỗi Promise đơn giản chỉ nên có một cấp và không lồng vào nhau, vì lồng cấp sẽ dẫn đến những tổ hợp phức tạp dễ gây ra lỗi. Xem các sai lầm thường gặp.

+ +

Lồng nhau là một cấu trúc kiểm soát để giới hạn phạm vi của các câu lệnh catch. Cụ thể, một câu lệnh catch lồng bên trong chỉ có thể bắt được những lỗi trong phạm vi của nó và bên dưới, không phải là lỗi phía bên trên của chuỗi bên ngoài phạm vi lồng nhau. Khi được sử dụng một cách hợp lý, nó mang lại độ chính xác cao hơn trong việc khôi phục lỗi:

+ +
doSomethingCritical()
+.then(result => doSomethingOptional()
+  .then(optionalResult => doSomethingExtraNice(optionalResult))
+  .catch(e => {})) // Ignore if optional stuff fails; proceed.
+.then(() => moreCriticalStuff())
+.catch(e => console.log("Critical failure: " + e.message));
+
+ +

Lưu ý rằng các bước tuỳ chọn ở đây được lồng vào trong, không phải từ việc thụt đầu dòng, mà từ vị trí đặt dấu ( và ) xung quanh chúng.

+ +

Câu lệnh catch bên trong chỉ bắt lỗi từ doSomethingOptional() và doSomethingExtraNice(), sau đó nó sẽ tiếp tục với moreCriticalStuff(). Điều quan trọng là nếu doSomethingCritical() thất bại, lỗi của nó chỉ bị bắt bởi catch cuối cùng (bên ngoài).

+ +

Một số sai lầm phổ biến

+ +

Dưới đây là một số lỗi phổ biến cần chú ý khi sử dụng chuỗi promise. Một số trong những sai lầm này biểu hiện trong ví dụ sau:

+ +
// Một ví dụ có đến 3 sai lầm!
+doSomething().then(function(result) {
+  doSomethingElse(result) // Quên trả về promise từ chuỗi lồng bên trong + lồng nhau không cần thiết
+  .then(newResult => doThirdThing(newResult));
+}).then(() => doFourthThing());
+// Quên kết thúc chuỗi bằng một hàm catch!
+
+ +

Sai lầm đầu tiên là không kết nối mọi thứ với nhau đúng cách. Điều này xảy ra khi chúng ta tạo ra một promise mới nhưng quên trả lại. Hậu quả là chuỗi bị hỏng, hay đúng hơn, chúng ta có hai chuỗi độc lập cùng chạy. Có nghĩa là doFourthThing() sẽ không đợi doSomethingElse() hoặc doThirdThing() kết thúc và sẽ chạy song song với chúng, có khả năng ngoài ý muốn. Các chuỗi riêng biệt cũng sẽ xử lý lỗi riêng biệt, dẫn đến khả năng lỗi không được xử lý.

+ +

Sai lầm thứ hai là làm lồng nhau không cần thiết, cho phép sai lầm đầu tiên. Lồng nhau cũng giới hạn phạm vi của các trình xử lý lỗi bên trong, điều mà nếu không cố ý thì có thể dẫn đến các lỗi chưa được xử lý. Một biến thể của điều này là​ ​​​​​​promise constructor anti-pattern, kết hợp lồng với việc sử dụng dự phòng của promise constructor để bọc mã đã sử dụng promise.

+ +

Sai lầm thứ ba là quên chấm dứt chuỗi với catch. Chuỗi promise không kết thúc bằng catch, khi bị reject sẽ gây ra lỗi "uncaught promise rejection" trong hầu hết các trình duyệt.

+ +

Một nguyên tắc tốt là luôn luôn trả lại hoặc chấm dứt chuỗi promise, và ngay khi bạn nhận được một promise mới, hãy trả lại ngay lập tức:

+ +
doSomething()
+.then(function(result) {
+  return doSomethingElse(result);
+})
+.then(newResult => doThirdThing(newResult))
+.then(() => doFourthThing())
+.catch(error => console.log(error));
+
+ +

Lưu ý rằng () => x được rút gọn từ () => { return x; }.

+ +

Bây giờ chúng ta có một chuỗi xác định duy nhất với xử lý lỗi thích hợp.

+ +

Sử dụng async/await sẽ khắc phục hầu hết, nếu không muốn nói là tất cả các vấn đề trên đây — với một hạn chế cũng là một lỗi thường gặp với cú pháp này đó là việc quên từ khoá await.

+ +

Xem thêm

+ + + +

{{PreviousNext("Web/JavaScript/Guide/Details_of_the_Object_Model", "Web/JavaScript/Guide/Iterators_and_Generators")}}

diff --git a/files/vi/web/javascript/guide/working_with_objects/index.html b/files/vi/web/javascript/guide/working_with_objects/index.html new file mode 100644 index 0000000000..6fdc2dba69 --- /dev/null +++ b/files/vi/web/javascript/guide/working_with_objects/index.html @@ -0,0 +1,500 @@ +--- +title: Working with objects +slug: Web/JavaScript/Guide/Working_with_Objects +translation_of: Web/JavaScript/Guide/Working_with_Objects +--- +
{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Details_of_the_Object_Model")}}
+ +

JavaScript được thiết kế trên mô hình đối tượng đơn giản. Một object là một tập hợp các thuộc tính, và một thuộc tính là sự liên kết giữa một cái tên và giá trị. Giá trị của một thuộc tính có thể là một hàm. Ngoài những đối tượng được định nghĩa trước trong trình duyệt, bạn có thể định nghĩa những đối tượng của riêng bạn. Chương này mô tả cách sử dụng những đối tượng, những thuộc tính, những hàm, và phương thức, cũng như cách thức để tạo đối tượng riêng của mình.

+ +

Tổng quan về Objects

+ +

Objects trong JavaScript, cũng tương tự như những ngôn ngữ khác, có thể so sánh như đối tượng trong đời thường. Khái niệm của objects trong JavaScript có thể được hiểu như những đối tượng thực tế trong đời thực.

+ +

Trong JavaScript, một object là một thực thể độc lập, với thuộc tính và kiểu. Lấy cái tách làm ví dụ. Cái tách là một object có những thuộc tính của riêng nó. Một cái tách có màu sắc, thiết kế, trọng lượng, chất liệu tạo ra nó, vân vân... Tương tự như vậy, JavaScript objects có thể có những thuộc tính định nghĩa nên đặc tính của nó.

+ +

Objects and thuộc tính

+ +

Một JavaScript object có những thuộc tính liên kết với nó. Một thuộc tính của một object có thể được giải thích như là một biến mà được gắn vào object đó. Những thuộc tính của object cơ bản cũng là những biến JavaScript bình thường, chỉ đặc biệt là được gắn lên object. Thuộc tính của object định nghĩa đặc tính của object. Chúng ta truy xuất thuộc tính của object với ký hiệu ".":

+ +
objectName.propertyName
+
+ +

Giống như những biến JavaScript, cả tên của object (có thể được xem như là 1 biến thông thường) và tên thuộc tính thì cũng phân biệt hoa thường. Bạn có thể định nghĩa một thuộc tính bằng cách gán giá trị cho nó. Ví dụ hãy tạo đối tượng myCar và gắn thuộc tính cho nó như make, model, and year như sau:

+ +
var myCar = new Object();
+myCar.make = 'Ford';
+myCar.model = 'Mustang';
+myCar.year = 1969;
+
+ +

Những thuộc tính không được gán giá trị sẽ có giá trị {{jsxref("undefined")}} (lưu ý nó là không {{jsxref("null")}}).

+ +
myCar.color; // undefined
+ +

Thuộc tính của JavaScript object có thể được truy xuất hoặc thiết lập bằng cách dùng dấu ngoặc vuông (xem property accessors để tham khảo chi tiết). Objects thỉnh thoảng cũng được gọi là mảng liên kết, vì mỗi thuộc tính được liên kết với một giá trị kiểu chuỗi mà có thể được dùng để truy xuất thuộc tính đó. Ví dụ bạn có thể truy xuất những thuộc tính của myCar object như sau:

+ +
myCar['make'] = 'Ford';
+myCar['model'] = 'Mustang';
+myCar['year'] = 1969;
+
+ +

Tên thuộc tính của một object có thế là chuỗi ký tự hợp lệ bất kỳ, hoặc bất kỳ thứ gì có thể chuyển đổi được thành chuỗi, bao gồm cả chuỗi rỗng. Tuy nhiên, bất kỳ tên thuộc tính nào mà không phải là 1 định danh hợp lệ trong JavaScript (ví dụ, một thuộc tính mà tên có khoảng trắng hoặc gạch ngang, hoặc bắt đầu bằng số) chỉ có thể truy xuất bằng cách dùng dấu ngoặc vuông []. Ký pháp này cũng rất hữu dụng khi tên thuộc tính được xác định động (khi tên thuộc tính chỉ được xác định lúc thực thi). Như trong ví dụ sau:

+ +
// Khởi tạo 4 biến và gán giá trị cho chúng trên cùng một dòng lệnh
+// phân cách bởi dấu ","
+var myObj = new Object(),
+    str = 'myString',
+    rand = Math.random(),
+    obj = new Object();
+
+myObj.type              = 'Dot syntax';
+myObj['date created']   = 'String with space';
+myObj[str]              = 'String value';
+myObj[rand]             = 'Random Number';
+myObj[obj]              = 'Object';
+myObj['']               = 'Even an empty string';
+
+console.log(myObj);
+
+ +

Chú ý tất cả biểu thức được làm khóa trong dấu ngoặc vuông đều được chuyển đổi thành kiểu chuỗi, bởi vì objects trong JavaScript chỉ chấp nhận khóa là kiểu chuỗi. Ví dụ trong đoạn mã trên khi khóa obj được thêm vào myObj, JavaScript sẻ gọi phương thức obj.toString(), và lấy kết quả đó làm khóa.

+ +

Bạn cũng có thể truy xuất thuộc tính bằng cách dùng giá trị chuỗi mà nó được lưu trong một biến:

+ +
var propertyName = 'make';
+myCar[propertyName] = 'Ford';
+
+propertyName = 'model';
+myCar[propertyName] = 'Mustang';
+
+ +

Bạn có thể sử dụng dấu ngoặc vuông với for...in để duyệt qua tất cả thuộc tính có thể đếm của object. Để minh họa cách nó hoạt động, hãy xem xét hàm sau đây, nó sẽ hiển thị những thuộc tính của đối tượng dựa vào 2 đối số gồm đối tượng (obj) và tên (objName) của đối tượng truyền vào cho hàm:

+ +
function showProps(obj, objName) {
+  var result = '';
+  for (var i in obj) {
+    // obj.hasOwnProperty() is used to filter out properties from the object's prototype chain
+    if (obj.hasOwnProperty(i)) {
+      result += objName + '.' + i + ' = ' + obj[i] + '\n';
+    }
+  }
+  return result;
+}
+
+ +

Như vậy, khi ta gọi showProps(myCar, "myCar") thì kết quả được trả về như sau:

+ +
myCar.make = Ford
+myCar.model = Mustang
+myCar.year = 1969
+ +

Liệt kê những thuộc tính của một object

+ +

Bắt đầu từ ECMAScript 5, có 3 cách để liệt kê/duyệt danh sách thuộc tính của object:

+ + + +

Trước ECMAScript 5, không có cách thức có sẵn để liệt kê tất cả thuộc tính của một object. Tuy nhiên, chúng ta có thực hiện việc  đó bằng hàm sau:

+ +
function listAllProperties(o) {
+	var objectToInspect;
+	var result = [];
+
+	for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)) {
+      result = result.concat(Object.getOwnPropertyNames(objectToInspect));
+	}
+
+	return result;
+}
+
+ +

Cách này rất hữu ích khi chúng ta muốn hiển thị những thuộc tính ẩn (những thuộc tính trong chuỗi prototype mà không thể truy xuất thông qua object, bởi vì một thuộc tính khác có cùng tên ở lớp trước của chuỗi prototype đã đè lên nó). Việc liệt kê những thuộc tính có thể truy xuất chỉ có thể dễ dàng hoàn thành bằng cách xóa bỏ trùng lặp trong mảng.

+ +

Tạo những object mới

+ +

JavaScript có một số object được định nghĩa sẵn. Ngoài ra, bạn có thể tạo định nghĩa những object mới. Bạn có thể tạo một object sử dụng bộ khởi tạo object (object initializer). Cách khác, bạn có thể tạo hàm dựng và rồi khởi tạo đối tượng bằng cách gọi hàm đó với toán tử new đặt trước.

+ +

Sử dụng bộ khởi tạo object

+ +

Ngoài việc khởi tạo object bằng cách dùng hàm dựng, bạn có thể tạo object bằng cách sử dụng bộ khởi tạo (object initializer). Sử dụng bộ khởi tạo thỉnh thoảng cũng được hiểu là cách khởi tạo bằng cách dùng literal. "Bộ khởi tạo" ("Object initializer") thì đồng nhất với thuật ngữ được sử dụng trong C++.

+ +

Cú pháp cho việc tạo một object bằng bộ khởi tạo la:

+ +
var obj = { property_1:   value_1,   // property_# may be an identifier...
+            2:            value_2,   // or a number...
+            // ...,
+            'property n': value_n }; // or a string
+
+ +

Trong đó obj là tên của object mới, mỗi property_i là một định danh (hoặc là một tên, một số, hoặc một chuỗi dạng literal), và mỗi value_i là một biểu thức mà giá trị được gán cho property_i . obj và phép gán là không bắt buộc; nếu bạn không cần tham chiếu đến object đó ở chổ khác, bạn không cần assign nó cho một biến. (lưu ý bạn có thể cần bao object literal lại bằng dấu ngoặc nếu nó xuất hiện ở chổ cần như là một câu lệnh, làm như vậy để tránh gây nhầm lẩn với câu lệnh khối.)

+ +

Bộ khởi tạo object là những biểu thức, và mỗi bộ khởi tạo object sẽ tạo ra object mỗi khi biểu thức đó được tính toán. Bộ khởi tạo object tạo ra những đối tượng riêng biệt và chúng không bằng nhau khi so sánh bằng ==. Những đối tượng được tạo ra bằng cách new Object() cũng như những đối tượng được tạo ra bằng object literal đều là thực thể (instance) của Object.

+ +

Câu lệnh sau tạo một đối tượng và gán nó cho biến x khi và chỉ khi biểu thức cond là true:

+ +
if (cond) var x = {greeting: 'hi there'};
+
+ +

Đoạn mã sau tạo đối tượng myHonda với ba thuộc tính. Lưu ý thuộc tính engine cũng là một object và nó có những thuộc tính riêng của nó.

+ +
var myHonda = {color: 'red', wheels: 4, engine: {cylinders: 4, size: 2.2}};
+
+ +

Bạn cũng có thể dùng bộ khởi tạo object để tạo mảng. Tham khảo array literals.

+ +

Sử dụng hàm dựng

+ +

Ngoài ra, bạn có thể tạo một object với hai bước sau đây:

+ +
    +
  1. Định nghĩa kiểu cho object bằng cách khai báo một hàm dựng. Bạn nên đặt tên hàm với chữ hoa cho ký tự đầu tiên.
  2. +
  3. Tạo một thực thể của đối tượng với toán tử new.
  4. +
+ +

Để định nghĩa một kiểu dữ liệu object, tạo một hàm cho kiểu dữ liệu đó rồi chỉ định tên, những thuộc tính và phương thưc. Ví dụ, giả sử bạn muốn tạo một kiểu dữ liệu cho cars. Bạn đặt tên cho kiểu dữ liệu đó là Car, và bạn muốn nó có những thuộc tính như make, model, và year. Để làm được điểu này, bạn sẽ viết hàm sau:

+ +
function Car(make, model, year) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+}
+
+ +

Lưu ý cách sử dụng this để gán trị giá trị cho những thuộc tính của object dựa vào giá trị được truyền vào cho hàm.

+ +

Bây giờ bạn có thể tạo một object tên mycar như sau:

+ +
var mycar = new Car('Eagle', 'Talon TSi', 1993);
+
+ +

Câu lệnh này tạo mycar gán những giá trị cụ thể cho những thuộc tính của nó. Giá trị của mycar.make là chuỗi "Eagle", mycar.year là số 1993, và tiếp tuc như thế.

+ +

Bạn có thể tạo bao nhiêu Car object đều được bằng toán tử new. Như ví dụ sau:

+ +
var kenscar = new Car('Nissan', '300ZX', 1992);
+var vpgscar = new Car('Mazda', 'Miata', 1990);
+
+ +

Một object có thể có một thuộc tính mà giá trị là một object khác. Ví dụ như, giả sử bạn định nghĩa một object tên person như sau:

+ +
function Person(name, age, sex) {
+  this.name = name;
+  this.age = age;
+  this.sex = sex;
+}
+
+ +

và sau đó khởi tạo hai object person mới như sau:

+ +
var rand = new Person('Rand McKinnon', 33, 'M');
+var ken = new Person('Ken Jones', 39, 'M');
+
+ +

Và rồi, bạn có thể viết lại định nghĩa của Car để thêm thuộc tính owner mà nhận giá trị là một đối tượng kiểu person, như sau:

+ +
function Car(make, model, year, owner) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+  this.owner = owner;
+}
+
+ +

Để khởi tạo object mới, bạn viết những câu lệnh sau:

+ +
var car1 = new Car('Eagle', 'Talon TSi', 1993, rand);
+var car2 = new Car('Nissan', '300ZX', 1992, ken);
+
+ +

Lưu ý rằng thay vì truyền vào một hằng chuỗi hoặc giá trị số khi tạo những object mới, câu lệnh trên truyền vào những đối tượng randken như là đối số cho owner. Sau đó nếu bạn muốn tìm tên của owner của car2, bạn có thể truy xuất như sau:

+ +
car2.owner.name
+
+ +

Lưu ý rằng bạn có thể luôn luôn thêm một thuộc tính mới vào những object đã được tạo ra. Như ví dụ câu lệnh sau:

+ +
car1.color = 'black';
+
+ +

thêm vào thuộc tính color cho car1, và gán giá trị cho nó là "black". Tuy nhiên, việc này không ảnh hưởng lên những object khác. Để thêm thuộc tính cho tất cả object của cùng một kiểu, bạn phải thêm thuộc tính đó khi định nghĩa kiểu đối tượng Car.
+  

+ +

Sử dụng Object.create

+ +

Những object có thể được tạo ra bằng phương thức {{jsxref("Object.create()")}}. Phương thức này có thể rất hữu ích, bởi vì nó cho phép bạn chọn prototype cho object bạn muốn tạo ra, mà không cần phải định nghĩa hàm dựng.

+ +
// Animal properties and method encapsulation
+var Animal = {
+  type: 'Invertebrates', // Default value of properties
+  displayType: function() {  // Method which will display type of Animal
+    console.log(this.type);
+  }
+};
+
+// Create new animal type called animal1
+var animal1 = Object.create(Animal);
+animal1.displayType(); // Output:Invertebrates
+
+// Create new animal type called Fishes
+var fish = Object.create(Animal);
+fish.type = 'Fishes';
+fish.displayType(); // Output:Fishes
+ +

Sự Kế  Thừa

+ +

Tất cả object trong JavaScript kế thừa từ ít nhất một object khác. Object mà được kế thừa từ nó thì được xem như là prototype, và những thuộc tính được kế thừa có thể được tìm thấy trong đối tượng prototype của hàm dựng. Xem thêm Inheritance and the prototype chain

+ +

Đánh chỉ mục thuộc tính object

+ +

Bạn có thể tham chiếu tới một object hoặc là bằng tên thuộc tính hoặc là bằng chỉ mục. Nếu bạn định nghĩa thuộc tính bằng tên, thì sau đó bạn phải dùng tên để truy xuất thuộc tính đó, còn nếu bạn định nghĩa thuộc tính bằng chỉ mục (một số), bạn phải dùng chỉ mục để truy xuất.

+ +

Giới hạn này áp dụng khi bạn tạo một object và thuộc tính của nó với hàm dựng (như chúng  ta đã làm với kiểu Car) và khi bạn định nghĩa những thuộc tính riêng lẻ một cách tường minh (ví dụ như , myCar.color = "red"). Nếu bạn định nghĩa thuộc tính với một chỉ mục, chẳng hạn như myCar[5] = "25 mpg", thì bạn phải tham chiếu đến thuộc tính đó với chỉ mục myCar[5].

+ +

Ngoại trừ nếu đó là object giống mảng (array-like), ví dụ như những object tạo ra từ HTML như forms object. Bạn có thể tham chiếu đến những object đó bằng cách hoặc là dùng số (dựa vào thứ tự nó xuất hiện trong document) hoặc là tên (nếu được định nghĩa). Ví dụ, nếu thẻ <FORM> thứ hai trong document có thuộc tính NAME là "myForm", bạn có thể tham chiếu đến form bằng cách document.forms[1] hoặc document.forms["myForm"] hoặc document.forms.myForm.

+ +

Định nghĩa những thuộc tính cho kiểu dữ liệu object

+ +

Bạn có thể thêm một thuộc tính cho một object đã định nghĩa trước đó bằng cách sử dụng thuộc tính prototype. Điều này định nghĩa một thuộc tính mà sẽ được chia sẻ bởi tất các các thuộc tính của cùng kiểu dữ liệu đó, không riêng cho một thực thể nào của kiểu đó. Đoạn mã sau sẽ thêm thuộc tính color cho tất cả các object của kiểu Car, và rồi gán giá trị cho thuộc tính color của object car1.

+ +
Car.prototype.color = null;
+car1.color = 'black';
+
+ +

Xem thêm prototype property của object Function trong JavaScript reference.

+ +

Định nghĩa phương thức

+ +

Một phương thức là một hàm liên kết với một object, hoặc, có thể nói phương thức là một thuộc tính của object có giá trị là một hàm. Phương thức được định nghĩa giống như cách định nghĩa hàm, ngoài trừ việc chúng phải được gán như là thuộc tính của một object. Xem thêm method definitions. Ví dụ:

+ +
objectName.methodname = functionName;
+
+var myObj = {
+  myMethod: function(params) {
+    // ...do something
+  }
+
+  // OR THIS WORKS TOO
+
+  myOtherMethod(params) {
+    // ...do something else
+  }
+};
+
+ +

objectName là một object đã được tạo trước đó, methodname là tên mà bạn đang gán cho phương thức, và functionName là tên của hàm.

+ +

Sau đó bạn có thể gọi hàm trong ngữ cảnh của object như sau:

+ +
object.methodname(params);
+
+ +

Bạn có thể định nghĩa phương thức của một kiểu object bằng cách thêm định nghĩa phương thức vào trong hàm dựng. Bạn có thể định nghĩa một hàm mà sẽ định dạng và hiển thị những thuộc tính của những đối tượng kiểu Car đã được định nghĩa trước, ví dụ:

+ +
function displayCar() {
+  var result = 'A Beautiful ' + this.year + ' ' + this.make
+    + ' ' + this.model;
+  pretty_print(result);
+}
+
+ +

trong đó pretty_print được giả định là một hàm đã được định nghĩa trước để hiển thị đường kẻ ngang và chuỗi kết quả. Lưu ý tham chiếu this đang trỏ đến đối tượng mà phương thức đang gắn trên đó.

+ +

Bạn có thể tạo hàm này thành phương thức của lớp Car bằng câu lệnh sau:

+ +
this.displayCar = displayCar;
+
+ +

khi định nghĩa kiểu object Car. Cài đặt đầy đủ của Car bây giờ sẽ là:

+ +
function Car(make, model, year, owner) {
+  this.make = make;
+  this.model = model;
+  this.year = year;
+  this.owner = owner;
+  this.displayCar = displayCar;
+}
+
+ +

Rồi sau đó bạn có thể gọi phương thức displayCar trên mỗi đối tượng như sau:

+ +
car1.displayCar();
+car2.displayCar();
+
+ +

Sử dụng this để tham chiếu đối tượng

+ +

JavaScript có từ khóa đặc biệt, this, mà bạn có thể sử dụng bên trong phương thức để tham chiếu đến object hiện tại. Ví dụ, giả sử bạn có một hàm tên là validate để kiểm tra giá trị của thuộc tính của một object là giá trị cao hay thấp:

+ +
function validate(obj, lowval, hival) {
+  if ((obj.value < lowval) || (obj.value > hival)) {
+    alert('Invalid Value!');
+  }
+}
+
+ +

Sau đó, bên trong hàm handler của event onchange của mỗi phần tử của form bạn có thể gọi hàm validate như ví dụ sau:

+ +
<input type="text" name="age" size="3"
+  onChange="validate(this, 18, 99)">
+
+ +

Nói chúng, this trỏ đến object đang gọi bên trong hàm.

+ +

Khi kết hợp với thuộc tính form, this có thể tham chiếu đến form cha của object hiện tại. Trong ví dụ sau, form myForm chứa đối tượng Text và một nút. Khi user nhấp lên cái nút, giá trị của đối tượng Text được gán bằng tên của form. Hàm thực thi của sự kiện onclick của cái nút sử dụng this.form để tham chiếu đến form cha, myForm.

+ +
<form name="myForm">
+<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
+<p><input name="button1" type="button" value="Show Form Name"
+     onclick="this.form.text1.value = this.form.name">
+</p>
+</form>
+ +

Định nghĩa getters và setters

+ +

Một getter là một phương thức mà nhận giá trị của một thuộc tính cụ thể. Một setter là một phương thức thiết lập giá trị cho một thuộc tính cụ thể. Bạn có thể định nghĩa getters và setters cho bất kỳ object nào có thể là do runtime tạo sẵn hoặc do người dùng định nghĩa. Chúng ta dùng cú pháp của object literal để tạo getter và setter.

+ +

Đoạn mã sau minh họa cách định nghĩa và thao tác trên getter và setter.

+ +
var o = {
+  a: 7,
+  get b() {
+    return this.a + 1;
+  },
+  set c(x) {
+    this.a = x / 2;
+  }
+};
+
+console.log(o.a); // 7
+console.log(o.b); // 8
+o.c = 50;
+console.log(o.a); // 25
+
+ +

Những thuộc tính của object o là:

+ + + +

Lưu ý ta dùng hàm để định nghĩa getter và setter, nhưng chúng ta truy xuất nó như một thuộc tính. Để định nghĩa một getter hoặc setter ta dùng cú pháp "[gs]et property()", Hoặc chúng ta một cách khác để định nghĩa getter hoặc setter là dùng Object.defineProperty  (hoặc dùng cách cũ với Object.prototype.__defineGetter__).

+ +

Đoạn mã sau minh họa cách dùng getter và setter mở rộng prototype của {{jsxref("Date")}} để thêm thuộc tính year cho tất cả thực thể được tạo ra từ lớp Date. Và dùng phương thức getFullYear and setFullYear để hỗ trợ getter và setter year.

+ +

Những câu lệnh sau định nghĩa getter và setter cho thuộc tính year:
+  

+ +
var d = Date.prototype;
+Object.defineProperty(d, 'year', {
+  get: function() { return this.getFullYear(); },
+  set: function(y) { this.setFullYear(y); }
+});
+
+ +

Những câu lệnh sau sử dụng getter và setter trong đối tượng Date:
+  

+ +
var now = new Date();
+console.log(now.year); // 2000
+now.year = 2001; // 987617605170
+console.log(now);
+// Wed Apr 18 11:13:25 GMT-0700 (Pacific Daylight Time) 2001
+
+ +

Điều cần ghi nhớ là, getter vad setter có thể:

+ + + +

Khi tạo getters và settes sử dụng bộ khởi tạo object điều bạn cần làm là đặt từ khóa  get trước tên phương thức getter và từ khóa set trước phương thức setter. Tất nhiên là phương thức getter không cần bất kỳ tham số nào, còn phương thức setter cần đúng một tham số. Như ví dụ sau:

+ +
var o = {
+  a: 7,
+  get b() { return this.a + 1; },
+  set c(x) { this.a = x / 2; }
+};
+
+ +

Getters and setters có thể được thêm vào một object bất kỳ khi nào sau khi được tạo bằng cách dùng phương thức Object.defineProperties. Tham số đầu tiên của phương thức này là object mà bạn muốn định nghĩa getter và setter. Tham số thứ hai là một object mà tên của thuộc tính là tên của getter và setter, giá trị của thuộc tính là object định nghĩa hàm getter và setter. Sau đây là ví dụ minh họa cách định nghĩa getter và setter

+ +
var o = { a: 0 };
+
+Object.defineProperties(o, {
+    'b': { get: function() { return this.a + 1; } },
+    'c': { set: function(x) { this.a = x / 2; } }
+});
+
+o.c = 10; // Runs the setter, which assigns 10 / 2 (5) to the 'a' property
+console.log(o.b); // Runs the getter, which yields a + 1 or 6
+
+ +

Việc lựa chọn cách nào để định nghĩa getter và setter tùy thuộc vào phong cách lập trình và công việc cụ thể. Nếu bạn luôn dùng bộ khởi tạo object khi định nghĩa một prototype thì bạn nên chọn cách thứ nhất. Cách này đơn giản và tự nhiên. Tuy nhiên, nếu bạn cần thêm getters và setters sau đó — bởi vì bạn không viết prototype hoặc object cụ thể — bạn phải sử dụng cách thứ hai. Cách thứ hai thể hiện tính chất "động" tự nhiên của JavaScript — nhưng nó làm cho code khó đọc và khó hiểu.

+ +

Việc xóa bỏ thuộc tính

+ +

Bạn có thể xóa bỏ một thuộc tính không kế thừa bằng toán tử  delete. Câu lệnh sau chỉ bạn cách để xóa bỏ một thuộc tính.

+ +
// Creates a new object, myobj, with two properties, a and b.
+var myobj = new Object;
+myobj.a = 5;
+myobj.b = 12;
+
+// Removes the a property, leaving myobj with only the b property.
+delete myobj.a;
+console.log ('a' in myobj); // yields "false"
+
+ +

Bạn cũng có thể dùng delete để xóa bỏ biến toàn cục nếu bạn không dùng từ khóa var khi định nghĩa biến.

+ +
g = 17;
+delete g;
+
+ +

So sánh objects

+ +

Trong JavaScript những object là kiểu tham chiếu. Hai đối tượng tách biệt không bao giờ bằng nhau, thậm chí nếu chúng có cùng những thuộc tính. Chỉ khi nó so sánh với chính nó thì kết quả mới là true.

+ +
// Two variables, two distinct objects with the same properties
+var fruit = {name: 'apple'};
+var fruitbear = {name: 'apple'};
+
+fruit == fruitbear; // return false
+fruit === fruitbear; // return false
+ +
// Two variables, a single object
+var fruit = {name: 'apple'};
+var fruitbear = fruit;  // assign fruit object reference to fruitbear
+
+// here fruit and fruitbear are pointing to same object
+fruit == fruitbear; // return true
+fruit === fruitbear; // return true
+
+ +
fruit.name = 'grape';
+console.log(fruitbear);    // yields { name: "grape" } instead of { name: "apple" }
+
+ +

Chi tiết về toán tử so sánh, xem thêm tại Comparison operators.

+ +

See also

+ + + +

{{PreviousNext("Web/JavaScript/Guide/Regular_Expressions", "Web/JavaScript/Guide/Details_of_the_Object_Model")}}

-- cgit v1.2.3-54-g00ecf