ES6 ngay lập tức gọi hàm mũi tên


149

Tại sao điều này hoạt động trong Node.jsbảng điều khiển (được thử nghiệm trong 4.1.1 và 5.3.0) nhưng không hoạt động trong trình duyệt (được thử nghiệm trong Chrome)? Khối mã này sẽ tạo và gọi một hàm ẩn danh ghi nhật ký Ok.

() => {
  console.log('Ok');
}()

Ngoài ra, trong khi các công cụ trên hoạt động trong Node, thì điều này không hoạt động:

n => {
  console.log('Ok');
}()

Cũng không phải cái này:

(n) => {
  console.log('Ok');
}()

Điều kỳ lạ là khi tham số được thêm vào, nó thực sự ném một SyntaxErrorphần ngay lập tức.


8
Câu hỏi hay. Cả hai phiên bản được tham số hóa đều hoạt động với Babel
CodingIntrigue

2
Không quan tâm, (n => { console.log("Ok"); })();làm việc?
Mã hóa

(n => { console.log("Ok"); })()hoạt động ngay cả trong bảng điều khiển Chrome dev
XCS

và vì vậy, 3 năm sau, câu trả lời là? chắc chắn một trong 3 câu trả lời dưới đây nên được chấp nhận?!
joedotnot

@joedotnot Tôi không nhận được câu trả lời rõ ràng, chủ yếu đó là một triển khai kỳ lạ trong Node.js. Có vẻ như trong phiên bản mới nhất của Node.jsphiên bản đầu tiên không hoạt động nữa.
XCS

Câu trả lời:


194

Bạn cần biến nó thành biểu thức hàm thay vì định nghĩa hàm không cần tên và biến nó thành JavaScript hợp lệ.

(() => {
  console.log('Ok');
})()

Là tương đương với IIFE

(function(){
   console.log('Ok')
})();

Và lý do có thể khiến nó hoạt động trong Node.js nhưng không phải trong chrome là vì trình phân tích cú pháp của nó diễn giải nó như là một hàm tự thực thi, vì điều này

function() { console.log('hello'); }();

hoạt động tốt trong Node.jsĐây là một biểu thức chức năng và chrome và firefox và hầu hết các trình duyệt diễn giải nó theo cách này. Bạn cần phải gọi nó bằng tay.

Cách được chấp nhận rộng rãi nhất để nói cho trình phân tích cú pháp mong đợi một biểu thức hàm chỉ là bọc nó trong parens, bởi vì trong JavaScript, parens không thể chứa các câu lệnh. Tại thời điểm này, khi trình phân tích cú pháp gặp từ khóa hàm, nó biết phân tích nó như một biểu thức hàm chứ không phải là khai báo hàm.

Về phiên bản tham số , điều này sẽ hoạt động.

((n) => {
  console.log('Ok');
})()

4
Ví dụ đầu tiên hoạt động Node.jsvà thực sự ghi lại giá trị. Câu hỏi của tôi là tại sao nó hoạt động? Và tại sao nó không khi tôi thêm tham số?
XCS

1
Tôi khá quen thuộc với IIFEs và biết cách sửa mã của mình. Tôi chỉ tò mò tại sao, ví dụ, tôi IIFEkhông hoạt động khi ntham số được thêm vào, mặc dù nó hoạt động mà không có tham số.
XCS

3
Tôi đã không downvote, nhưng câu hỏi đặt ra là tại sao phiên bản được tham số hóa không hoạt động trong Node khi định nghĩa chính xác không có tham số - không hỏi sự khác biệt giữa việc triển khai các chức năng ẩn danh của Node / Chrome
CodingIntrigue

1
Đó là một điều cần biết nhưng nó không trả lời câu hỏi, như đã đề cập trước đây - tại sao phiên bản tham số hóa không hoạt động trong Node khi định nghĩa chính xác không có tham số
jkris

Nhưng tương đương với các function(){}()chức năng trong mũi tên là gì? Nếu tôi muốn các đối tượng chức năng tiếp xúc biểu thức chức năng bảo vệ chống lại điều đó.
dabadaba

18

Không ai trong số này nên làm việc mà không có dấu ngoặc đơn.

Tại sao?

Bởi vì theo thông số kỹ thuật:

  1. ArrowFunction được liệt kê dưới AssignmentExpression
  2. Các LHS của một CallExpression phải là một MemberExpression , SuperCall hoặc CallExpression

Vì vậy, một Mũi tên không thể có trong LHS của CallExpression .


Điều này có ý nghĩa gì trong cách =>giải thích, là nó hoạt động trên cùng một mức độ như các toán tử gán =, +=v.v.

Ý nghĩa

  • x => {foo}() không trở thành(x => {foo})()
  • Thông dịch viên cố gắng giải thích nó như là x => ({foo}())
  • Vì vậy, nó vẫn là một SyntaxError
  • Vì vậy, trình thông dịch quyết định rằng (phải có lỗi và ném SyntaxError

Có một lỗi trên Babel về nó ở đây , quá.


Đó là một số điểm hợp lệ, nhưng nếu tôi cố gắng thay thế phiên bản đầu tiên, đang hoạt động, với: () => ({console.log('Ok')}())nó không còn hoạt động. Vì vậy, nó không thực sự giải thích nó theo cách đó.
XCS

@Cristy Đây không phải là sản phẩm Chức năng Mũi tên hợp lệ. Nó nghĩ rằng bạn đang cố gắng tạo một Object với Object bằng chữ (kèm theo dấu ngoặc đơn) và console.log(...)không phải là một tên khóa hợp lệ.
thefourtheye

@Cristy: Vâng, tôi nghĩ rằng phần diễn giải của phần trên (bit "Ý nghĩa") có thể không hoàn toàn chính xác, nhưng các phần đặc tả là như tôi có thể nói. Nó cũng phù hợp với lỗi mà tôi nhận được từ V8: SyntaxError: Unexpected token ((chỉ vào phần (cuối ()ở phần cuối, không phải phần (trong console.log(...)).
TJ Crowder

@TJCrowder bạn nói đúng, tôi sẽ loại bỏ vì nó thay đổi thông báo lỗi và những gì tôi đang cố nói không được truyền đạt (nghĩa là (người phiên dịch đã bỏ cuộc sau khi cố gắng tìm cách giải thích hợp lệ và đi "Điều này phải sai rồi"), dù sao cũng có thể sai vì tôi không biết người phiên dịch thực sự được viết như thế nào
Paul S.

Tôi tự hỏi nếu nó không phải là một mã thông báo hợp lệ ở vị trí này, liệu nó có cố gắng chèn một dấu hai chấm không?
thefourtheye

2

Lý do bạn gặp vấn đề như thế này là do chính bàn điều khiển cố gắng mô phỏng phạm vi toàn cầu của bối cảnh bạn hiện đang nhắm mục tiêu. Nó cũng cố gắng nắm bắt các giá trị trả về từ các câu lệnh và biểu thức bạn viết trong bảng điều khiển, để hiển thị dưới dạng kết quả. Lấy ví dụ:

> 3 + 2
< 5

Ở đây, nó thực thi như thể nó là một biểu thức, nhưng bạn đã viết nó như thể đó là một tuyên bố. Trong các tập lệnh thông thường, giá trị sẽ bị loại bỏ, nhưng ở đây, mã phải được xử lý bên trong (như bao bọc toàn bộ câu lệnh với ngữ cảnh hàm và returncâu lệnh), gây ra tất cả các loại hiệu ứng kỳ lạ, bao gồm cả các vấn đề bạn gặp phải.

Đây cũng là một trong những lý do khiến một số mã ES6 trần trong các tập lệnh hoạt động tốt nhưng không có trong bảng điều khiển Chrome Dev Tools.

Hãy thử thực hiện điều này trong bảng điều khiển Node và Chrome:

{ let a = 3 }

Trong Node hoặc một <script>thẻ nó hoạt động tốt, nhưng trong giao diện điều khiển, nó mang lại Uncaught SyntaxError: Unexpected identifier. Nó cũng cung cấp cho bạn một liên kết đến nguồn dưới dạng VMxxx:1mà bạn có thể nhấp để kiểm tra nguồn được đánh giá, hiển thị dưới dạng:

({ let a = 3 })

Vậy tại sao nó làm điều này?

Câu trả lời là nó cần chuyển đổi mã của bạn thành một biểu thức để kết quả có thể được trả về cho người gọi và hiển thị trong bảng điều khiển. Bạn có thể làm điều này bằng cách gói câu lệnh trong ngoặc đơn, làm cho nó là một biểu thức, nhưng nó cũng làm cho khối ở trên cú pháp không chính xác (một biểu thức không thể có khai báo khối).

Bảng điều khiển đã cố gắng khắc phục các trường hợp cạnh này bằng cách thông minh về mã, nhưng điều đó vượt quá phạm vi của câu trả lời này, tôi nghĩ vậy. Bạn có thể gửi một lỗi để xem nếu đó là một cái gì đó họ sẽ xem xét sửa chữa.

Đây là một ví dụ tốt về một cái gì đó rất giống nhau:

https://stackoverflow.com/a/28431346/46588

Cách an toàn nhất để làm cho mã của bạn hoạt động là đảm bảo nó có thể được chạy như một biểu thức và kiểm tra SyntaxErrorliên kết nguồn để xem mã thực thi thực tế là gì và thiết kế ngược lại một giải pháp từ đó. Thông thường nó có nghĩa là một cặp dấu ngoặc được đặt chiến lược.

Nói ngắn gọn: bảng điều khiển cố gắng mô phỏng bối cảnh thực thi toàn cầu một cách chính xác nhất có thể, nhưng do những hạn chế của sự tương tác với công cụ v8 và ngữ nghĩa JavaScript, điều này đôi khi khó hoặc không thể giải quyết.


1
Đó là toàn bộ vấn đề, tôi quan tâm đến tham số, nhưng nó không hoạt động với bộ tham số.
XCS

OK, tôi thấy quan điểm của bạn. Sự khác biệt nằm ở cách bảng điều khiển Chrome Dev Tools thực sự thực thi mã của bạn. Tôi sẽ chỉnh sửa câu trả lời để phản ánh điều này.
Klemen Slavič

0

Tôi đã hỏi một câu hỏi như thế này:

@getify Tôi có câu hỏi này: để tạo ra một mẫu #IIFE, chúng tôi sử dụng parans xung quanh một khai báo hàm để chuyển đổi nó thành một biểu thức hàm và sau đó gọi nó. Bây giờ trong IIFE chức năng mũi tên, tại sao chúng ta cần parans?! Không phải là chức năng mũi tên đã là một biểu thức theo mặc định?!

và đây là câu trả lời của Kyle Simpson:

một hàm mũi tên một expr, nhưng chúng ta cần các parens b / c xung quanh "ưu tiên toán tử" (sorta), để các parens cuối cùng gọi mũi tên-IIFE áp dụng cho toàn bộ hàm và không chỉ là mã thông báo cuối cùng của cơ thể .

x => console.log(x)(4)

đấu với

(x => console.log(x))(4)

- getify (@getify) ngày 12 tháng 6 năm 2020


Câu hỏi của tôi là tại sao nó làm việc trên một số trình biên dịch mà không phải trên các trình biên dịch khác.
XCS

Đó là bởi vì các trình biên dịch khác nhau hoạt động khác nhau ở một số chi tiết, giống như các trình duyệt khác nhau, tất nhiên có các trình biên dịch khác nhau
Ershad Qaderi

Bạn nói đúng, họ hành xử khác nhau, nhưng thông số kỹ thuật JavaScript là giống nhau cho tất cả chúng. Tôi tò mò xem cái nào đúng, thông số kỹ thuật của JS nói gì về trường hợp này và đặc biệt là làm thế nào để nó hoạt động mà không cần tranh luận nhưng không tranh luận. Tôi đã tìm kiếm một phản ứng kỹ thuật hơn.
XCS

Ví dụ của bạn là khá rõ ràng, trong trường hợp đầu tiên nó thực sự nên gọi console.log(x)(4).
XCS

Tôi chỉ đoán ở đây nhưng tôi nghĩ rất hợp lý để giải thích nó như thế này: trong biểu thức hàm mũi tên khi chúng ta không sử dụng tham số, chúng ta phải sử dụng parens và điều đó cho thấy rõ đây là một mũi tên biểu thức chức năng, nhưng khi chúng ta có một tham số duy nhất, các parens là tùy ý, có thể không rõ ràng rằng đây là một chức năng và gây nhầm lẫn cho công cụ đó, để giải quyết sự nhầm lẫn, chúng ta phải đặt một cặp parens xung quanh toàn bộ biểu thức chức năng
Ershad Qaderi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.