Theo đề xuất , các mũi tên nhằm "giải quyết và giải quyết một số điểm đau chung của truyền thống Function Expression". Họ dự định cải thiện vấn đề bằng cách ràng buộc từ thisvựng và đưa ra cú pháp ngắn gọn.
Tuy nhiên,
- Người ta không thể liên kết từ
thisvựng
- Cú pháp hàm mũi tên là tinh tế và mơ hồ
Do đó, các hàm mũi tên tạo cơ hội cho sự nhầm lẫn và lỗi và nên được loại trừ khỏi từ vựng của lập trình viên JavaScript, được thay thế bằng functionđộc quyền.
Về từ vựng this
this có vấn đề:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Các hàm mũi tên có ý định khắc phục sự cố trong đó chúng ta cần truy cập vào một thuộc tính thisbên trong một cuộc gọi lại. Đã có một số cách để làm điều đó: Người ta có thể gán thischo một biến, sử dụng bindhoặc sử dụng đối số thứ 3 có sẵn trên các Arrayphương thức tổng hợp. Tuy nhiên, mũi tên dường như là cách giải quyết đơn giản nhất, vì vậy phương pháp có thể được cấu trúc lại như thế này:
this.pages.forEach(page => page.draw(this.settings));
Tuy nhiên, hãy xem xét nếu mã sử dụng một thư viện như jQuery, có phương thức liên kết thisđặc biệt. Bây giờ, có hai thisgiá trị để giải quyết:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Chúng ta phải sử dụng functionđể cho eachđể ràng buộc thisđộng. Chúng ta không thể sử dụng chức năng mũi tên ở đây.
Đối phó với nhiều thisgiá trị cũng có thể gây nhầm lẫn, bởi vì thật khó để biết thistác giả nào đang nói về:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
Có phải tác giả thực sự có ý định gọi Book.prototype.reformat? Hay anh quên không ràng buộc this, và có ý định gọi Reader.prototype.reformat? Nếu chúng ta thay đổi trình xử lý thành hàm mũi tên, chúng ta sẽ tự hỏi tương tự liệu tác giả có muốn động không this, nhưng đã chọn một mũi tên vì nó phù hợp trên một dòng:
function Reader() {
this.book.on('change', () => this.reformat());
}
Người ta có thể đặt ra: "Điều đặc biệt là mũi tên đôi khi có thể là chức năng sai khi sử dụng? Có lẽ nếu chúng ta hiếm khi chỉ cần các thisgiá trị động , thì vẫn có thể sử dụng mũi tên trong hầu hết thời gian."
Nhưng hãy tự hỏi mình điều này: "Sẽ là" đáng giá "để gỡ lỗi mã và thấy rằng kết quả của một lỗi đã được đưa ra bởi một 'trường hợp cạnh?'" Tôi muốn tránh rắc rối không chỉ trong hầu hết thời gian, nhưng 100% thời gian.
Có một cách tốt hơn: Luôn luôn sử dụng function(vì vậy thisluôn có thể bị ràng buộc động) và luôn tham chiếu thisqua một biến. Các biến là từ vựng và giả sử nhiều tên. Việc gán thischo một biến sẽ làm cho ý định của bạn rõ ràng:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Hơn nữa, luôn luôn gán thischo một biến (ngay cả khi có một thischức năng duy nhất hoặc không có chức năng nào khác) đảm bảo ý định của một người vẫn rõ ràng ngay cả sau khi mã được thay đổi.
Ngoài ra, năng động thislà khó khăn đặc biệt. jQuery được sử dụng trên hơn 50 triệu trang web (kể từ khi viết vào tháng 2 năm 2016). Dưới đây là các API khác ràng buộc thisđộng:
- Mocha (~ 120k lượt tải ngày hôm qua) hiển thị các phương thức cho các thử nghiệm của nó thông qua
this.
- Grunt (~ 63k lượt tải ngày hôm qua) hiển thị các phương thức để xây dựng các tác vụ thông qua
this.
- Xương sống (~ 22k lượt tải ngày hôm qua) xác định phương thức truy cập
this.
- API tổ chức sự kiện (như của DOM) đề cập đến một
EventTargetvới this.
- API nguyên mẫu được vá hoặc mở rộng tham chiếu đến các trường hợp với
this.
(Số liệu thống kê qua http://trends.builtwith.com/javascript/jQuery và https://www.npmjs.com .)
Bạn có khả năng yêu cầu thisràng buộc năng động đã.
Một từ vựng thisđôi khi được mong đợi, nhưng đôi khi không; giống như một động lực thisđôi khi được mong đợi, nhưng đôi khi không. Rất may, có một cách tốt hơn, luôn tạo ra và truyền đạt sự ràng buộc mong đợi.
Về cú pháp ngắn gọn
Các hàm mũi tên đã thành công trong việc cung cấp "dạng cú pháp ngắn hơn" cho các hàm. Nhưng những chức năng ngắn này sẽ làm cho bạn thành công hơn?
Là x => x * x"dễ đọc" hơn function (x) { return x * x; }? Có lẽ là như vậy, bởi vì nó có nhiều khả năng tạo ra một dòng mã ngắn. Tham gia vào Dyson ' Ảnh hưởng của tốc độ đọc và độ dài dòng đến hiệu quả của việc đọc từ màn hình ,
Độ dài dòng trung bình (55 ký tự trên mỗi dòng) xuất hiện để hỗ trợ đọc hiệu quả ở tốc độ bình thường và nhanh. Điều này tạo ra mức độ hiểu biết cao nhất. . .
Các biện minh tương tự được thực hiện cho toán tử điều kiện (ternary) và cho các ifcâu lệnh đơn.
Tuy nhiên, bạn có thực sự viết các hàm toán học đơn giản được quảng cáo trong đề xuất không? Miền của tôi không phải là toán học, vì vậy chương trình con của tôi hiếm khi thanh lịch như vậy. Thay vào đó, tôi thường thấy các hàm mũi tên phá vỡ giới hạn cột và bọc sang một dòng khác do trình chỉnh sửa hoặc hướng dẫn kiểu, làm vô hiệu hóa "khả năng đọc" theo định nghĩa của Dyson.
Người ta có thể đặt ra, "Làm thế nào về việc chỉ sử dụng phiên bản ngắn cho các chức năng ngắn, khi có thể?" Nhưng bây giờ một quy tắc phong cách mâu thuẫn với một ràng buộc ngôn ngữ: "Cố gắng sử dụng ký hiệu chức năng ngắn nhất có thể, hãy nhớ rằng đôi khi chỉ có ký hiệu dài nhất sẽ ràng buộc thisnhư mong đợi." Sự kết hợp như vậy làm cho mũi tên đặc biệt dễ bị sử dụng sai.
Có nhiều vấn đề với cú pháp hàm mũi tên:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Cả hai chức năng này đều có giá trị cú pháp. Nhưng doSomethingElse(x);không phải trong cơ thể b, nó chỉ là một tuyên bố cấp thấp, thụt lề.
Khi mở rộng sang dạng khối, không còn ẩn return, người ta có thể quên khôi phục. Nhưng biểu thức chỉ có thể được dự định để tạo ra một hiệu ứng phụ, vậy ai biết nếu một điều rõ ràng returnsẽ là cần thiết trong tương lai?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
Những gì có thể được dự định là một tham số phần còn lại có thể được phân tích cú pháp như toán tử trải rộng:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
Bài tập có thể bị nhầm lẫn với các đối số mặc định:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Các khối trông giống như các đối tượng:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Điều đó có nghĩa là gì?
() => {}
Có phải tác giả đã có ý định tạo ra một no-op, hoặc một hàm trả về một đối tượng trống? (Với suy nghĩ này, chúng ta có nên đặt {sau =>không? Chúng ta có nên hạn chế chỉ sử dụng cú pháp biểu thức không? Điều đó sẽ làm giảm thêm tần suất của mũi tên.)
=>trông giống như <=và >=:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Để gọi một biểu thức chức năng mũi tên ngay lập tức, người ta phải đặt ()bên ngoài, nhưng đặt ()bên trong là hợp lệ và có thể có chủ ý.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Mặc dù, nếu một người viết (() => doSomething()());với ý định viết một biểu thức hàm được gọi ngay lập tức, đơn giản là sẽ không có gì xảy ra.
Thật khó để tranh luận rằng các chức năng mũi tên là "dễ hiểu hơn" với tất cả các trường hợp trên trong tâm trí. Người ta có thể tìm hiểu tất cả các quy tắc đặc biệt cần thiết để sử dụng cú pháp này. nó thật sự đáng giá thế sao?
Cú pháp của functionlà không khái quát. Để sử dụng functionriêng có nghĩa là ngôn ngữ tự ngăn người ta viết mã khó hiểu. Để viết các thủ tục cần được hiểu theo cú pháp trong mọi trường hợp, tôi chọn function.
Về hướng dẫn
Bạn yêu cầu một hướng dẫn cần phải "rõ ràng" và "nhất quán". Việc sử dụng các hàm mũi tên cuối cùng sẽ dẫn đến mã hợp lệ, không hợp lệ về mặt cú pháp, với cả hai dạng hàm được đan xen, có ý nghĩa và tùy ý. Vì vậy, tôi cung cấp như sau:
Hướng dẫn về ký hiệu chức năng trong ES6:
- Luôn tạo thủ tục với
function.
- Luôn luôn gán
thischo một biến. Không sử dụng () => {}.
Fixed this bound to scope at initialisationnhư một hạn chế?