Làm cách nào để viết một hàm mũi tên có tên trong ES2015?


204

Tôi có một chức năng mà tôi đang cố gắng chuyển đổi sang cú pháp mũi tên mới trong ES6 . Đây là một chức năng được đặt tên:

function sayHello(name) {
    console.log(name + ' says hello');
}

Có cách nào để đặt tên cho nó mà không có câu lệnh var:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

Rõ ràng, tôi chỉ có thể sử dụng chức năng này sau khi tôi đã xác định nó. Một cái gì đó như sau:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

Có một cách mới để làm điều này trong ES6 ?


3
Không phải là điểm của cú pháp mũi tên để không đặt tên cho hàm?
AstroCB

64
Không nhất thiết, các hàm mũi tên duy trì phạm vi từ vựng để có một tốc ký để tạo ra các hàm được đặt tên (hữu ích cho dấu vết ngăn xếp) với phạm vi từ vựng sẽ khá hữu ích
Matt Styles

6
Điều gì là sai với đoạn mã thứ hai?
Bergi

Bạn chắc chắn có thể tham chiếu một tên được gán bên trong phần thân của hàm: var sayHello = (name) => {console.log (sayHello); }; Và trong trường hợp các hàm đệ quy, bạn sẽ thường làm chính xác điều đó. Không phải là một ví dụ siêu hữu ích, nhưng đây là một hàm tự trả về nếu nó không nhận được đối số của nó: var sayHello = (name) => name? Console.log (name + 'say hello'): sayHello; sayHello () ('thẳng thắn'); // -> "thẳng thắn nói xin chào"
Dtipson

4
Tiêu đề của câu hỏi cực kỳ sai lệch so với nội dung của nó, bởi vì bạn đã loại trừ cách bạn đặt tên cho các hàm mũi tên (là đoạn trích thứ hai).
TJ Crowder

Câu trả lời:


211

Làm cách nào để viết một hàm mũi tên có tên trong ES2015?

Bạn thực hiện theo cách bạn đã loại trừ trong câu hỏi của mình: Bạn đặt nó ở phía bên phải của một công cụ khởi tạo chuyển nhượng hoặc thuộc tính trong đó tên biến hoặc thuộc tính có thể được sử dụng một cách hợp lý làm tên của công cụ JavaScript. Không có cách nào khác để làm điều đó, nhưng làm điều đó là chính xác và được bảo vệ đầy đủ bởi đặc điểm kỹ thuật.

Theo thông số kỹ thuật, chức năng này có tên thật , sayHello:

var sayHello = name => {
    console.log(name + ' says hello');
};

Điều này được định nghĩa trong Toán tử chuyển nhượng> Ngữ nghĩa thời gian chạy: Đánh giá trong đó nó gọi hoạt động trừu tượngSetFunctionName (cuộc gọi đó hiện đang ở bước 1.e.iii).

Tương tự, ngữ nghĩa thời gian chạy: Các cuộc gọi propertyDefDefEvalvalSetFunctionName và do đó cung cấp cho hàm này một tên thật:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

Các công cụ hiện đại đặt tên nội bộ của hàm cho các câu lệnh như vậy rồi; Edge vẫn có bit làm cho nó có sẵn như nametrên thể hiện chức năng đằng sau một cờ thời gian chạy.

Ví dụ: trong Chrome hoặc Firefox, hãy mở bảng điều khiển web và sau đó chạy đoạn mã này:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

Trên Chrome 51 trở lên và Firefox 53 trở lên (và Edge 13 trở lên với cờ thử nghiệm), khi bạn chạy nó, bạn sẽ thấy:

foo.name là: foo
lỗi
    tại foo (http://stacksnippets.net/js:14:23)
    tại http://stacksnippets.net/js:17:3

Lưu ý foo.name is: fooError...at foo.

Trên Chrome 50 trở về trước, Firefox 52 trở về trước và Edge không có cờ thử nghiệm, thay vào đó bạn sẽ thấy điều này vì họ chưa có Function#nametài sản (chưa):

tên foo là: 
lỗi
    tại foo (http://stacksnippets.net/js:14:23)
    tại http://stacksnippets.net/js:17:3

Lưu ý rằng tên bị thiếu foo.name is:, nhưng nó được hiển thị trong theo dõi ngăn xếp. Chỉ là thực sự triển khai thuộc name tính trên hàm có mức độ ưu tiên thấp hơn một số tính năng ES2015 khác; Chrome và Firefox có ngay bây giờ; Edge có nó phía sau một lá cờ, có lẽ nó sẽ không ở đằng sau lá cờ lâu hơn nhiều.

Rõ ràng, tôi chỉ có thể sử dụng chức năng này sau khi tôi đã xác định nó

Chính xác. Không có cú pháp khai báo hàm cho các hàm mũi tên, chỉ có cú pháp biểu thức hàm và không có mũi tên tương đương với tên trong biểu thức hàm có tên kiểu cũ ( var f = function foo() { };). Vì vậy, không có tương đương với:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

Bạn cần phải phá vỡ nó thành hai biểu thức (tôi muốn tranh luận, bạn nên làm điều đó nào ) :

let fact = n => {
    if (n < 0) {
      throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

Tất nhiên, nếu bạn phải đặt biểu thức này ở nơi cần một biểu thức, bạn luôn có thể ... sử dụng hàm mũi tên:

console.log((() => {
    let fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

Tôi không nói là nó đẹp, nhưng nó hoạt động nếu bạn hoàn toàn, tích cực cần một trình bao bọc biểu thức duy nhất.


Làm cách nào để ngăn tên được đặt (như trong các động cơ cũ)?
trusktr

1
@trusktr: Tôi không thể hiểu lý do tại sao bạn muốn, nhưng nếu bạn muốn ngăn chặn điều đó: Đừng làm những điều ở trên. Chẳng hạn, trong khi let f = () => { /* do something */ };gán tên cho hàm, let g = (() => () => { /* do something */ })();thì không.
TJ Crowder

Đó là những gì tôi đã làm. Tôi muốn các lớp ẩn danh được tạo bởi thư viện thừa kế lớp của tôi thành ẩn danh hơn là có tên của các biến nội bộ mà người dùng của thư viện thừa kế lớp của tôi không cần phải suy nghĩ và tôi muốn người dùng cuối nhìn thấy tên chỉ khi họ cung cấp một tên cho lớp. ( github.com/trusktr/low
class

4
@trusktr: Một ngoại lệ họ thực hiện có thể phù hợp với mục đích của bạn, sau đó: Nếu bạn gán một chức năng cho một thuộc tính trên một đối tượng hiện có , tên không được đặt: o.foo = () => {};không đặt tên cho hàm. Đây là cố ý để ngăn chặn rò rỉ thông tin.
TJ Crowder

86

Không. Cú pháp mũi tên là một dạng rút gọn cho các hàm ẩn danh. Chức năng ẩn danh là, tốt, ẩn danh.

Các chức năng được đặt tên được xác định với functiontừ khóa.


16
Như đã được tuyên bố bởi @ DenysSéguret, không phải là một dạng rút gọn cho các chức năng ẩn danh . Bạn sẽ có được một chức năng ràng buộc cứng . Bạn có thể xem kết quả được phiên mã tại đây bit.do/es2015-arrow để hiểu rõ hơn điều này ngụ ý gì ...
sminutoli 18/03/2016

16
Từ đầu tiên của câu trả lời này là chính xác cho câu hỏi được hỏi vì OP loại trừ cách bạn đặt tên cho hàm mũi tên. Nhưng phần còn lại của câu trả lời chỉ đơn giản là không chính xác. Nhìn vào đặc tả cho nơi hoạt động trừu tượngSetFunctionName được sử dụng. let a = () => {};định nghĩa một hàm được đặt tên (tên là a) cả theo thông số kỹ thuật và trong các động cơ hiện đại (đưa ra một lỗi trong đó để xem); họ chỉ chưa hỗ trợ nametài sản (nhưng đó là thông số kỹ thuật và cuối cùng họ sẽ đến đó).
TJ Crowder

4
@ DenysSéguret: Bạn có thể chỉ cần đặt tên cho chúng, đó là trong thông số kỹ thuật. Bạn làm theo cách mà OP loại trừ trong câu hỏi, điều này cung cấp cho hàm (không chỉ là biến) một tên thật.
TJ Crowder

5
@TJCrowder Cảm ơn bạn về thông tin đó và câu trả lời hữu ích của bạn . Tôi không biết tính năng này (rất rất kỳ lạ).
Denys Séguret

4
Câu trả lời này không đúng. Câu hỏi đã được trả lời chính xác bởi @TJCrowder bên dưới
Drenai

44

Nếu theo 'tên', bạn có nghĩa là bạn muốn đặt thuộc .nametính của hàm mũi tên của mình, bạn sẽ gặp may.

Nếu một chức năng mũi tên được xác định ở phía bên phải của biểu thức gán, động cơ sẽ lấy tên ở phía bên trái và sử dụng nó để đặt chức năng của mũi tên .name, ví dụ:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

sayHello.name //=== 'sayHello'

Như đã nói, câu hỏi của bạn dường như nhiều hơn 'tôi có thể lấy hàm mũi tên để nâng lên không?'. Câu trả lời cho câu hỏi đó là một câu "không" lớn, tôi sợ.


1
Trong phiên bản hiện tại của chrome, ít nhất, sayHello.name vẫn là ""; Lưu ý rằng giống như tất cả các hàm, sayHello là một đối tượng, vì vậy bạn có thể xác định các thuộc tính tùy ý cho nó nếu bạn muốn. Bạn không thể viết thành "tên" vì nó chỉ đọc, nhưng bạn có thể nóiHello.my_name = "sayHello"; Không chắc chắn những gì mang lại cho bạn mặc dù bạn không thể làm / tham chiếu rằng trong cơ thể của chức năng.
Dtipson

2
Tôi đã sai: trong khi tên không thể thay đổi trực tiếp, bạn có thể định cấu hình như thế này: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson 15/1/2016

1
"Nếu một chức năng mũi tên được xác định ở phía bên phải [..]" thì nguồn cho việc này là gì? Tôi không thể tìm thấy nó trong spec. Chỉ cần một tài liệu tham khảo để trích dẫn.
Gajus 4/2/2016

@Dtipson: Đó chỉ là do các động cơ hiện đại chưa triển khai cài đặt thuộc nametính ở mọi nơi mà thông số ES2015 yêu cầu chúng phải; họ sẽ nhận được nó. Đây là một trong những điều cuối cùng trong danh sách tuân thủ dành cho Chrome và ngay cả Chrome 50 cũng không có (cuối cùng Chrome 51 sẽ có). Chi tiết . Nhưng OP đặc biệt loại trừ làm điều này (kỳ lạ).
TJ Crowder

Tôi có thể lấy tên này trong toString không? Tôi đã cố gắng ghi đè nó, nhưng sau đó tôi không biết làm thế nào để truy cập vào cơ thể chức năng.
Qwerty

0

Dường như điều này sẽ khả thi với ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-fifts

Ví dụ được đưa ra là:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

Phần thân của các hàm mũi tên ES6 có chung từ vựng giống như mã bao quanh chúng, điều này mang lại cho chúng ta kết quả mong muốn do cách các trình khởi tạo thuộc tính ES7 được đặt trong phạm vi.

Lưu ý rằng để làm việc này với babel, tôi cần kích hoạt cú pháp ES7 giai đoạn 0 thử nghiệm nhất. Trong tệp webpack.config.js của tôi, tôi đã cập nhật trình tải babel như sau:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},

6
Đó không chính xác là một chức năng mũi tên được đặt tên. Đây là một lối tắt để tạo các phương thức cá thể trong hàm tạo và tôi tự hỏi làm thế nào nó có liên quan ở đây?
Bergi

Xin chào @Bergi, tôi không chắc đây có phải là ý định của tác giả ban đầu hay không, nhưng tôi đã cố gắng tạo một hàm có tên với cùng phạm vi này với đối tượng. Nếu chúng ta không sử dụng kỹ thuật này và chúng ta muốn có phạm vi phù hợp, chúng ta phải đưa nó vào trong hàm tạo của lớp. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie

8
Uh, bạn có thể dễ dàng sử dụng constructor() { this.handleOptionsButtonClick = (e) => {…}; }hoàn toàn tương đương với mã trong câu trả lời của bạn nhưng đã hoạt động trong ES6. Không cần sử dụng .bind.
Bergi

1
Btw, tôi nghĩ rằng bạn đang nhầm lẫn giữa "hàm được đặt tên" với "phương thức" (ví dụ) "và" phạm vi "với" thisbối cảnh "
Bergi

1
@tdecs: Vâng, bạn có thể; Jörg bị nhầm. let a = () => {};định nghĩa một tên chức năng mũi tên gọi a. Chi tiết .
TJ Crowder

0

Trên thực tế, có vẻ như một cách để đặt tên cho các hàm mũi tên (ít nhất là bằng chrome 77 ...) là để làm điều này:

"use strict";
let fn_names = {};
fn_names.foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

nhập mô tả hình ảnh ở đây


Về mặt lý thuyết có thể tạo một macro babel fn('yourName', ()=>{}), có thể duy trì ngữ nghĩa hàm mũi tên chính xác 100% hoặc tốt hơn là một plugin babel cho "cú pháp hàm mũi tên có tên" : fn yourName() => {}. Một trong số này sẽ biên dịch theo mã ở trên. Tôi nghĩ rằng mũi tên được đặt tên sẽ là BADA55
Devin G Rhode

-1

để viết hàm mũi tên có tên, bạn có thể tham gia ví dụ dưới đây, trong đó tôi có một lớp có tên là lớp Đăng nhập và bên trong lớp này tôi đã viết một hàm có tên mũi tên , tên là successAuth lớp LoginClass {

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}

1
Tốt hơn hết là thêm một lời giải thích cho mã bạn đang đăng dưới dạng câu trả lời, để nó giúp khách truy cập hiểu tại sao đây là một câu trả lời hay.
abarisone

Đây là điều mà OP nói rằng anh ấy không muốn làm, và dựa vào đề xuất này chỉ có ở Giai đoạn 2 và thậm chí có thể sẽ không tạo ra ES2017 (nhưng tôi nghĩ có khả năng sẽ tạo ES2018 và các bộ chuyển đổi đã hỗ trợ nó trong nhiều tháng). Nó cũng có hiệu quả sao chép một số câu trả lời trước đó, không hữu ích.
TJ Crowder

-2

Đây là ES6

Vâng tôi nghĩ những gì bạn đang theo đuổi là như thế này:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"

Tôi đã thử ví dụ này, nó hoạt động tốt. Bất kỳ lý do tốt cho việc bỏ phiếu tiêu cực? Việc sử dụng này tạo ra bất kỳ tác dụng phụ không mong muốn hay tôi thiếu bất kỳ điểm quan trọng? Sự giúp đỡ của bạn trong việc làm rõ nghi ngờ của tôi sẽ được đánh giá cao.
FullMoon

@GaneshAdapa: Tôi hy vọng các downvote là bởi vì điều này gợi ý làm chính xác những gì OP nói rằng anh ấy / cô ấy không muốn làm, mà không cung cấp thêm thông tin nào.
TJ Crowder

-2

Bạn có thể bỏ qua phần chức năng và phần mũi tên để tạo chức năng. Thí dụ:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
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.