ฉันควรวางฟังก์ชั่นที่ใช้ในฟังก์ชั่นอื่นเพียงหนึ่งฟังก์ชั่นภายในฟังก์ชั่นนั้นหรือไม่?


30

โดยเฉพาะฉันเขียนใน JavaScript

สมมติว่าฟังก์ชั่นหลักของฉันคือฟังก์ชั่น A ถ้าฟังก์ชั่น A ทำการโทรไปยังฟังก์ชั่น B หลายครั้ง แต่ฟังก์ชั่น B นั้นไม่ได้ใช้ที่อื่นฉันควรวางฟังก์ชั่น B ไว้ในฟังก์ชั่น A หรือไม่

เป็นการปฏิบัติที่ดีหรือไม่? หรือฉันควรจะวาง Function B ในขอบเขตเดียวกับ Function A หรือไม่

คำตอบ:


32

ฉันมักจะชอบฟังก์ชั่นที่ซ้อนกันโดยเฉพาะอย่างยิ่งใน JavaScript

  • ใน JavaScript วิธีเดียวที่จะ จำกัด การมองเห็นของฟังก์ชันคือการซ้อนภายในฟังก์ชันอื่น
  • ฟังก์ชั่นตัวช่วยเป็นรายละเอียดการใช้งานส่วนตัว การวางไว้ในขอบเขตเดียวกันนั้นคล้ายกับการทำให้ฟังก์ชั่นส่วนตัวของชั้นเรียนปรากฏต่อสาธารณะ
  • หากปรากฎว่าเป็นการใช้งานทั่วไปมากขึ้นมันเป็นเรื่องง่ายที่จะย้ายออกเพราะคุณสามารถมั่นใจได้ว่าผู้ช่วยนั้นใช้เพียงฟังก์ชั่นเดียวในปัจจุบันเท่านั้น กล่าวอีกนัยหนึ่งมันทำให้รหัสของคุณเหนียวแน่นยิ่งขึ้น
  • คุณสามารถกำจัดพารามิเตอร์ซึ่งทำให้ลายเซ็นฟังก์ชันและการใช้งานน้อยลง สิ่งนี้ไม่เหมือนกับการสร้างตัวแปรให้เป็นส่วนกลาง มันเหมือนกับการใช้สมาชิกคลาสในวิธีส่วนตัว
  • คุณสามารถให้ฟังก์ชันผู้ช่วยได้ดีขึ้นง่ายขึ้นโดยไม่ต้องกังวลเกี่ยวกับความขัดแย้ง
  • ฟังก์ชั่นตัวช่วยค้นหาได้ง่ายกว่า ใช่มีเครื่องมือที่สามารถช่วยคุณได้ ยังง่ายกว่าที่จะขยับดวงตาขึ้นมาเล็กน้อย
  • รหัสในรูปแบบของต้นไม้ที่เป็นนามธรรม: ฟังก์ชั่นทั่วไปบางอย่างที่รูท หากคุณใช้เอดิเตอร์ที่มีฟังก์ชันการพับ / ยุบการซ้อนจะสร้างลำดับชั้นของฟังก์ชันที่เกี่ยวข้องอย่างใกล้ชิดในระดับที่เป็นนามธรรม ทำให้ง่ายต่อการศึกษารหัสในระดับที่คุณต้องการและซ่อนรายละเอียด

ฉันคิดว่าฝ่ายค้านจำนวนมากมาจากความจริงที่ว่าโปรแกรมเมอร์ส่วนใหญ่ได้รับการเลี้ยงดูในแบบ C / C ++ / Java หรือได้รับการสอนจากคนอื่นที่เป็น ฟังก์ชั่นแบบซ้อนไม่ได้ดูเป็นธรรมชาติเพราะเราไม่ได้สัมผัสกับมันมากนักเมื่อเราเรียนรู้ที่จะเขียนโปรแกรม ไม่ได้หมายความว่ามันไม่มีประโยชน์


14

คุณควรจะอยู่ในขอบเขตทั่วโลกด้วยเหตุผลหลายประการ

  • การซ้อนฟังก์ชั่นตัวช่วยเข้ากับผู้โทรจะเพิ่มความยาวของผู้โทร ความยาวฟังก์ชั่นมักจะเป็นตัวบ่งชี้เชิงลบ ฟังก์ชั่นสั้นง่ายต่อการเข้าใจจดจำแก้จุดบกพร่องและบำรุงรักษา

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

  • มีผู้ช่วยทั่วโลกที่มีอยู่ช่วยให้ฟังก์ชั่นอื่น ๆ เรียกมันว่ามันจะเป็นประโยชน์โดยทั่วไปหลังจากทั้งหมด หากผู้ช่วยไม่พร้อมใช้งานคุณถูกล่อลวงให้ตัดและวางหรือลืมและนำกลับมาใช้ใหม่ไม่ดีหรือทำให้ฟังก์ชันอื่นยาวเกินกว่าที่จะเป็น

  • การซ้อนฟังก์ชันผู้ช่วยจะเพิ่มการล่อลวงให้ใช้ตัวแปรจากขอบเขตของผู้โทรโดยไม่มีการประกาศดังนั้นมันจะไม่ชัดเจนว่าอินพุตและเอาต์พุตของผู้ช่วยเหลือคืออะไร หากฟังก์ชั่นไม่ได้ระบุอย่างชัดเจนว่ามีการทำงานของข้อมูลใดและมีผลกระทบอย่างไรมันมักเป็นสัญญาณของความรับผิดชอบที่ไม่ชัดเจน การประกาศตัวช่วยทำงานแบบสแตนด์อโลนบังคับให้คุณรู้ว่ามันทำอะไรจริงๆ

แก้ไข

นั่นกลายเป็นคำถามที่ถกเถียงกันมากกว่าที่ฉันคิด เพื่อชี้แจง:

ใน JavaScript ฟังก์ชั่นการขยายไฟล์ขนาดใหญ่มักจะเติมเต็มบทบาทของคลาสเนื่องจากภาษาไม่ได้มีกลไกการ จำกัด ขอบเขตอื่น ๆ ฟังก์ชั่นผู้ช่วยแน่นอนควรเข้าไปในคลาสเสมือนดังกล่าวไม่ใช่ข้างนอกพวกเขา

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


ขอบคุณทุกจุดที่ดีมาก ในบันทึกด้านข้างฉันจะสั่งซื้อฟังก์ชั่นของฉันได้อย่างไร ตัวอย่างเช่นในขณะนี้ด้วยโปรแกรมขนาดเล็กฉันแค่สั่งให้เรียงตามตัวอักษร แต่ฉันควรจัดกลุ่มฟังก์ชันตัวช่วยและตัวเรียกเข้าด้วยกันไหม ฉันเดาว่าอย่างน้อยฉันควรแยกย่อยฟังก์ชั่นของฉันตามส่วนของแอพพลิเคชั่นที่พวกเขาใช้เช่น
แกรี่

ฉันไม่ได้ใส่ใจกับการกำหนดวิธีการสั่งซื้อเป็นเวลานาน หากคุณตั้งโปรแกรมด้วยความเป็นมืออาชีพในระดับใดคุณควรมีสภาพแวดล้อมที่ช่วยให้คุณข้ามไปยังคำจำกัดความใด ๆ ด้วยการกดแป้นบางครั้ง ดังนั้นวิธีการแบบสั้นจึงมีประโยชน์ แต่ไฟล์คลาสแบบสั้นหรือแม้กระทั่งที่จัดเรียงอย่างดีจะช่วยเพิ่มคุณค่าเพียงเล็กน้อย (แน่นอน JavaScript ใช้เพื่อให้คำจำกัดความทั้งหมดถูกวางไว้เหนือการใช้งานของพวกเขาหรือผลลัพธ์ที่ได้จะเป็นพฤติกรรมที่ไม่ได้กำหนดไว้ในทางเทคนิค - จำไม่ได้ว่าเป็นกรณีหรือไม่)
Kilian Foth

2
นี่คือคำตอบที่ดี ปัญหาของการห่อหุ้มที่ขาดนั้นเป็นสิ่งที่หลายคนไม่ควรพิจารณาเมื่อใช้ฟังก์ชันภายใน
sbichenko

2
Globals เป็นรหัสกลิ่น มันทำให้เกิดการเชื่อมต่อที่ไม่จำเป็นซึ่งป้องกันการ refactoring พวกเขาทำให้เกิดความขัดแย้งของชื่อพวกเขาเปิดเผยรายละเอียดการใช้งานและอื่น ๆ โดยเฉพาะอย่างยิ่งใน Javascript เนื่องจากตัวแปรทั้งหมดไม่แน่นอนรหัสปกติจะโหลดโดยตรงจากบุคคลที่สาม ระบบโมดูลที่มีให้ใช้อย่างกว้างขวางหรือแม้แต่การใช้ "ปล่อย" อย่างกว้างขวาง ในสภาพแวดล้อมที่เป็นศัตรูกลยุทธ์การป้องกันเพียงอย่างเดียวที่เรามีคือการห่อหุ้มภายในฟังก์ชั่น one-shot
Warbo

1
"การตอบสนองบทบาทของคลาส" พยายามเขียน JS ราวกับว่ามันเป็นอย่างอื่น "บทบาทของชั้นเรียน" คืออะไร? ประเภทการตรวจสอบ? Modularity? รวบรวมฉาก? มรดก? คำถามเกี่ยวข้องกับการกำหนดขอบเขตดังนั้นฉันถือว่าคุณหมายถึงกฎการกำหนดขอบเขตอย่างหยาบของคลาส นั่นเป็นเพียงการผ่านเจ้าชู้: วิธีการที่ชั้นเรียนควรจะกำหนดขอบเขต? PHP ทำให้เป็นสากลซึ่งไม่มีการห่อหุ้ม Python กำหนดขอบเขตคลาสด้วยคำศัพท์ แต่ถ้าคุณอยู่ในบล็อกที่มีการกำหนดขอบเขตแบบเล็ก ๆ ทำไมจึงรวมทุกอย่างไว้ในบล็อกคลาสที่มีขอบเขต JS ไม่มีความซ้ำซ้อนเช่น: ใช้ขอบเขตคำศัพท์มากเท่าที่คุณต้องการ
Warbo

8

ฉันจะเสนอเส้นทางที่สามเพื่อวางทั้งสองฟังก์ชั่นภายในการปิด มันจะมีลักษณะ:

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

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

โดยการตัดทั้งสองฟังก์ชั่นเหมือนเช่นนั้นfunctionBอยู่ในขณะนี้ภาคเอกชนอย่างสมบูรณ์และไม่สามารถเข้าถึงนอกของการปิด functionAแต่มองเห็นได้เฉพาะ มันไม่ได้เป็น cluttering namespace โลก, และfunctionAจะไม่ให้เกะกะนิยามของ


0

การกำหนดฟังก์ชัน B ภายในฟังก์ชัน A ให้ฟังก์ชัน B เข้าถึงตัวแปรภายในของ A

ดังนั้นฟังก์ชั่น B ที่กำหนดไว้ในฟังก์ชั่น A จะให้การแสดงผล (หรืออย่างน้อยก็น่าจะเป็นไปได้) ที่ B กำลังใช้งานหรือแก้ไขตัวแปรท้องถิ่นของ A ในขณะที่การกำหนด B ด้านนอกของ A ทำให้มันชัดเจนว่า B ไม่ได้

ดังนั้นเพื่อความชัดเจนของรหัสฉันจะกำหนด B ภายใน A เฉพาะเมื่อ B จำเป็นต้องเข้าถึงตัวแปรโลคอลของ A หาก B มีจุดประสงค์ที่ชัดเจนซึ่งเป็นอิสระจาก A ฉันจะนิยามนอก B แน่นอน


-1

คุณไม่ควรวางซ้อนเพราะมิฉะนั้นฟังก์ชันซ้อนจะถูกสร้างขึ้นใหม่ทุกครั้งที่คุณเรียกใช้ฟังก์ชันภายนอก


-3

เนื่องจากฟังก์ชั่น B ไม่ได้ใช้ที่อื่นคุณอาจทำเพียงฟังก์ชั่นด้านในของฟังก์ชั่น A เพราะนั่นเป็นเหตุผลเดียวที่ทำให้มันมีอยู่


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