Cách viết hàm ngắn gọn hơn
Arrow function là cú pháp ngắn gọn hơn của JavaScript để định nghĩa hàm (function). Chúng xuất hiện từ ES2015 và nay là kiểu viết mặc định cho các hàm nhỏ viết tại chỗ, nhất là callback truyền vào phương thức mảng, trình xử lý promise và event listener.
Hình dạng của nó là: tham số, rồi =>, rồi thân hàm.
Kết quả giống hệt dạng cổ điển:
const add = function (a, b) {
return a + b;
};
Gõ ít hơn thì tốt, nhưng lý do thật sự để quan tâm đến arrow function là cơ chế trả về ngầm định và cách nó xử lý this. Cả hai sẽ được nói ngay sau đây.
Trả về ngầm định
Nếu thân hàm chỉ là một biểu thức, bạn có thể bỏ dấu ngoặc nhọn và return. Giá trị của biểu thức được trả về tự động:
Đây là lúc arrow function tỏa sáng: những phép biến đổi một dòng đọc như công thức toán:
Ngay khi cần nhiều hơn một câu lệnh, bạn lại phải dùng dấu ngoặc nhọn và return tường minh:
Quên return bên trong dấu ngoặc nhọn là lỗi phổ biến nhất với arrow function. x => { x * 2 } trả về undefined: dấu ngoặc nhọn biến thân hàm thành một khối lệnh, và biểu thức bên trong bị bỏ đi.
Dấu ngoặc tròn quanh tham số
Khi có đúng một tham số, dấu ngoặc tròn là tùy chọn:
Khi không có tham số, hoặc có từ hai tham số trở lên, bạn phải dùng ngoặc:
Một số nhóm thích luôn viết ngoặc cho nhất quán. Cả hai kiểu đều ổn: chọn một kiểu và giữ nguyên.
Trả về một object literal
Đây là một cái bẫy. Nếu bạn cố trả về ngầm định một đối tượng, dấu ngoặc nhọn trông giống thân hàm:
Đoạn này in ra undefined. JavaScript đọc { name: name } thành một khối lệnh chứa một câu lệnh có nhãn, chứ không phải object literal. Bọc đối tượng trong dấu ngoặc tròn để buộc nó thành một biểu thức:
Cặp () bao quanh { ... } chính là cách sửa. Ghi nhớ điều này nhé, bạn sẽ gặp nó ngay trong tuần đầu viết arrow function.
this theo phạm vi từ vựng
Lý do thật sự để arrow function tồn tại không phải là cú pháp ngắn hơn. Mà là nó không có this của riêng mình. Nó kế thừa this từ phạm vi bao quanh.
Để thấy vì sao điều đó quan trọng, hãy xem một hàm thường được dùng làm callback:
Bên trong function () { ... } được truyền cho setInterval, this không phải là counter. Hàm thường có this riêng tùy theo cách nó được gọi, và setInterval gọi callback với this là undefined (ở strict mode) hoặc đối tượng toàn cục.
Arrow function giữ this của phương thức bao quanh nó:
Bên trong arrow function, this vẫn là counter vì arrow function không tạo ra this riêng. Trước khi có arrow function, người ta lách vấn đề này bằng const self = this; hoặc .bind(this). Những cách đó vẫn chạy nhưng giờ hiếm khi cần đến.
Những gì arrow function không có
Quy tắc this theo phạm vi từ vựng là một phần của một khuôn mẫu lớn hơn: arrow function bỏ qua một số thứ mà hàm thường có.
- Không có
thisriêng: kế thừa từ phạm vi bao quanh. - Không có đối tượng
arguments: dùng rest parameter (...args) thay thế. - Không dùng được với
new: bạn không thể dùng arrow function làm hàm khởi tạo. - Không có thuộc tính
prototype: hệ quả của việc không dùng đượcnew.
...args cho bạn khả năng "nhận bao nhiêu đối số cũng được" giống như arguments, kèm theo lợi thế là nó là một mảng thật. Lời gọi new Greeter(...) ném lỗi vì arrow function không phải hàm khởi tạo.
Khi nào không nên dùng arrow function
Arrow function là lựa chọn mặc định cho callback, nhưng lại là lựa chọn sai ở một vài chỗ:
Phương thức của đối tượng viết bằng cú pháp mũi tên không có this gắn với đối tượng: nó kế thừa this từ nơi object literal được viết (thường là phạm vi module hoặc toàn cục). Hãy dùng cú pháp phương thức rút gọn (greet() { ... }) cho phương thức.
Tương tự với phương thức trên prototype của lớp, phương thức xử lý sự kiện cần this trỏ tới phần tử, và mọi hàm bạn định gọi bằng new. Ở những chỗ đó, function thông thường mới là công cụ đúng.
Quy tắc nhanh
- Callback ngắn, chỉ một biểu thức? Arrow function.
- Cần
thisgắn với phạm vi bên ngoài? Arrow function. - Định nghĩa phương thức trên đối tượng hoặc prototype của lớp? Hàm thường (hoặc phương thức rút gọn).
- Viết hàm khởi tạo? Hàm thường, hoặc tốt hơn nữa là một
class.
Bạn sẽ thấy cả hai kiểu trong bất kỳ codebase JavaScript thực tế nào. Biết kiểu nào phù hợp cần vài tuần đọc code của người khác; sau đó, việc lựa chọn sẽ thành phản xạ.
Tiếp theo: tham số và giá trị mặc định
Arrow function và hàm thường có chung các tính năng về tham số: giá trị mặc định, rest parameter, destructuring đối số và hơn thế nữa. Đó là nội dung của trang tiếp theo, và nó áp dụng cho mọi dạng hàm bạn đã gặp đến giờ.
Câu hỏi thường gặp
Arrow function trong JavaScript là gì?
Arrow function là cách viết hàm ngắn gọn hơn, dùng cú pháp =>. const add = (a, b) => a + b; là một arrow function nhận hai đối số và trả về tổng của chúng. Ngoài cú pháp gọn hơn, arrow function không có this, arguments hay super của riêng mình mà kế thừa chúng từ phạm vi bao quanh.
Arrow function và hàm thường khác nhau thế nào?
Hàm thường khai báo bằng function có this riêng, đối tượng arguments riêng và có thể dùng làm hàm khởi tạo với new. Arrow function không có những thứ đó: nó kế thừa this từ nơi nó được định nghĩa. Arrow function cũng không được hoisting như khai báo hàm.
Khi nào nên dùng arrow function?
Dùng arrow function cho các callback ngắn (arr.map(x => x * 2)) và ở bất cứ đâu bạn muốn this vẫn gắn với phạm vi bao quanh, chẳng hạn bên trong một phương thức của lớp truyền trình xử lý cho setTimeout hoặc một event listener. Dùng function thông thường cho phương thức của đối tượng, hàm khởi tạo và các hàm độc lập ở cấp ngoài cùng.