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ừ this
vự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ừ
this
vự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 this
bê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 this
cho một biến, sử dụng bind
hoặc sử dụng đối số thứ 3 có sẵn trên các Array
phươ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 this
giá 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 this
giá trị cũng có thể gây nhầm lẫn, bởi vì thật khó để biết this
tá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 this
giá 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 this
luôn có thể bị ràng buộc động) và luôn tham chiếu this
qua một biến. Các biến là từ vựng và giả sử nhiều tên. Việc gán this
cho 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 this
cho một biến (ngay cả khi có một this
chứ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 this
là 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
EventTarget
vớ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 this
rà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 if
câ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 this
như 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 return
sẽ 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 function
là không khái quát. Để sử dụng function
riê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
this
cho một biến. Không sử dụng () => {}
.
Fixed this bound to scope at initialisation
như một hạn chế?