ฟังก์ชันที่ส่งคืนฟังก์ชัน


110

ฉันติดอยู่กับแนวคิดเรื่อง 'ฟังก์ชันที่ส่งคืนฟังก์ชัน' ฉันหมายถึงหนังสือ 'Object Oriented Javascript' โดย Stoyan Stefanov

Snippet One:

    function a() {
      
        alert('A!');
    
        function b(){
            alert('B!'); 
        }
    
        return b();
    }
    
    var s = a();
    alert('break');
    s();

เอาท์พุต:

A!
B!
break

ตัวอย่างข้อมูล 2

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b;
}

var s = a();
alert('break');
s();
เอาท์พุต:

A!
break
B!

ใครช่วยบอกความแตกต่างระหว่างการกลับรถbกับb()ในตัวอย่างข้างต้นได้ไหม


2
คุณจะสังเกตเห็นตัวอย่างแรกให้ข้อผิดพลาดบน s ();
weirdalsuperfan

คำตอบ:


122

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

การสาธิต

function a() {
    alert('A');
}
//alerts 'A', returns undefined

function b() {
    alert('B');
    return a;
}
//alerts 'B', returns function a

function c() {
    alert('C');
    return a();
}
//alerts 'C', alerts 'A', returns undefined

alert("Function 'a' returns " + a());
alert("Function 'b' returns " + b());
alert("Function 'c' returns " + c());

ในตัวอย่างของคุณคุณกำลังกำหนดฟังก์ชันภายในฟังก์ชัน เช่น:

function d() {
    function e() {
        alert('E');
    }
    return e;
}
d()();
//alerts 'E'

ฟังก์ชันยังคงเรียกใช้ได้ มันยังคงมีอยู่ สิ่งนี้ถูกใช้ใน JavaScript ตลอดเวลา ฟังก์ชั่นที่สามารถผ่านรอบเพียงเช่นค่าอื่น ๆ พิจารณาสิ่งต่อไปนี้:

function counter() {
    var count = 0;
    return function() {
        alert(count++);
    }
}
var count = counter();
count();
count();
count();

การนับฟังก์ชันสามารถเก็บตัวแปรที่กำหนดไว้ภายนอกได้ นี่เรียกว่าการปิด นอกจากนี้ยังใช้กันมากใน JavaScript


Snippet One: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: hero.sayName} hero.nayName (); Snippet 2: var hero = {name: 'Rafaelo', sayName: function () {return hero.name; }, nayName: this.sayName} hero.nayName (); ตัวอย่างแรกให้ผลลัพธ์ที่ถูกต้องแก่ฉันในขณะที่ส่วนที่สองไม่มี ทำไม? ความนับถือ.
Cafecorridor

thisหมายถึงบางสิ่งภายในร่างกายที่ทำงานเท่านั้นมิฉะนั้นจะเป็นแบบโกลบอล สิ่งที่คุณกำลังพูดถึงthis.sayNameคือคุณต้องการให้ตัวแปรโกลบอลsayNameซึ่งไม่มีอยู่มันไม่ได้กำหนดดังนั้นจึงไม่สามารถเรียกใช้ได้
kzh

8
ฉันสับสนกับ d () (); ในตอนแรก แต่จากนั้นก็ตระหนักว่า () เรียก d และครั้งที่สอง () เรียกค่าส่งคืนของ d ซึ่งก็คือ e
skud

1
แปดปีต่อมาและสิ่งนี้ยังคงเกี่ยวข้อง!
Brad Vanderbush

45

กลับชื่อฟังก์ชั่นโดยไม่ต้องผลตอบแทนอ้างอิงถึงฟังก์ชั่นที่สามารถกำหนดเป็นคุณทำกับ() ในขณะนี้มีการอ้างอิงถึงฟังก์ชั่นและโทรเป็นหน้าที่เทียบเท่าโทรvar s = a()sb()s()b()

// Return a reference to the function b().
// In your example, the reference is assigned to var s
return b;

การเรียกใช้ฟังก์ชันด้วย()ในคำสั่ง return จะเรียกใช้ฟังก์ชันและส่งคืนค่าที่ฟังก์ชันส่งกลับ มันคล้ายกับการโทรvar x = b();แต่แทนที่จะของการกำหนดค่าตอบแทนของคุณจะกลับมาได้จากฟังก์ชั่นการโทรb() a()ถ้าฟังก์ชันb()นั้นไม่ส่งคืนค่าการโทรจะส่งกลับundefinedหลังจากที่ทำงานอื่นเสร็จb()แล้ว

// Execute function b() and return its value
return b();
// If b() has no return value, this is equivalent to calling b(), followed by
// return undefined;

1
จากคำตอบทั้งหมดฉันชอบคำตอบของคุณมากกว่าเนื่องจากความเรียบง่าย
anar khalilov

ใส่ดี! คำตอบนี้เข้าใจง่ายมาก
AndrewSteinheiser

ขอขอบคุณสำหรับการตรวจสอบความถูกต้องของบิตเกี่ยวกับกรณีที่ฟังก์ชันไม่ส่งคืนค่าการโทรกลับไม่ได้กำหนด ฉันค้นพบสิ่งนี้เมื่อไม่นานมานี้: แม้ว่าฟังก์ชันจะส่งคืนค่าว่างอย่างชัดเจน แต่ถ้าคุณกำหนดค่าที่ส่งกลับไปยังตัวแปรก็จะไม่ได้กำหนดไม่ใช่ null ฉันคิดว่าสิ่งนี้ทำให้เกิดปัญหาแปลก ๆ มากมายในฐานรหัสบางตัวเนื่องจากค่าว่างและไม่ได้กำหนดนั้นไม่เทียบเท่ากับ ===
Benjamin

37

return b(); เรียกใช้ฟังก์ชัน b () และส่งกลับผลลัพธ์

return b; ส่งคืนการอ้างอิงไปยังฟังก์ชัน b ซึ่งคุณสามารถเก็บไว้ในตัวแปรเพื่อเรียกใช้ในภายหลัง


17

การส่งbคืนกำลังส่งคืนวัตถุฟังก์ชัน ใน Javascript ฟังก์ชันเป็นเพียงวัตถุเช่นเดียวกับวัตถุอื่น ๆ หากคุณพบว่าไม่มีประโยชน์ให้แทนที่คำว่า "object" ด้วย "thing" คุณสามารถส่งคืนวัตถุใด ๆ จากฟังก์ชัน คุณสามารถส่งคืนค่าจริง / เท็จ จำนวนเต็ม (1,2,3,4 ... ) คุณสามารถส่งคืนสตริง คุณสามารถส่งคืนอ็อบเจ็กต์ที่ซับซ้อนที่มีคุณสมบัติหลายอย่าง และคุณสามารถส่งคืนฟังก์ชัน ฟังก์ชันเป็นเพียงสิ่งหนึ่ง

ในกรณีของคุณการส่งbคืนสิ่งที่ส่งคืนสิ่งนั้นคือฟังก์ชันที่เรียกได้ การb()ส่งคืนจะส่งคืนค่าที่ส่งคืนโดยฟังก์ชันที่เรียกได้

พิจารณารหัสนี้:

function b() {
   return 42;
}

โดยใช้คำจำกัดความข้างต้นreturn b();ส่งคืนค่า 42 ในทางreturn b;กลับกันส่งคืนฟังก์ชันซึ่งจะคืนค่าเป็น 42 ซึ่งเป็นสองสิ่งที่แตกต่างกัน


4
มันควรจะกลับมา42;)
c69

4

เมื่อคุณกลับมาbมันเป็นเพียงการอ้างอิงถึงฟังก์ชัน b แต่จะไม่ถูกเรียกใช้งานในขณะนี้

เมื่อคุณกลับมาb()คุณกำลังเรียกใช้ฟังก์ชันและส่งคืนค่า

ลองalertไอเอ็นจีtypeof(s)ในตัวอย่างของคุณ Snippet b จะให้ 'function' ตัวอย่างข้อมูลจะให้อะไรแก่คุณ?


อันแรกให้ "ไม่ได้กำหนด" หมายความว่าการ return b () ไร้ประโยชน์โดยสิ้นเชิงหรือไม่? นอกจากนี้ในตัวอย่างที่สองฟังก์ชัน b จะเป็นแบบส่วนตัว เราจะเข้าถึงข้อมูลอ้างอิงนอกฟังก์ชันได้อย่างไร? โปรดให้ลิงก์ที่อธิบายแนวคิดนี้อย่างชัดเจนหากเป็นไปได้ ขอบคุณ!
Cafecorridor

ฉันได้คำตอบสำหรับคนแรก ส่งคืน 1 + 2 ในฟังก์ชัน b () และ typeof จะแสดงตัวเลข ขอบคุณ.
Cafecorridor

ดีใจที่คุณคิดออก! สำหรับฟังก์ชั่นส่วนตัว: มันไม่ส่วนตัวจริงๆในตัวอย่างที่สองเนื่องจากคุณส่งคืนไปแล้ว sในความเป็นจริงจะได้รับการมอบหมายให้ ลองreturn thisแทนดูสิreturn b…คุณจะทำได้s.b();)
vzwick

ฉันจะลองแน่นอน ยังไม่ถึงแนวคิดนี้ใน Javascript อาจจะในสองสามวัน ขอบคุณ! :)
Cafecorridor

ฟังก์ชั่น () {alert ("A!"); ฟังก์ชัน b () {alert ("B!"); } กลับ b; } var s = a (); ลบ; s (); ---- จบ ---- แนวคิดการอ้างอิงใน Javascript เหมือนกับใน Java หรือไม่? ที่นี่ฉันได้ลบฟังก์ชั่น a () และยังมีการเรียกใช้ s () เรียกใช้ b () ดังนั้นฉันสามารถพูดได้ว่า s มีสำเนาของ b และไม่ได้ชี้ไปที่ b () ที่กำหนดไว้ใน a ()?
Cafecorridor

2

ลองนึกภาพฟังก์ชันเป็นชนิดเช่น int คุณสามารถคืนค่า ints ในฟังก์ชัน คุณสามารถส่งคืนฟังก์ชันได้เช่นกันซึ่งเป็นวัตถุประเภท "ฟังก์ชัน"

ตอนนี้ปัญหาทางไวยากรณ์: เนื่องจากฟังก์ชันส่งคืนค่าคุณจะส่งคืนฟังก์ชันได้อย่างไรและไม่คืนค่า

โดยละเว้นวงเล็บ! เนื่องจากไม่มีวงเล็บฟังก์ชันจะไม่ทำงาน! ดังนั้น:

return b;

จะส่งคืน "ฟังก์ชัน" (ลองนึกภาพว่าคุณกำลังส่งคืนตัวเลข) ในขณะที่:

return b();

ก่อนอื่นให้เรียกใช้ฟังก์ชันจากนั้นส่งคืนค่าที่ได้รับจากการเรียกใช้งานมันเป็นความแตกต่างที่ยิ่งใหญ่!


ในส่วนย่อยที่สองฟังก์ชัน b เป็นแบบส่วนตัว เราจะเข้าถึงข้อมูลอ้างอิงนอกฟังก์ชันได้อย่างไร? มีกฎเกณฑ์ที่ใช้บังคับเหมือนกันหรือไม่?
Cafecorridor

วัตถุใน JavaScript (ซึ่งรวมถึงฟังก์ชันต่างๆ) จะไม่เป็นส่วนตัวอีกต่อไปหากคุณแชร์
kzh

@Cafecorridor: หากฟังก์ชันส่วนตัวถูกส่งกลับโดยบางสิ่ง (ฟังก์ชันสาธารณะ) หรือถูกกำหนดให้กับตัวแปรสาธารณะ (ซึ่งเป็นตัวแปรที่สามารถเข้าถึงได้ของแอปพลิเคชัน) คุณสามารถทำการตัวแปรของคุณได้อย่างง่ายดาย(); มิฉะนั้นกำหนดฟังก์ชันที่ส่งกลับไปยังตัวแปรและทำอีกครั้งyourvariable ();
Francesco Belladonna

2

สร้างตัวแปร :

var thing1 = undefined;

ประกาศฟังก์ชัน :

function something1 () {
    return "Hi there, I'm number 1!";
}

แจ้งเตือนค่าของthing1(ตัวแปรแรกของเรา):

alert(thing1); // Outputs: "undefined".

ตอนนี้ถ้าเราต้องการที่thing1จะเป็นการอ้างอิงไปยังฟังก์ชันsomething1ซึ่งหมายความว่ามันจะเป็นสิ่งเดียวกับฟังก์ชั่นที่สร้างขึ้นของเราเราจะทำ:

thing1 = something1;

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

thing1 = something1(); // Value of thing1: "Hi there, I'm number 1!" 

-1

ตัวอย่างข้อมูลที่หนึ่ง:

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b(); //return nothing here as b not defined a return value
}

var s = a(); //s got nothing assigned as b() and thus a() return nothing.
alert('break');
s(); // s equals nothing so nothing will be executed, JavaScript interpreter will complain

คำสั่ง 'b ()' หมายถึงเรียกใช้ฟังก์ชันชื่อ 'b' ซึ่งแสดงกล่องโต้ตอบที่มีข้อความ 'B!'

คำสั่ง 'return b ();' หมายถึงเรียกใช้ฟังก์ชันชื่อ 'b' แล้วส่งคืนฟังก์ชัน 'b' ที่ส่งกลับมา แต่ 'b' ไม่คืนค่าอะไรเลยดังนั้นคำสั่ง 'return b ()' นี้จะไม่คืนค่าอะไรเลย ถ้า b () ส่งคืนตัวเลขดังนั้น 'return b ()' จะเป็นตัวเลขด้วย

ตอนนี้ 's' ถูกกำหนดค่าของสิ่งที่ 'a ()' return ซึ่งส่งกลับ 'b ()' ซึ่งไม่มีอะไรดังนั้น 's' จึงไม่มีอะไรเลย (ใน JavaScript มันเป็นสิ่งที่จริงแล้วมันเป็น 'ไม่ได้กำหนด' ดังนั้น เมื่อคุณขอให้ JavaScript แปลความหมายของข้อมูลว่า 's' คืออะไรล่าม JavaScript จะบอกคุณว่า 's' เป็นสิ่งที่ไม่ได้กำหนดไว้เมื่อคุณขอให้ JavaScript ดำเนินการคำสั่งนี้ 's ()' คุณกำลังขอให้ JavaScript เรียกใช้ฟังก์ชันที่มีชื่อว่า 's' แต่ 's' ในที่นี้คือ 'ไม่ได้กำหนด' ไม่ใช่ฟังก์ชันดังนั้น JavaScript จะบ่นว่า "เดี๋ยวก่อนไม่ใช่ฟังก์ชันฉันไม่รู้ว่า จะทำอย่างไรกับสิ่งนี้ "จากนั้นข้อความแสดงข้อผิดพลาด" Uncaught TypeError: s is not a function "จะแสดงโดย JavaScript (ทดสอบใน Firefox และ Chrome)


ตัวอย่างข้อมูล 2

function a() {
  
    alert('A!');

    function b(){
        alert('B!'); 
    }

    return b; //return pointer to function b here
}

var s = a();  //s get the value of pointer to b
alert('break');
s(); // b() function is executed

ตอนนี้ฟังก์ชัน 'a' ส่งคืนตัวชี้ / นามแฝงไปยังฟังก์ชันชื่อ 'b' ดังนั้นเมื่อรัน 's = a ()', 's' จะได้รับค่าที่ชี้ไปที่ b นั่นคือ 's' เป็นนามแฝงของ 'b' ตอนนี้การเรียก 's' เท่ากับเรียก 'b' เช่น 's' เป็นฟังก์ชันในขณะนี้ Execute 's ()' หมายถึงการเรียกใช้ฟังก์ชัน 'b' (เหมือนกับการเรียกใช้งาน 'b ()') กล่องโต้ตอบที่แสดง 'B!' จะปรากฏขึ้น (เช่นเรียกใช้คำสั่ง 'alert (' B! '); ในฟังก์ชัน' b ')


-2

นี่เป็นประโยชน์อย่างยิ่งในชีวิตจริง

การทำงานกับ Express.js

expressเส้นทางปกติของคุณมีลักษณะดังนี้:

function itWorksHandler( req, res, next ) {
  res.send("It works!");
}

router.get("/check/works", itWorksHandler );

แต่ถ้าคุณต้องการเพิ่ม wrapper, error handler หรือ smth ล่ะ?

จากนั้นคุณเรียกใช้ฟังก์ชันของคุณจากกระดาษห่อหุ้ม

function loggingWrapper( req, res, next, yourFunction ) {
  try {
    yourFunction( req, res );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", function( req, res, next ) {
  loggingWrapper( req, res, next, itWorksHandler );
});

ดูซับซ้อน? แล้วสิ่งนี้:

function function loggingWrapper( yourFunction ) => ( req, res, next ) {
  try {
    yourFunction( req, res, next );
  } catch ( err ) {
    console.error( err );
    next( err );
  }
}

router.get("/check/works", loggingWrapper( itWorksHandler ) );

ดูในตอนท้ายคุณกำลังส่งฟังก์ชันที่loggingWrapperมีอาร์กิวเมนต์หนึ่งเป็นฟังก์ชันอื่นitWorksHandlerและloggingWrapperส่งกลับฟังก์ชันใหม่ที่ใช้req, res, nextเป็นอาร์กิวเมนต์

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