diff options
Diffstat (limited to 'files/vi/web/javascript/guide/functions/index.html')
-rw-r--r-- | files/vi/web/javascript/guide/functions/index.html | 668 |
1 files changed, 668 insertions, 0 deletions
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 +--- +<div>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}</div> + +<p class="summary">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.</p> + +<p>See also the <a href="/en-US/docs/Web/JavaScript/Reference/Functions">exhaustive reference chapter about JavaScript functions</a> to get to know the details.</p> + +<h2 id="Định_nghĩa_hàm">Định nghĩa hàm</h2> + +<h3 id="Khai_báo_hàm">Khai báo hàm</h3> + +<p><strong>Định nghĩa hàm</strong> (hay còn gọi là <strong>khai báo hằm</strong>, hoặc <strong>lệnh hàm</strong>) bao gồm từ khóa <a href="/en-US/docs/Web/JavaScript/Reference/Statements/function" title="function"><code>function</code></a>, theo sau nó là:</p> + +<ul> + <li>Tên của hàm.</li> + <li>Danh sách các tham số truyền vào hàm, được đặt trong ngoặc đơn và cách nhau bởi dấu phẩy.</li> + <li>Các câu lệnh của JavaScript để tạo ra một hàm, được đặt trong ngoặc nhọn <code>{...}</code>.</li> +</ul> + +<p>Ví dụ, để định nghĩa một hàm có tên là <code>square</code>:</p> + +<pre class="brush: js">function square(number) { + return number * number; +} +</pre> + +<p>Hàm <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">square</span></font> nhận 1 tham số, có tên là <code>number</code>. Hàm này bao gồm một câu lệnh mà nó sẽ trả về tham số (<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">number</span></font>) nhân với chính nó. Câu lệnh <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/return" title="return">return</a></code> chỉ định giá trị được trả lại bới hàm.</p> + +<pre class="brush: js">return number * number; +</pre> + +<p>Các tham số nguyên thủy (primitive parameters - ví dụ như một số) được truyền vào hàm <strong>bằng giá trị</strong>; giá trị được truyền vào hàm, nhưng nếu hàm thay đổi giá trị của tham số, <strong>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ó</strong> (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).</p> + +<p>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:</p> + +<pre class="brush: js">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) +</pre> + +<h3 id="Biểu_thức_hàm_hàm_trong_biến">Biểu thức hàm (hàm trong biến)</h3> + +<p>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 (<a href="https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function">function expression</a>). Một hàm như vậy có thể <strong>nặc danh</strong>; nó không cần phải có tên. Ví dụ, hàm <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">square</span></font> có thể được khai báo như sau:</p> + +<pre class="brush: js">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</pre> + +<p>Tuy nhiên, một cái tên <em>có thể</em> đượ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.</p> + +<pre class="brush: js">const factorial = function fac(n) { return n < 2 ? 1 : n * fac(n-1) }; + +console.log(factorial(3)); +</pre> + +<p>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 <code>map</code> 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.</p> + +<pre class="brush: js">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; +} +</pre> + +<p>Trong đoạn code dưới đây, hàm <code>map</code> 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à <code>map</code> 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):</p> + +<pre class="brush: js">map(function(x) {return x * x * x}, [0, 1, 2, 5, 10]); +</pre> + +<p>Kết quả trả về: [<code>0, 1, 8, 125, 1000]</code>.</p> + +<p>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 <code>myFunc</code> chỉ khi <code>num</code> bằng 0.</p> + +<pre class="brush: js">var myFunc; +if (num == 0){ + myFunc = function(theObject) { + theObject.make = "Toyota" + } +}</pre> + +<p>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()")}}.</p> + +<p>Một <strong>phương thức</strong> 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 <a href="/en-US/docs/Web/JavaScript/Guide/Working_with_Objects" title="en-US/docs/JavaScript/Guide/Working with Objects">Working with objects</a>.</p> + +<h2 id="Gọi_hàm">Gọi hàm</h2> + +<p><em>Việc định nghĩa</em> một hàm sẽ không <em>thực thi</em> 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.</p> + +<p><strong>Gọi </strong>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 <code>square</code>, bạn có thể gọi nó như sau:</p> + +<pre class="brush: js">square(5); +</pre> + +<p>Câu lệnh bên trên gọi hàm với một đối số của <code>5</code>. Hàm thực thi các câu lệnh của nó và trả về giá trị <code>25</code>.</p> + +<p>Các hàm phải đặt <em>trong phạm vi (</em><em>in scope)</em> 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:</p> + +<pre class="brush: js">console.log(square(5)); +/* ... */ +function square(n) { return n*n } +</pre> + +<p>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).</p> + +<div class="note"> +<p><strong>Ghi chú:</strong> Đ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ụ <code>function funcName(){}</code>). Đoạn code bên dưới sẽ không hoạt động.</p> + +<p>Đ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).</p> +</div> + +<pre class="brush: js example-bad">console.log(square); // ReferenceError: square is not defined +console.log(square(5)); // ReferenceError: square is not defined +square = function (n) { + return n * n; +} +</pre> + +<p>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 <code>show_props()</code>(được định nghĩa trong <a href="/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_Properties" title="https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Working_with_Objects#Objects_and_Properties">Working with objects</a>) là một ví dụ của một hàm mà nó nhận một object như là một đối số.</p> + +<p>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:</p> + +<pre class="brush: js">function factorial(n){ + if ((n == 0) || (n == 1)) + return 1; + else + return (n * factorial(n - 1)); +} +</pre> + +<p>Bạn có thể tính giai thừa của <code>1</code> tới <code>5</code> như sau:</p> + +<pre class="brush: js">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 +</pre> + +<p>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.</p> + +<p>Điều đó lại hóa ra là <em>các hàm tự bản thân chúng là các object</em>, 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.</p> + +<h2 class="deki-transform" id="Phạm_vi_của_hàm_function_scope">Phạm vi của hàm (function scope)</h2> + +<p>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.</p> + +<p>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.</p> + +<pre class="brush: js">// 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" +</pre> + +<h2 id="Phạm_vi_và_ngăn_xếp_của_hàm">Phạm vi và ngăn xếp của hàm</h2> + +<h3 id="Recursion">Recursion</h3> + +<p>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ó:</p> + +<ol> + <li>Dùng tên của hàm</li> + <li><code><a href="/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee">arguments.callee</a></code></li> + <li>Một biến in-scope mà có tham chiếu đến hàm.</li> +</ol> + +<p>Ví dụ, xem xét việc định nghĩa hàm sau đây:</p> + +<pre class="brush: js">var foo = function bar() { + // statements go here +}; +</pre> + +<p>Bên trong phần body của hàm, các điều sau là tương tự nhau:</p> + +<ol> + <li><code>bar()</code></li> + <li><code>arguments.callee()</code></li> + <li><code>foo()</code></li> +</ol> + +<p>Một hàm mà gọi chính nó được gọi là (<em>hàm đệ quy)</em> <em>recursive function</em>. 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... </p> + +<pre class="brush: js">var x = 0; +while (x < 10) { // "x < 10" là điều kiện lặp + // thực thi việc sau + x++; +} +</pre> + +<p>...có thể được chuyển đổi sang một hàm đệ quy:</p> + +<pre class="brush: js">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); +</pre> + +<p>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ư <a href="/en-US/docs/DOM">DOM</a>) sẽ được thực hiện dễ dàng hơn thông qua đệ quy:</p> + +<pre class="brush: js">function walkTree(node) { + if (node == null) // + return; + // do something with node + for (var i = 0; i < node.childNodes.length; i++) { + walkTree(node.childNodes[i]); + } +} +</pre> + +<p>So với hàm <code>loop</code>, mỗi một lần gọi đệ quy sẽ tạo ra nhiều lần gọi đệ quy tại đây.</p> + +<p>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).</p> + +<p>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:</p> + +<pre class="brush: js">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</pre> + +<h3 id="Hàm_lồng_nhau_và_các_closures">Hàm lồng nhau và các closures</h3> + +<p>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).</p> + +<p>Điều đó cũng định hình nên một <em>closure</em>. 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).</p> + +<p>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.</p> + +<p>Tóm tắt lại:</p> + +<ul> + <li>Hàm con chỉ có thể được truy cập từ các câu lệnh đặt bên trong hàm cha.</li> + <li>Hàm con sẽ định hình nên một closure: hàm con có thể sử dụng các đối số và các biến của hàm cha, trong khi hàm cha không thể sử dụng các đối số và các biến của hàm con.</li> +</ul> + +<p>Ví dụ sau chỉ ra các hàm được lồng nhau:</p> + +<pre class="brush: js">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 +</pre> + +<p>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.</p> + +<pre class="brush: js">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 +</pre> + +<h3 id="Sự_bảo_tồn_của_các_biến">Sự bảo tồn của các biến</h3> + +<p>Để ý cách mà <code>x</code> được bảo tồn sau khi hàm <code>inside</code> 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 <code>outside</code>. Bộ nhớ bảo tồn này chỉ có thể được giải phóng khi hàm <code>inside</code> được return không còn khả dụng.</p> + +<p>Đ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.</p> + +<h3 id="Các_hàm_lồng_nhau_nhiều_cấp">Các hàm lồng nhau nhiều cấp</h3> + +<p>Các hàm có thể được lồng nhau nhiều cấp. Ví dụ:</p> + +<ul> + <li>Một hàm (<code>A</code>) chứa một hàm (<code>B</code>), hàm (<code>B</code>) này chứa một hàm (<code>C</code>)</li> + <li>Ở đây, cả hai hàm <code>B</code> và <code>C</code> định hình nên các closures.Vì thế, <code>B</code> có thể truy cập đến <code>A</code>, và <code>C</code> có thể truy cập đến <code>B</code>.</li> + <li>Ngoài ra, vì <code>C</code> có thể truy cập đến <code>B</code> mà <code>B</code> truy cập được đến <code>A</code>, nên <code>C</code> cũng có thể truy cập đến <code>A</code>.</li> +</ul> + +<p>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à <em>scope chaining.</em> (Lí do tại sao gọi là "chaining" sẽ giải thích sau).</p> + +<p>Cân nhắc ví dụ sau:</p> + +<pre class="brush: js">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) +</pre> + +<p>Trong ví dụ này, <code>C</code> truy cập đến <code>y</code> của <code>B</code> và <code>x</code> của <code>A</code>. Điều này có thể đạt được bởi vì: </p> + +<ol> + <li><code>B</code> định hình một closure mà closure này bao gồm <code>A</code>, ví dụ <code>B</code> có thể truy cập đến các đối số và biến của <code>A</code>.</li> + <li><code>C</code> định hình nên một closure mà closure này bao gồm <code>B</code>.</li> + <li>Vì closure của <code>B</code> bao gồm <code>A</code>, closure của <code>C</code> bao gồm <code>A</code>, <code>C</code> có thể truy cập đến biến và đối số của cả hai hàm <code>A</code> và <code>B</code>. Nói cách khác, <code>C</code> <em>chains </em>the scopes của <code>B</code> và <code>A</code>, <em>theo đúng thứ tự đó</em>.</li> +</ol> + +<p>Tuy nhiên nếu chạy ngược lại thì không đúng. <code>A</code> không thể truy cập đến <code>C</code>, vì <code>A</code> không thể truy cập đén các đối số và biến của <code>B</code>, mà <code>C</code> chính là một biến của <code>B</code>. Vì vậy <code>C</code> duy trì quyền truy cập private chỉ riêng đối với <code>B</code>.</p> + +<h3 id="Xung_đột_tên_gọi">Xung đột tên gọi</h3> + +<p>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 <em>xung đột tên gọi,</em> 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:</p> + +<pre class="brush: js">function outside() { + var x = 10; + function inside(x) { + return x; + } + return inside; +} +result = outside()(20); // returns 20 thay vì 10 +</pre> + +<p>Xung đột tên gọi xảy ra tại câu lệnh <code>return x</code> giữa tham số <code>x</code> của hàm <code>inside</code> và tham số <code>x</code> của hàm <code>outside</code>. Scope chain ở đây là {<code>inside</code>, <code>outside</code>, global object}. Vì vậy tham số <code>x</code> của <code>inside</code> được ưu tiên hơn <code>x</code> của <code>outside</code>, và 20 (giá trị <code>x</code> của <code>insisde</code>) được trả về thay vì 10.</p> + +<h2 id="Closures">Closures</h2> + +<p>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).</p> + +<p>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.</p> + +<p>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.</p> + +<pre class="brush: js">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" +</pre> + +<p>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.</p> + +<pre class="brush: js">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 +</pre> + +<p>Tron đoạn code trên, các function con có thể truy cập vào biến <code>name</code> 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.</p> + +<pre class="brush: js">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 +</pre> + +<p><strong>Lưu ý</strong>: Có một vài cạm bẫy cần đề phòng khi sử dụng các closures!</p> + +<p>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.)</p> + +<pre class="brush: js">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 ??? + } + } +} +</pre> + +<p>The magical <code>this</code> variable is very tricky in closures. They have to be used carefully, as what <code>this</code> refers to depends completely on where the function was called, rather than where it was defined.</p> + +<h2 id="Sử_dụng_arguments_object">Sử dụng arguments object</h2> + +<p>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:</p> + +<pre class="brush: js">arguments[i] +</pre> + +<p>trong đó <code>i</code> 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à <code>arguments[0]</code>. Tổng số đối số được xác định bằng <code>arguments.length</code>.</p> + +<p>Sử dụng các <code>arguments</code> 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 <code>arguments.length</code> để 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 <code>arguments</code> object. </p> + +<p>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:</p> + +<pre class="brush: js">function myConcat(separator) { + var result = "", // initialize list + i; + // iterate through arguments + for (i = 1; i < arguments.length; i++) { + result += arguments[i] + separator; + } + return result; +} +</pre> + +<p>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.</p> + +<pre class="brush: js">// 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"); +</pre> + +<div class="note"> +<p><strong>Ghi chú:</strong> Biến <code>arguments</code> 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 <code>length</code>. Tuy nhiên, nó <em>không</em> sở hữu bất kỳ phương thức nào để thao tác sử dụng mảng.</p> +</div> + +<p>Xem {{jsxref("Function")}} object trong JavaScript reference để biết thêm.</p> + +<h2 id="Các_tham_số_của_function">Các tham số của function</h2> + +<p>Kể từ ES6, xuất hiện 2 dạng tham số mới: <em>default parameters</em> và <em>rest parameters</em></p> + +<h3 id="Default_parameters">Default parameters</h3> + +<p>Trong JavaScript, các tham số của function được mặc định là <code>undefined</code>. 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.</p> + +<h4 id="Khi_không_có_default_parameters_trước_ES6">Khi không có default parameters (trước ES6)</h4> + +<p>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à <code>undefined</code>.</p> + +<p>Trong ví dụ sau, nếu không có giá trị nào được truyền cho <code>b</code>, giá trị của nó sẽ là <code>undefined</code> khi thực hiện tính toán <code>a*b</code>, và việc gọi hàm <code>multiply</code> sẽ trả về <code>NaN</code>. Tuy nhiên, điều này bị ngăn chặn bởi dòng thứ 2 trong ví dụ này:</p> + +<pre class="brush: js">function multiply(a, b) { + b = typeof b !== 'undefined' ? b : 1; + + return a*b; +} + +multiply(5); // 5 +</pre> + +<h4 id="Khi_có_default_parameters_sau_ES6">Khi có default parameters (sau ES6)</h4> + +<p>Với <em>default parameters</em>, 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 <code>1</code> vào làm giá trị mặc định cho <code>b</code> ngay tại head của function:</p> + +<pre class="brush: js">function multiply(a, b = 1) { + return a*b; +} + +multiply(5); // 5</pre> + +<p>Để chi tiết hơn, xem <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">default parameters</a> trong phần tham khảo.</p> + +<h3 id="Rest_parameters">Rest parameters</h3> + +<p>Cú pháp <a href="/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest parameter</a> 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ố.</p> + +<p>Trong ví dụ sau, hàm <code>multiply</code> sử dụng <em>rest parameters</em> để 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.</p> + +<pre class="brush: js">function multiply(multiplier, ...theArgs) { + return theArgs.map(x => multiplier * x); +} + +var arr = multiply(2, 1, 2, 3); +console.log(arr); // [2, 4, 6]</pre> + +<h2 id="Arrow_functions">Arrow functions</h2> + +<p>Một <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">arrow function expression</a> (trước đây, và hiện tại được biết đến một cách không còn đúng là <strong>fat arrow function</strong>) có một cú pháp ngắn hơn function expressions và không có <code>this</code>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments">arguments</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super">super</a>, or <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target">new.target</a> của chính nó. Các arrow function luôn luôn là nặc danh. Xem "<a href="https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/">ES6 In Depth: Arrow functions</a>".</p> + +<p>Có 2 yếu tố dẫn đến việc giới thiệu arrow function: các <em>function ngắn hơn</em> và sự <em>non-binding</em> của <code>this</code> (lexical <code>this</code>).</p> + +<h3 id="Các_function_ngắn_hơn">Các function ngắn hơn</h3> + +<p>Trong một mẫu function, các function ngắn hơn được khuyến khích. So sánh:</p> + +<pre>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]</pre> + +<h3 id="No_separate_this_Lexical_this">No separate <code>this</code> (Lexical <code>this</code>)</h3> + +<p>Trước khi có arrow functions, mọi function mới sẽ tự định nghĩa giá trị <code>this</code> của nó (a new object in the case of a constructor, undefined in <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a> 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.</p> + +<pre class="brush: js">function Person() { + // Constructor của Person() tự định nghĩa `<code>this`</code>. + 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();</pre> + +<p>Trong ECMAScript 3/5, vấn đề này được sửa chữa bằng cách gán giá trị bên trong <code>this</code> cho một biến mà biến đó có thể được đóng hoàn toàn.</p> + +<pre class="brush: js">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(); +</pre> + +<h2 id="Predefined_functions">Predefined functions</h2> + +<p>JavaScript has several top-level, built-in functions:</p> + +<dl> + <dt>{{jsxref("Global_Objects/eval", "eval()")}}</dt> + <dd> + <p>The <code><strong>eval()</strong></code> method evaluates JavaScript code represented as a string.</p> + </dd> + <dt>{{jsxref("Global_Objects/uneval", "uneval()")}} {{non-standard_inline}}</dt> + <dd> + <p>The <code><strong>uneval()</strong></code> method creates a string representation of the source code of an {{jsxref("Object")}}.</p> + </dd> + <dt>{{jsxref("Global_Objects/isFinite", "isFinite()")}}</dt> + <dd> + <p>The global <code><strong>isFinite()</strong></code> function determines whether the passed value is a finite number. If needed, the parameter is first converted to a number.</p> + </dd> + <dt>{{jsxref("Global_Objects/isNaN", "isNaN()")}}</dt> + <dd> + <p>The <code><strong>isNaN()</strong></code> function determines whether a value is {{jsxref("Global_Objects/NaN", "NaN")}} or not. Note: coercion inside the <code>isNaN</code> function has <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN#Description">interesting</a> rules; you may alternatively want to use {{jsxref("Number.isNaN()")}}, as defined in ECMAScript 6, or you can use <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/typeof">typeof</a></code> to determine if the value is Not-A-Number.</p> + </dd> + <dt>{{jsxref("Global_Objects/parseFloat", "parseFloat()")}}</dt> + <dd> + <p>The <code><strong>parseFloat()</strong></code> function parses a string argument and returns a floating point number.</p> + </dd> + <dt>{{jsxref("Global_Objects/parseInt", "parseInt()")}}</dt> + <dd> + <p>The <code><strong>parseInt()</strong></code> function parses a string argument and returns an integer of the specified radix (the base in mathematical numeral systems).</p> + </dd> + <dt>{{jsxref("Global_Objects/decodeURI", "decodeURI()")}}</dt> + <dd> + <p>The <code><strong>decodeURI()</strong></code> function decodes a Uniform Resource Identifier (URI) previously created by {{jsxref("Global_Objects/encodeURI", "encodeURI")}} or by a similar routine.</p> + </dd> + <dt>{{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent()")}}</dt> + <dd> + <p>The <code><strong>decodeURIComponent()</strong></code> method decodes a Uniform Resource Identifier (URI) component previously created by {{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent")}} or by a similar routine.</p> + </dd> + <dt>{{jsxref("Global_Objects/encodeURI", "encodeURI()")}}</dt> + <dd> + <p>The <code><strong>encodeURI()</strong></code> 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).</p> + </dd> + <dt>{{jsxref("Global_Objects/encodeURIComponent", "encodeURIComponent()")}}</dt> + <dd> + <p>The <code><strong>encodeURIComponent()</strong></code> 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).</p> + </dd> + <dt>{{jsxref("Global_Objects/escape", "escape()")}} {{deprecated_inline}}</dt> + <dd> + <p>The deprecated <code><strong>escape()</strong></code> 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.</p> + </dd> + <dt>{{jsxref("Global_Objects/unescape", "unescape()")}} {{deprecated_inline}}</dt> + <dd> + <p>The deprecated <code><strong>unescape()</strong></code> 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 <code>unescape()</code> is deprecated, use {{jsxref("Global_Objects/decodeURI", "decodeURI()")}} or {{jsxref("Global_Objects/decodeURIComponent", "decodeURIComponent")}} instead.</p> + </dd> +</dl> + +<p>{{PreviousNext("Web/JavaScript/Guide/Loops_and_iteration", "Web/JavaScript/Guide/Expressions_and_Operators")}}</p> |