Tìm kiếm trang web

javascript - Làm thế nào để tôi viết một hàm mũi tên có tên trong ES2015?


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

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

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

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 đã định nghĩa nó. Một cái gì đó như sau:

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

Có cách mới nào để làm điều này trong ES6 không?

Câu trả lời #1

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

Bạn làm 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 phép gán hoặc khởi tạo thuộc tính, nơi tên biến hoặc thuộc tính có thể được sử dụng hợp lý như một tên bởi trình biên dịch JavaScript. Không có cách nào khác để làm việc này, nhưng làm như vậy là đúng và hoàn toàn được quy định trong đặc tả. (Nó cũng hoạt động cho các biểu thức function ẩn danh truyền thống.)

Theo đặc tả, hàm này có tên thực là sayHello:

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

Hiện tại điều này được định nghĩa trong Toán tử gán > Ngữ nghĩa thời gian chạy: Đánh giá nơi nó thực hiện thao tác trừu tượng NamedEvalution (hiện tại là bước 1.c.i). (Bạn có thể xem mọi nơi điều này áp dụng bằng cách di chuột qua NamedEvalution trong phần đầu và nhấp vào "References".) (Trước đây, trước ES2019, Toán tử gán > Ngữ nghĩa thời gian chạy: Đánh giá sử dụng thao tác trừu tượng SetFunctionName, bước 1.e.iii, nhưng từ ES2019 trở đi sự trừu tượng trong đặc tả này đã được thay thế bằng NamedEvalution.)

Tương tự, PropertyDefinitionEvaluation sử dụng NamedEvaluation và do đó đặt cho hàm này một tên thật:

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

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.

Lưu ý: Để suy luận tên này xảy ra, biểu thức hàm phải được gán trực tiếp cho đối tượng mục tiêu. Ví dụ, điều này không suy luận được tên:

const sayHello = (void 0, (name) => {
    console.log(name + ' says hello');
});
console.log(sayHello.name); // ""

Đó là vì biểu thức hàm không được gán trực tiếp cho const, nó là một toán hạng cho một toán tử khác (trong trường hợp đó, là toán tử dấu phẩy, nhưng nó sẽ tương tự đối với [ví dụ] true && (name) => { }).

Ví dụ, trong Chrome, Edge (dựa trên Chromium, từ phiên bản 79 trở đi), hoặc Firefox, 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 phiên bản "Legacy" trở lên với một cờ thử nghiệm, hoặc Edge 79 trở đi phiên bản "Chromium"), khi bạn chạy điều đó, bạn sẽ thấy:

foo.name is: foo
Error
    at foo (http://stacksnippets.net/js:14:23)
    at http://stacksnippets.net/js:17:3

Lưu ý foo.name là: foo và Lỗi...tại foo.

Trên Chrome 50 và các phiên bản trước, Firefox 52 và các phiên bản trước, và Legacy Edge mà không bật cờ thí nghiệm, bạn sẽ thấy điều này thay vì vì chúng chưa có thuộc tính Function#name:

foo.name is: 
Error
    at foo (http://stacksnippets.net/js:14:23)
    at http://stacksnippets.net/js:17:3

Lưu ý rằng tên bị thiếu trong foo.name is:, nhưng nó có hiển thị trong ngăn xếp lỗi. Chỉ là việc thực sự triển khai name property trên hàm có mức ưu tiên thấp hơn so với một số tính năng ES2015 khác; Chrome và Firefox đã có; Edge có nó nhưng cần bật cờ, có lẽ nó sẽ không còn phải bật cờ lâu nữa.

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

Đúng. 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ó tương đương mũi tên 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 phải chia nó thành hai biểu thức (Tôi cho rằng bạn nên làm điều đó dù sao đi nữa):

const 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 điều này ở nơi cần một biểu thức duy nhất, bạn luôn có thể...sử dụng một hàm mũi tên:

console.log((() => {
    const 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 rằng điều đó đẹp, nhưng nó hoạt động nếu bạn thực sự, chắc chắn cần một bộ bao biểu thức đơn lẻ.


Ghi chú thêm: Nếu bạn không muốn một hàm lấy tên của nó từ định danh mà bạn đang gán thì sao? Nghĩa là, giả sử bạn không muốn example.name là "example" ở đây?

const example = () => {};
console.log(example.name); // "example"

Bạn có thể tránh nó bằng cách sử dụng bất kỳ biểu thức nào không sử dụng NamedEvaluation. Có lẽ cách phổ biến nhất để làm việc này là toán tử dấu phẩy:

const example = (0, () => {});
//              ^^^−−−−−−−−−^
console.log(example.name); // ""

Số 0 ở đó có thể là bất cứ gì bạn muốn, nó được đánh giá rồi sau đó bị loại bỏ, vì vậy 0 là một lựa chọn phổ biến. Việc truyền hàm qua toán tử dấu phẩy phá vỡ liên kết trực tiếp giữa phép gán và biểu thức hàm, ngăn NamedEvaluation cung cấp tên example cho hàm. (Điều này tương tự với các cách sử dụng nổi tiếng khác của toán tử dấu phẩy, như (0, object.example)() gọi object.example mà không làm cho object trở thành giá trị của this trong cuộc gọi, hoặc (0, eval)("code"), thực hiện một eval, nhưng không trong phạm vi hiện tại như bình thường.)

(Cảm ơn Sebastian Simon đã nêu ý này trong phần bình luận.)

Câu trả lời #2

Không. Cú pháp mũi tên là dạng viết tắt cho các hàm vô danh. Các hàm vô danh thì, đúng vậy, là vô danh.

Các hàm được đặt tên được định nghĩa với từ khóa function.

Câu trả lời #3

Nếu bằng 'named', bạn muốn thuộc tính .name của hàm mũi tên của bạn được đặt, bạn thật may mắn.

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

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

sayHello.name //=== 'sayHello'

Nói như vậy thì, câu hỏi của bạn dường như là 'liệu tôi có thể nhận một hàm mũi tên để nâng lên không?'. Câu trả lời cho câu đó là một 'không' thật lớn, tôi e rằng vậy.

Bài viết liên quan