ฉันจะเขียนฟังก์ชัน arrow ที่มีชื่อใน ES2015 ได้อย่างไร


204

ฉันมีฟังก์ชั่นที่ฉันพยายามที่จะแปลงไวยากรณ์ลูกศรใหม่ในES6 มันเป็นฟังก์ชั่นที่มีชื่อ:

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

มีวิธีให้ชื่อโดยไม่มีคำสั่ง var:

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

เห็นได้ชัดว่าฉันสามารถใช้ฟังก์ชั่นนี้หลังจากที่ฉันได้กำหนดไว้เท่านั้น สิ่งต่อไปนี้:

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

มีวิธีการใหม่ในES6หรือไม่?


3
ไวยากรณ์ของลูกศรไม่ได้ให้ชื่อฟังก์ชันใช่หรือไม่
AstroCB

64
ไม่จำเป็นว่าฟังก์ชั่นลูกศรรักษาขอบเขตคำศัพท์เพื่อให้มีชวเลขเพื่อสร้างฟังก์ชั่นที่ตั้งชื่อ (มีประโยชน์สำหรับการติดตามสแต็ค) ที่มีขอบเขตคำศัพท์จะค่อนข้างมีประโยชน์
Matt Styles

6
เกิดอะไรขึ้นกับตัวอย่างที่สอง
Bergi

แน่นอนที่สุดคุณสามารถอ้างอิงชื่อที่กำหนดไว้ในเนื้อหาของฟังก์ชัน: var sayHello = (name) => {console.log (sayHello); }; และในกรณีของฟังก์ชั่นวนซ้ำคุณมักจะทำอย่างนั้น ไม่ใช่ตัวอย่างที่มีประโยชน์มาก แต่นี่คือฟังก์ชันที่คืนค่าตัวเองหากไม่ได้รับอาร์กิวเมนต์: var sayHello = (name) => name? console.log (ชื่อ + 'say hello'): sayHello; sayHello () ( 'ตรงไปตรงมา'); // -> "frank says hello"
Dtipson

4
ชื่อของคำถามนั้นทำให้เข้าใจผิดอย่างมหาศาลเมื่อเทียบกับเนื้อหาเนื่องจากคุณได้จัดการวิธีที่คุณตั้งชื่อฟังก์ชั่นลูกศร (ซึ่งเป็นตัวอย่างที่สอง)
TJ Crowder

คำตอบ:


211

ฉันจะเขียนฟังก์ชัน arrow ที่มีชื่อใน ES2015 ได้อย่างไร

คุณทำสิ่งที่คุณตัดออกในคำถามของคุณ: คุณวางไว้ที่ด้านขวามือของการกำหนดค่าเริ่มต้นหรือคุณสมบัติเริ่มต้นที่ตัวแปรหรือชื่อคุณสมบัติสามารถใช้เป็นชื่อโดยเอ็นจิน JavaScript ไม่มีวิธีอื่นที่จะทำ แต่การทำนั้นถูกต้องและครอบคลุมโดยข้อกำหนด

ตามข้อมูลจำเพาะฟังก์ชั่นนี้มีชื่อจริงsayHello:

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

สิ่งนี้ถูกกำหนดไว้ในตัวดำเนินการที่ได้รับมอบหมาย> ความหมายของรันไทม์: การประเมินผลที่เรียกการSetFunctionNameดำเนินการเชิงนามธรรม (การเรียกนั้นอยู่ในขั้นตอนที่ 1.e.iii

เสมือนความหมายรันไทม์: การเรียกPropertyDefinitionEvaluationSetFunctionNameและทำให้ชื่อนี้เป็นฟังก์ชันจริง:

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

เอ็นจิ้นสมัยใหม่ตั้งชื่อภายในของฟังก์ชั่นสำหรับข้อความแบบนั้น; Edge ยังมีบิตที่ทำให้ใช้งานได้nameบนอินสแตนซ์ของฟังก์ชั่นด้านหลังธงรันไทม์

ตัวอย่างเช่นใน Chrome หรือ Firefox เปิดเว็บคอนโซลแล้วเรียกใช้ตัวอย่างนี้:

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

ใน Chrome 51 ขึ้นไปและ Firefox 53 ขึ้นไป (และ Edge 13 ขึ้นไปด้วยการตั้งค่าสถานะทดลอง) เมื่อคุณเรียกใช้คุณจะเห็น:

foo.name คือ: foo
ความผิดพลาด
    ที่ foo (http://stacksnippets.net/js:14:23)
    ที่ http://stacksnippets.net/js:17:3

หมายเหตุและfoo.name is: fooError...at foo

ใน Chrome 50 และรุ่นก่อนหน้า, Firefox 52 และรุ่นก่อนหน้าและ Edge โดยไม่มีการตั้งค่าสถานะการทดลองคุณจะเห็นสิ่งนี้แทนเนื่องจากไม่มีFunction#nameคุณสมบัติ (ยัง):

foo.name คือ: 
ความผิดพลาด
    ที่ foo (http://stacksnippets.net/js:14:23)
    ที่ http://stacksnippets.net/js:17:3

โปรดทราบว่าชื่อหายไปfoo.name is:แต่จะแสดงในการติดตามสแต็ก เป็นเพียงว่าการใช้name คุณสมบัติในฟังก์ชั่นนั้นมีความสำคัญต่ำกว่าคุณสมบัติ ES2015 อื่น ๆ Chrome และ Firefox มีตอนนี้ Edge มีด้านหลังธงโดยสันนิษฐานว่าจะไม่อยู่ด้านหลังธงนานนัก

เห็นได้ชัดว่าฉันสามารถใช้ฟังก์ชั่นนี้หลังจากที่ฉันได้กำหนดไว้เท่านั้น

แก้ไข. ไม่มีไวยากรณ์การประกาศฟังก์ชันสำหรับฟังก์ชันลูกศรเฉพาะไวยากรณ์นิพจน์ฟังก์ชันเท่านั้นและไม่มีลูกศรเทียบเท่ากับชื่อในรูปแบบเก่าที่ชื่อ function expression ( var f = function foo() { };) ดังนั้นจึงไม่เทียบเท่ากับ:

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

คุณจะต้องทำลายมันเป็นสองการแสดงออก(ฉันเถียงคุณควรจะทำอย่างนั้นอยู่แล้ว ) :

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

แน่นอนถ้าคุณต้องวางสิ่งนี้ในที่ที่ต้องการนิพจน์เดียวคุณสามารถ ... ใช้ฟังก์ชั่นลูกศร:

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

ฉันไม่ได้บอกว่ามันสวย แต่ก็ใช้ได้ดีถ้าคุณต้องการบวกกระดาษห่อนิพจน์


ฉันจะป้องกันชื่อจากการตั้งค่า (เช่นในเครื่องมือรุ่นเก่า) ได้อย่างไร
trusktr

1
@trusktr: ฉันไม่เห็นสาเหตุที่คุณต้องการ แต่ถ้าคุณต้องการที่จะป้องกันมัน: อย่าทำสิ่งต่าง ๆ ข้างต้น ตัวอย่างเช่นในขณะที่let f = () => { /* do something */ };กำหนดชื่อให้กับฟังก์ชั่นlet g = (() => () => { /* do something */ })();ไม่ได้
TJ Crowder

นั่นคือสิ่งที่ฉันทำ ฉันต้องการมีคลาสแบบไม่ระบุชื่อที่สร้างโดยไลบรารีการสืบทอดคลาสของฉันเป็นแบบไม่ระบุชื่อมากกว่ามีชื่อของตัวแปรภายในที่ผู้ใช้ของไลบรารีการสืบทอดคลาสของฉันไม่จำเป็นต้องคิดถึงและฉันต้องการให้ผู้ใช้เห็นชื่อ เฉพาะในกรณีที่พวกเขาให้ชื่อสำหรับชั้นเรียน ( github.com/trusktr/lowclass )
trusktr

4
@trusktr: ข้อยกเว้นประการหนึ่งที่พวกเขาทำอาจเหมาะสมกับวัตถุประสงค์ของคุณแล้ว: หากคุณกำหนดฟังก์ชั่นให้กับคุณสมบัติบนวัตถุที่มีอยู่ชื่อจะไม่ถูกตั้งค่า: o.foo = () => {};ไม่ได้ให้ชื่อฟังก์ชั่น นี่คือเจตนาเพื่อป้องกันการรั่วไหลของข้อมูล
TJ Crowder

86

ไม่ไวยากรณ์ของลูกศรเป็นรูปแบบย่อสำหรับฟังก์ชันที่ไม่ระบุชื่อ ฟังก์ชั่นที่ไม่ระบุชื่อเป็นอย่างดีไม่ระบุชื่อ

ฟังก์ชั่นที่มีชื่อจะถูกกำหนดด้วยfunctionคำหลัก


16
ตามที่ได้ระบุไว้โดย @ DenysSéguretไม่ได้เป็นแบบย่อสำหรับฟังก์ชั่นที่ไม่ระบุชื่อ คุณจะได้ฟังก์ชั่นที่ผูกมัดอย่างหนัก คุณสามารถดูผลลัพธ์transpiledที่นี่bit.do/es2015- แคบเพื่อให้เข้าใจได้ดีขึ้นว่ามันหมายถึงอะไร ...
sminutoli

16
คำแรกของคำตอบนี้ถูกต้องสำหรับคำถามที่ถามเพราะ OP จะพิจารณาวิธีที่คุณตั้งชื่อฟังก์ชั่นลูกศร แต่คำตอบที่เหลือนั้นไม่ถูกต้อง ดูข้อมูลจำเพาะสำหรับการใช้งานนามธรรม SetFunctionNamelet a = () => {};กำหนดฟังก์ชั่นที่มีชื่อ (ชื่อคือa) ทั้งตามข้อกำหนดและในเครื่องยนต์ที่ทันสมัย ​​(โยนข้อผิดพลาดในการดู); พวกเขายังไม่สนับสนุนnameคุณสมบัติ (แต่อยู่ในข้อมูลจำเพาะและพวกเขาจะไปที่นั่นในที่สุด)
TJ Crowder

4
@ DenysSéguret: คุณสามารถตั้งชื่อพวกมันได้ในสเป็ค คุณทำตามวิธีที่ OP ใช้ในคำถามซึ่งให้ฟังก์ชัน (ไม่ใช่แค่ตัวแปร) ชื่อจริง
TJ Crowder

5
ขอบคุณ @TJCrowder สำหรับบิตของข้อมูลนั้นและสำหรับคำตอบที่มีประโยชน์ ฉันไม่รู้คุณสมบัตินี้ (แปลกมาก)
Denys Séguret

4
คำตอบนี้ไม่ถูกต้อง ตอบคำถามอย่างถูกต้องโดย @TJCrowder ด้านล่าง
Drenai

44

ถ้าโดย 'named' คุณหมายถึงคุณต้องการให้.nameคุณสมบัติของฟังก์ชั่นลูกศรของคุณตั้งค่าคุณกำลังโชคดี

หากฟังก์ชั่นลูกศรถูกกำหนดที่ด้านขวามือของการแสดงออกการมอบหมายเครื่องยนต์จะใช้ชื่อทางด้านซ้ายมือและใช้เพื่อตั้งค่าฟังก์ชั่นลูกศร.nameเช่น

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

sayHello.name //=== 'sayHello'

ต้องบอกว่าคำถามของคุณดูเหมือนจะมากกว่า 'ฉันจะใช้ฟังก์ชันลูกศรเพื่อยก' คำตอบสำหรับคนนั้นคือเฒ่าใหญ่ '' ไม่ 'ฉันกลัว


1
ใน Chrome รุ่นปัจจุบันอย่างน้อย sayHello.name ยังคงเป็น ""; โปรดทราบว่าเช่นเดียวกับทุกฟังก์ชั่น sayHello เป็นวัตถุดังนั้นคุณสามารถกำหนดคุณสมบัติโดยพลการได้ถ้าคุณต้องการ คุณไม่สามารถเขียนถึง "ชื่อ" ได้เนื่องจากเป็นแบบอ่านอย่างเดียว แต่คุณสามารถพูดได้ว่าสวัสดี "ชื่อ" "สวัสดี" ไม่แน่ใจว่าสิ่งที่ทำให้คุณได้รับเนื่องจากคุณไม่สามารถทำ / อ้างอิงที่ภายในร่างกายของฟังก์ชั่น
Dtipson

2
ฉันผิด: ในขณะที่ชื่อไม่สามารถเปลี่ยนแปลงได้โดยตรงคุณสามารถกำหนดค่าเช่นนี้: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson

1
"ถ้าฟังก์ชั่นลูกศรถูกกำหนดที่ด้านขวามือ [.. ]" แหล่งที่มาของสิ่งนี้คืออะไร? ฉันหามันไม่เจอในสเป็ค เพียงแค่ต้องมีการอ้างอิงเพื่ออ้าง
Gajus

@Dipsips: นั่นเป็นเพียงเพราะเครื่องยนต์ที่ทันสมัยยังไม่ได้ใช้งานการตั้งค่าnameคุณสมบัติทุกที่ที่ ES2015 สเป็คต้องการ; พวกเขาจะไปถึงมัน เป็นหนึ่งในสิ่งสุดท้ายในรายการการปฏิบัติตามข้อกำหนดสำหรับ Chrome และแม้แต่ Chrome 50 ก็ไม่มีอยู่ (ในที่สุด Chrome 51 จะ) รายละเอียด . แต่ OP ได้ตัดการทำเช่นนี้เป็นพิเศษ (พิลึก)
TJ Crowder

ฉันสามารถใช้ชื่อนี้ใน toString ได้ไหม ฉันพยายามเอาชนะมันแล้ว แต่ฉันไม่รู้วิธีเข้าถึงฟังก์ชั่นของตัวเครื่อง
Qwerty

0

ดูเหมือนว่าสิ่งนี้จะเป็นไปได้ด้วย ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

ตัวอย่างที่ให้มาคือ:

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

ฟังก์ชันลูกศรของ ES6 ใช้คำศัพท์เดียวกันกับรหัสที่ล้อมรอบซึ่งทำให้เราได้ผลลัพธ์ตามที่ต้องการเนื่องจากวิธีการกำหนดคุณสมบัติเริ่มต้นของ ES7

โปรดทราบว่าเพื่อให้การทำงานกับ babel ฉันต้องเปิดใช้งานไวยากรณ์ ES7 stage 0 ที่มีการทดลองมากที่สุด ในไฟล์ webpack.config.js ฉันได้อัปเดตตัวโหลด babel ดังนี้:

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

6
นั่นไม่ใช่ฟังก์ชั่นลูกศรที่ชื่อว่า นี่เป็นทางลัดสำหรับการสร้างวิธีการอินสแตนซ์ใน Constructor และฉันสงสัยว่ามันเกี่ยวข้องกันอย่างไร
Bergi

สวัสดี @Bergi ฉันไม่แน่ใจว่านี่เป็นความตั้งใจของผู้เขียนต้นฉบับหรือไม่ แต่ฉันพยายามสร้างฟังก์ชันที่มีชื่อซึ่งมีขอบเขตเดียวกันกับวัตถุ ถ้าเราไม่ใช้เทคนิคนี้และเราต้องการให้มีขอบเขตที่เหมาะสมเราจะต้องใช้มันในตัวสร้างคลาส this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie

8
เอ่อคุณสามารถใช้งานได้ง่ายconstructor() { this.handleOptionsButtonClick = (e) => {…}; }ซึ่งเทียบเท่ากับรหัสทั้งหมดในคำตอบของคุณ แต่ทำงานได้ใน ES6 .bindไม่จำเป็นต้องใช้
Bergi

1
Btw ฉันคิดว่าคุณกำลังสับสน "ฟังก์ชั่นที่มีชื่อ" กับ "วิธีการ (ตัวอย่าง)" และ "ขอบเขต" กับ " thisบริบท"
Bergi

1
@tdecs: ใช่คุณสามารถ; Jörgเข้าใจผิด let a = () => {};กำหนดชื่อaฟังก์ชั่นที่เรียกว่าลูกศร รายละเอียด .
TJ Crowder

0

ที่จริงแล้วดูเหมือนว่าจะมีวิธีหนึ่งในการตั้งชื่อฟังก์ชั่นลูกศร (อย่างน้อยที่สุดของ chrome 77 ... ) คือการทำสิ่งนี้:

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

ป้อนคำอธิบายรูปภาพที่นี่


ในทางทฤษฎีสามารถสร้างแมโครบาเบลfn('yourName', ()=>{})ซึ่งสามารถรักษา 100% ลูกศรที่ถูกต้องความหมายฟังก์ชั่นหรือดีกว่ายังปลั๊กอิน Babel สำหรับ fn yourName() => {}"ไวยากรณ์ฟังก์ชั่นการตั้งชื่อลูกศร": อย่างใดอย่างหนึ่งเหล่านี้จะรวบรวมลงในรหัสด้านบน ฉันคิดว่าลูกศรชื่อน่าจะเป็น BADA55
Devin G Rhode

-1

เพื่อเขียนฟังก์ชั่น arrow ชื่อคุณสามารถเพื่อนตัวอย่างร้องซึ่งฉันมีชั้นชื่อ LoginClass และภายในชั้นนี้ฉันเขียนลูกศรชื่อฟังก์ชั่นชื่อsuccessAuth ชั้น LoginClass {

    constructor() {

    }

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

    }

}

1
การเพิ่มคำอธิบายลงในรหัสที่คุณโพสต์เป็นคำตอบจะดีกว่าเสมอเพื่อให้ผู้เข้าชมเข้าใจว่าเหตุใดจึงเป็นคำตอบที่ดี
abarisone

สิ่งนี้ทำในสิ่งที่ OP บอกว่าเขาไม่ต้องการทำและอาศัยข้อเสนอนี้ซึ่งอยู่ที่ด่าน 2 เท่านั้นและอาจไม่ใช่ ES2017 (แต่ฉันคิดว่ามันน่าจะเป็น ES2018 และ transpilers สนับสนุน หลายเดือน). นอกจากนี้ยังทำซ้ำคำตอบก่อนหน้านี้หลายรายการได้อย่างมีประสิทธิภาพซึ่งไม่มีประโยชน์
TJ Crowder

-2

นี่คือ ES6

ใช่ฉันคิดว่าสิ่งที่คุณตามมาคืออะไรเช่นนี้:

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

ฉันลองตัวอย่างนี้ใช้งานได้ดี มีเหตุผลที่ดีสำหรับการให้คะแนนลบ? การใช้งานนี้สร้างผลข้างเคียงที่ไม่ต้องการหรือฉันพลาดจุดสำคัญใด ๆ หรือไม่? ความช่วยเหลือของคุณในการชี้แจงข้อสงสัยของฉันจะได้รับการชื่นชม
FullMoon

@GaneshAdapa: ฉันคาดหวังว่า downvotes นั้นเป็นเพราะมันแนะนำให้ทำในสิ่งที่ OP บอกว่าเขา / เธอไม่ต้องการทำโดยไม่ให้ข้อมูลใด ๆ เพิ่มเติม
TJ Crowder

-2

คุณสามารถข้ามส่วนของฟังก์ชั่นและส่วนของลูกศรเพื่อสร้างฟังก์ชั่น ตัวอย่าง:

 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();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.