Functions

Bản dịch này chưa hoàn thành. Xin hãy giúp dịch bài viết này từ tiếng Anh

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à:

  • Tên của hàm.
  • 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.
  • Các câu lệnh của JavaScript để tạo ra một hàm, được đặt trong ngoặc nhọn {...}.

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 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 Function constructor to create functions from a string at runtime, much like 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 Function object). Một trong số chúng, phương thức 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. arguments.callee
  3. Một biến in-scope mà có tham chiếu đến hàm.

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. arguments.callee()
  3. foo()

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:

  • Hàm con chỉ có thể được truy cập từ các câu lệnh đặt bên trong hàm cha.
  • 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.

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ụ:

  • Một hàm (A) chứa một hàm (B), hàm (B) này chứa một hàm (C)
  • Ở đây, cả hai hàm B và C định hình nên các closures.Vì thế, B có thể truy cập đến A, và C có thể truy cập đến B.
  • Ngoài ra, vì C có thể truy cập đến B mà B truy cập được đến A, nên C cũng có thể truy cập đến A.

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. C định hình nên một closure mà closure này bao gồm B.
  3. 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ự đó.

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

eval()

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

uneval()

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

isFinite()

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

isNaN()

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

parseFloat()

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

parseInt()

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

decodeURI()

The decodeURI() function decodes a Uniform Resource Identifier (URI) previously created by encodeURI or by a similar routine.

decodeURIComponent()

The decodeURIComponent() method decodes a Uniform Resource Identifier (URI) component previously created by encodeURIComponent or by a similar routine.

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).

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).

escape()

The deprecated escape() method computes a new string in which certain characters have been replaced by a hexadecimal escape sequence. Use encodeURI or encodeURIComponent instead.

unescape()

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 escape. Because unescape() is deprecated, use decodeURI() or decodeURIComponent instead.