Bagaimana cara menulis fungsi panah bernama di ES2015?

204

Saya memiliki fungsi yang saya coba konversi ke sintaks panah baru di ES6 . Ini adalah fungsi bernama:

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

Apakah ada cara untuk memberinya nama tanpa pernyataan var:

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

Jelas, saya hanya bisa menggunakan fungsi ini setelah saya mendefinisikannya. Sesuatu seperti berikut:

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

Apakah ada cara baru untuk melakukan ini di ES6 ?

jhamm
sumber
3
Bukankah sintaks panah titik untuk tidak memberikan fungsi nama?
AstroCB
64
Tidak harus, fungsi panah mempertahankan lingkup leksikal sehingga memiliki singkatan untuk menghasilkan fungsi bernama (berguna untuk jejak stack) dengan lingkup leksikal akan sangat berguna
Matt Styles
6
Apa yang salah dengan cuplikan kedua?
Bergi
Anda pasti dapat merujuk nama yang ditugaskan di dalam tubuh fungsi: var sayHello = (name) => {console.log (sayHello); }; Dan dalam hal fungsi rekursif, Anda akan sering melakukan hal itu. Bukan contoh yang sangat berguna, tapi inilah fungsi yang mengembalikan dirinya sendiri jika tidak mendapatkan argumennya: var sayHello = (name) => name? Console.log (nama + 'bilang halo'): sayHello; sayHello () ('frank'); // -> "frank
say
4
Judul pertanyaan ini sangat menyesatkan dibandingkan dengan kontennya, karena Anda telah mengesampingkan cara Anda memberi nama fungsi panah (yang merupakan cuplikan kedua).
TJ Crowder

Jawaban:

211

Bagaimana cara menulis fungsi panah bernama di ES2015?

Anda melakukannya dengan cara Anda mengesampingkan dalam pertanyaan Anda: Anda meletakkannya di sisi kanan tugas atau penginisialisasi properti di mana variabel atau nama properti dapat digunakan sebagai nama oleh mesin JavaScript. Tidak ada yang lain cara untuk melakukannya, tetapi melakukan itu benar dan sepenuhnya dicakup oleh spesifikasi.

Per spec, fungsi ini memiliki nama yang benar, sayHello:

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

Ini didefinisikan dalam Penugasan Operator> Runtime Semantics: Evaluasi di mana ia menyebut operasi abstrakSetFunctionName (panggilan itu saat ini dalam langkah 1.e.iii).

Serupa dengan itu, Runtime Semantics: PropertyDefinitionEvaluation memanggil SetFunctionNamedan dengan demikian memberikan fungsi ini nama sebenarnya:

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

Mesin modern menetapkan nama internal fungsi untuk pernyataan seperti itu sudah; Edge masih memiliki bit yang membuatnya tersedia namepada instance fungsi di belakang flag runtime.

Misalnya, di Chrome atau Firefox, buka konsol web lalu jalankan cuplikan ini:

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

Di Chrome 51 dan di atasnya dan Firefox 53 dan di atasnya (dan Edge 13 dan di atasnya dengan bendera eksperimental), saat Anda menjalankannya, Anda akan melihat:

foo.name adalah: foo
Kesalahan
    di foo (http://stacksnippets.net/js:14:23)
    di http://stacksnippets.net/js:17:3

Perhatikan foo.name is: foodan Error...at foo.

Di Chrome 50 dan sebelumnya, Firefox 52 dan sebelumnya, dan Edge tanpa bendera eksperimental, Anda akan melihat ini karena mereka tidak memiliki Function#nameproperti (belum):

foo.name adalah: 
Kesalahan
    di foo (http://stacksnippets.net/js:14:23)
    di http://stacksnippets.net/js:17:3

Perhatikan bahwa nama ini hilang dari foo.name is:, tetapi yang ditampilkan dalam jejak stack. Hanya saja sebenarnya mengimplementasikan name properti pada fungsi itu prioritas lebih rendah daripada beberapa fitur ES2015 lainnya; Chrome dan Firefox memilikinya sekarang; Edge memilikinya di belakang bendera, mungkin itu tidak akan berada di belakang bendera lebih lama.

Jelas, saya hanya bisa menggunakan fungsi ini setelah saya mendefinisikannya

Benar. Tidak ada sintaks deklarasi fungsi untuk fungsi panah, hanya sintaks ekspresi fungsi , dan tidak ada panah yang setara dengan nama dalam ekspresi fungsi bernama gaya lama ( var f = function foo() { };). Jadi tidak ada yang setara dengan:

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

Anda harus memecahnya menjadi dua ekspresi (saya berpendapat Anda harus tetap melakukannya ) :

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));

Tentu saja, jika Anda harus meletakkan ini di mana satu ekspresi diperlukan, Anda selalu dapat ... menggunakan fungsi panah:

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

Saya tidak mengatakan itu cantik, tetapi bekerja jika Anda benar-benar membutuhkan pembungkus ekspresi tunggal.

TJ Crowder
sumber
Bagaimana cara mencegah agar nama tidak ditetapkan (seperti pada mesin yang lebih lama)?
trusktr
1
@trusktr: Saya tidak bisa melihat mengapa Anda ingin, tetapi jika Anda ingin mencegahnya: Jangan lakukan hal-hal di atas. Misalnya, saat let f = () => { /* do something */ };memberikan nama ke fungsi, let g = (() => () => { /* do something */ })();tidak.
TJ Crowder
Itulah yang akhirnya saya lakukan. Saya lebih suka memiliki kelas anonim yang dihasilkan oleh perpustakaan warisan kelas saya menjadi anonim daripada memiliki nama variabel internal yang tidak perlu dipikirkan oleh pengguna perpustakaan warisan kelas saya, dan saya ingin pengguna akhir melihat nama hanya jika mereka memberikan nama untuk kelas tersebut. ( github.com/trusktr/lowclass )
trusktr
4
@trusktr: Satu-satunya pengecualian yang mereka buat mungkin sesuai dengan tujuan Anda, kemudian: Jika Anda menetapkan fungsi ke properti pada objek yang ada , namanya tidak disetel: o.foo = () => {};tidak memberikan fungsi pada nama. Ini disengaja untuk mencegah kebocoran informasi.
TJ Crowder
86

Tidak. Sintaks panah adalah bentuk pendek untuk fungsi anonim. Fungsi anonim, yah, anonim.

Fungsi-fungsi yang disebutkan didefinisikan dengan functionkata kunci.

Jörg W Mittag
sumber
16
Seperti yang dinyatakan oleh @ DenysSéguret, bukan bentuk pendek untuk fungsi anonim . Anda akan mendapatkan fungsi yang diikat dengan keras . Anda dapat melihat hasil yang ditranskripsikan di sini bit.do/es2015-arrow untuk memahami dengan lebih baik apa implikasinya ...
sminutoli
16
Kata pertama dari jawaban ini benar untuk pertanyaan yang diajukan karena OP mengesampingkan cara Anda memberi nama fungsi panah. Tetapi sisa jawabannya tidak benar. Lihat spesifikasi di mana operasi abstrakSetFunctionName digunakan. let a = () => {};mendefinisikan fungsi bernama (namanya a) baik menurut spesifikasi dan di mesin modern (melemparkan kesalahan untuk melihatnya); mereka hanya belum mendukung nameproperti (tetapi masih dalam spesifikasi dan mereka akan sampai di sana pada akhirnya).
TJ Crowder
4
@ DenysSéguret: Anda dapat hanya nama mereka, itu di spec. Anda melakukannya dengan cara OP mengesampingkan dalam pertanyaan, yang memberikan fungsi (bukan hanya variabel) nama sebenarnya.
TJ Crowder
5
@TJCrowder Terima kasih atas sedikit informasi itu, dan atas jawaban Anda yang bermanfaat . Saya tidak tahu fitur ini (sangat sangat aneh).
Denys Séguret
4
Jawaban ini tidak benar. Pertanyaan itu dijawab dengan benar oleh @TJCrowder di bawah ini
Drenai
44

Jika dengan 'bernama', berarti Anda ingin .nameproperti fungsi panah Anda disetel, Anda beruntung.

Jika fungsi panah didefinisikan di sisi kanan ekspresi penugasan, mesin akan mengambil nama di sisi kiri dan menggunakannya untuk mengatur fungsi panah .name, misalnya

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

sayHello.name //=== 'sayHello'

Karena itu, pertanyaan Anda tampaknya lebih 'dapatkah saya mendapatkan fungsi panah untuk diangkat?'. Jawabannya adalah jawaban "tidak", saya rasa.

hughfdjackson
sumber
1
Di versi chrome saat ini, setidaknya, sayHello.name masih ""; Perhatikan bahwa seperti semua fungsi, sayHello adalah objek, sehingga Anda dapat menentukan properti sewenang-wenang jika ingin. Anda tidak dapat menulis ke "nama" karena hanya-baca, tetapi Anda bisa mengatakanHello.my_name = "sayHello"; Tidak yakin apa yang membuat Anda, karena Anda tidak dapat melakukan / referensi yang ada di dalam tubuh fungsi.
Dtips
2
Saya salah: sementara nama tidak dapat secara langsung berubah, Anda dapat mengonfigurasinya seperti ini: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson
1
"Jika fungsi panah didefinisikan pada sisi kanan [..]" apa sumber untuk ini? Saya tidak dapat menemukannya di spec. Hanya perlu referensi kutipan.
Gajus
@Dtipson: Itu hanya karena mesin modern belum menerapkan pengaturan nameproperti di mana pun spesifikasi ES2015 mengharuskannya; mereka akan sampai ke sana. Ini adalah salah satu hal terakhir dalam daftar kepatuhan untuk Chrome dan bahkan Chrome 50 tidak memilikinya (Chrome 51 akhirnya akan memilikinya). Detail . Tetapi OP secara khusus mengesampingkan melakukan hal ini (aneh).
TJ Crowder
Bisakah saya mendapatkan nama ini di toString? Saya mencoba menimpanya, tetapi kemudian saya tidak tahu bagaimana mengakses fungsi tubuh.
Qwerty
0

Tampaknya ini akan dimungkinkan dengan ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

Contoh yang diberikan adalah:

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

Tubuh fungsi panah ES6 berbagi leksikal yang sama ini dengan kode yang mengelilinginya, yang memberi kita hasil yang diinginkan karena cara penginisialisasi properti ES7 dicakup.

Perhatikan bahwa untuk mendapatkan ini bekerja dengan babel saya perlu mengaktifkan sintaks ES7 tahap 0 paling eksperimental. Dalam file webpack.config.js saya, saya memperbarui babel loader seperti:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},
Hamish Currie
sumber
6
Itu bukan fungsi panah bernama. Ini adalah jalan pintas untuk membuat metode instan di konstruktor, dan saya ingin tahu bagaimana ini relevan di sini?
Bergi
Hai @Bergi, saya tidak yakin apakah ini maksud penulis asli, tapi saya mencoba membuat fungsi bernama dengan lingkup yang sama dengan objek. Jika kita tidak menggunakan teknik ini, dan kita ingin memiliki ruang lingkup yang sesuai, kita harus memasukkannya ke dalam konstruktor kelas. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie
8
Eh, Anda bisa dengan mudah menggunakan constructor() { this.handleOptionsButtonClick = (e) => {…}; }yang benar-benar setara dengan kode dalam jawaban Anda tetapi sudah berfungsi di ES6. Tidak perlu digunakan .bind.
Bergi
1
Btw, saya pikir Anda membingungkan "fungsi bernama" dengan "(contoh) metode" dan "ruang lingkup" dengan " thiskonteks"
Bergi
1
@tdecs: Ya, Anda bisa; Jorg keliru. let a = () => {};mendefinisikan fungsi panah bernama dipanggil a. Detail .
TJ Crowder
0

Sebenarnya sepertinya salah satu cara untuk memberi nama fungsi panah (setidaknya pada chrome 77 ...) adalah dengan melakukan ini:

"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);
}

masukkan deskripsi gambar di sini

Devin G Rhode
sumber
secara teoritis bisa membuat makro babel fn('yourName', ()=>{}), yang bisa mempertahankan 100% benar panah semantik fungsi, atau lebih baik lagi plugin babel untuk "bernama sintaks panah fungsi": fn yourName() => {}. Salah satu dari ini akan dikompilasi ke kode di atas. Saya pikir panah bernama akan jadi BADA55
Devin G Rhode
-1

untuk menulis fungsi panah bernama Anda dapat menggunakan contoh di bawah ini, di mana saya memiliki kelas bernama LoginClass dan di dalam kelas ini saya menulis sebuah panah bernama fungsi , bernama successAuth kelas LoginClass {

    constructor() {

    }

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

    }

}
BERGUIGA Mohamed Amine
sumber
1
Itu selalu lebih baik untuk menambahkan penjelasan ke kode yang Anda posting sebagai jawaban, sehingga akan membantu pengunjung memahami mengapa ini adalah jawaban yang baik.
abarisone
Ini melakukan apa yang OP katakan tidak ingin dia lakukan, dan bergantung pada proposal ini yang hanya pada Tahap 2 dan bahkan mungkin tidak akan membuat ES2017 (tapi saya pikir itu kemungkinan membuat ES2018, dan transponder telah mendukungnya) selama berbulan-bulan). Ini juga secara efektif menggandakan beberapa jawaban sebelumnya, yang tidak berguna.
TJ Crowder
-2

INI ES6

Ya saya pikir apa yang Anda kejar adalah sesuatu seperti ini:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"
Venkatesh Namburi
sumber
Saya mencoba contoh ini, itu berfungsi dengan baik. Adakah alasan bagus untuk memberikan suara negatif? Apakah penggunaan ini menimbulkan efek samping yang tidak diinginkan atau apakah saya kehilangan poin penting? Bantuan Anda dalam menjelaskan keraguan saya akan dihargai.
FullMoon
@ GaneshAdapa: Saya berharap downvotes adalah karena ini menyarankan melakukan persis apa yang OP katakan dia tidak ingin lakukan, tanpa memberikan informasi lebih lanjut.
TJ Crowder
-2

Anda bisa melewati bagian fungsi dan bagian panah untuk membuat fungsi. Contoh:

 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();
scre_www
sumber