มันคืออะไร
ใน JavaScript แต่ละฟังก์ชั่นเมื่อเรียกว่าสร้างบริบทการดำเนินการใหม่ เนื่องจากตัวแปรและฟังก์ชั่นที่กำหนดไว้ในฟังก์ชั่นเป็นตัวแปรเดียวที่เข้าถึงภายในไม่ได้จากภายนอกเมื่อเรียกฟังก์ชั่นบริบทที่จัดทำโดยฟังก์ชั่นให้วิธีที่ง่ายมากในการสร้างตัวแปรส่วนตัว
ฟังก์ชั่น makeCounter () {var i = 0; return function () {console.log (++ i); - } // โปรดจำไว้ว่า: `counter` และ` counter2` มีตัวแปรของตัวเอง `i`var counter = makeCounter (); counter (); // 1Counter (); // 2var counter2 = makecounter (); counter2 (); // 1counter2 (); // 2i;ในหลายกรณีคุณอาจไม่จำเป็นต้องใช้ฟังก์ชั่นเช่นการทำสิ่งที่จะส่งคืนค่าสะสมหลายค่าและคุณสามารถเรียกได้เพียงครั้งเดียวเพื่อรับค่าเดียวและในบางกรณีคุณไม่จำเป็นต้องรู้ค่าคืนอย่างชัดเจน
แกนกลางของมัน
ตอนนี้ไม่ว่าคุณจะกำหนดฟังก์ชั่นเช่นฟังก์ชัน foo () {} หรือ var foo = function () {} เมื่อโทรคุณต้องเพิ่มคู่ของวงเล็บหลังจากนั้นเช่น foo ()
// ฟังก์ชั่นที่กำหนดไว้ด้านล่างสามารถเรียกได้โดยการเพิ่มคู่ของวงเล็บหลังจากชื่อฟังก์ชั่นเช่น `foo ()`, // เพราะ foo เป็นเพียงตัวแปรอ้างอิง var foo = function () {/ * รหัส */} `สัมพันธ์กับฟังก์ชั่นฟังก์ชัน`) วงเล็บหลังจากนั้น? function () { / * code * /} (); // syntaxerror: โทเค็นที่ไม่คาดคิด (อย่างที่คุณเห็นแมลงถูกจับที่นี่ เมื่อวงเล็บปรากฏอยู่เบื้องหลังฟังก์ชั่นเพื่อเรียกฟังก์ชั่นไม่ว่าคุณจะพบคำหลักของฟังก์ชั่นในสภาพแวดล้อมทั่วโลกหรือสภาพแวดล้อมในท้องถิ่นโดยค่าเริ่มต้นมันจะถือว่าเป็นการประกาศฟังก์ชั่นมากกว่าการแสดงออกของฟังก์ชั่น หากคุณไม่ได้บอกวงเล็บอย่างชัดเจนว่ามันเป็นนิพจน์มันจะถือว่าเป็นฟังก์ชั่นที่ไม่มีชื่อและโยนข้อผิดพลาดเนื่องจากการประกาศฟังก์ชั่นต้องใช้ชื่อ
คำถามที่ 1: ฉันนึกถึงคำถามที่นี่ได้ไหม เราสามารถเรียกฟังก์ชัน var foo = function () {console.log (1)} () โดยตรงเช่นนี้และคำตอบก็โอเค
คำถามที่ 2: ในทำนองเดียวกันเราสามารถคิดถึงคำถามได้ หากการประกาศฟังก์ชั่นเช่นนี้เรียกโดยตรงกับวงเล็บหลังจากนั้นจะเกิดอะไรขึ้น? โปรดดูคำตอบด้านล่าง
ฟังก์ชั่น, วงเล็บ, ข้อผิดพลาด
ที่น่าสนใจถ้าคุณระบุชื่อสำหรับฟังก์ชั่นและใส่วงเล็บคู่หนึ่งไว้ข้างหลังข้อผิดพลาดเดียวกันจะถูกโยนลงไป แต่คราวนี้มันเป็นอีกเหตุผลหนึ่ง เมื่อวงเล็บถูกวางไว้หลังจากการแสดงออกของฟังก์ชั่นมันบ่งชี้ว่านี่เป็นฟังก์ชันที่เรียกว่าและวงเล็บจะถูกวางไว้หลังจากการประกาศหมายความว่าพวกเขาจะถูกแยกออกจากการประกาศฟังก์ชั่นก่อนหน้านี้อย่างสมบูรณ์ ในเวลานี้วงเล็บเป็นเพียงการแสดงอย่างง่าย ๆ ของวงเล็บ (วงเล็บปีกกาที่ใช้ควบคุมลำดับความสำคัญการทำงาน)
// อย่างไรก็ตามการประกาศฟังก์ชั่นนั้นไม่ถูกต้องทาง syntactically มันยังคงเป็นคำประกาศและวงเล็บต่อไปนี้ไม่ถูกต้องเนื่องจากวงเล็บจำเป็นต้องรวมฟังก์ชันนิพจน์ foo () {/* รหัส*/} (); // syntaxerror: ฟังก์ชันที่ไม่คาดคิด */} (1) // มันเทียบเท่ากับต่อไปนี้การประกาศฟังก์ชั่นดังต่อไปนี้นิพจน์ที่ไม่มีความสัมพันธ์เลย: ฟังก์ชัน foo () {/ *รหัส */} (1);ดำเนินการฟังก์ชั่นฟังก์ชั่นทันที (iife)
โชคดีที่แก้ไขข้อผิดพลาดทางไวยากรณ์ได้ง่าย วิธีที่ได้รับความนิยมและเป็นที่ยอมรับมากที่สุดคือการปิดการประกาศฟังก์ชั่นในวงเล็บเพื่อบอกตัวแยกวิเคราะห์เพื่อแสดงการแสดงออกของฟังก์ชั่นเนื่องจากในจาวาสคริปต์วงเล็บไม่สามารถมีการประกาศได้ ด้วยเหตุนี้เมื่อวงเล็บพบคำหลักของฟังก์ชั่นเพื่อห่อฟังก์ชั่นมันจะรู้ว่าจะแยกวิเคราะห์เป็นนิพจน์ฟังก์ชั่นแทนการประกาศฟังก์ชั่น ให้ความสนใจกับการทำความเข้าใจว่าวงเล็บที่นี่แตกต่างจากวงเล็บข้างต้นเมื่อพวกเขาพบฟังก์ชั่นนั่นคือ
เมื่อวงเล็บปรากฏในตอนท้ายของฟังก์ชั่นที่ไม่ระบุชื่อและต้องการเรียกฟังก์ชั่นมันจะเริ่มต้นเพื่อรักษาฟังก์ชั่นเป็นการประกาศฟังก์ชั่น
เมื่อห่อฟังก์ชั่นในวงเล็บมันจะแยกฟังก์ชั่นเป็นนิพจน์โดยค่าเริ่มต้นแทนที่จะประกาศฟังก์ชั่น
// รูปแบบทั้งสองสามารถใช้เรียกนิพจน์ฟังก์ชั่นได้ทันทีโดยใช้การดำเนินการของฟังก์ชั่นเพื่อสร้างตัวแปรส่วนตัว (ฟังก์ชัน () {/ * รหัส */} ()); // Crockford แนะนำอันนี้การแสดงออกในวงเล็บแสดงฟังก์ชั่นทันที ผู้ประกอบการคือการทำให้ disambiguate // ระหว่างการแสดงออกของฟังก์ชั่นและการประกาศฟังก์ชั่นพวกเขาสามารถ // ละเว้นเมื่อตัวแยกวิเคราะห์คาดว่าจะมีการแสดงออก (แต่โปรดดู // "บันทึกสำคัญ" ด้านล่าง) .var i = ฟังก์ชั่น () {return 10;} (); true && function () {/*/*/} เป็นไปได้คุณสามารถจัดเก็บไบต์โดยการใช้ตัวดำเนินการที่อยู่ข้างหน้าฟังก์ชั่นของคุณ! function () {/ * code */} (); ~ function () {/ * code */} (); - function () {/ * code */} ();+function () {/ * code */} (); // นี่เป็นอีกรูปแบบอื่น http://twitter.com/kuvos/status/18209252090847232new function () {/ * รหัส */} ฟังก์ชั่นใหม่ () {/ * รหัส */} () // ต้องการ parens เท่านั้นหมายเหตุสำคัญเกี่ยวกับวงเล็บ
ในบางกรณีไม่จำเป็นต้องล้อมรอบการแสดงออกของฟังก์ชั่นเมื่อวงเล็บที่คลุมเครือเพิ่มเติมอยู่รอบ ๆ การแสดงออกของฟังก์ชั่น (เนื่องจากวงเล็บแสดงเป็นนิพจน์แล้ว) แต่นี่ยังคงเป็นความคิดที่ดีเมื่อใช้วงเล็บเพื่อเรียกฟังก์ชันนิพจน์
วงเล็บดังกล่าวบ่งชี้ว่านิพจน์ฟังก์ชั่นจะถูกเรียกทันทีและตัวแปรจะเก็บผลลัพธ์ของฟังก์ชั่นไม่ใช่ฟังก์ชันเอง เมื่อนี่เป็นนิพจน์ฟังก์ชั่นที่ยาวมากสิ่งนี้จะช่วยประหยัดเวลาได้มากกว่าคนที่อ่านรหัสของคุณโดยไม่ต้องเลื่อนไปที่ด้านล่างของหน้าเพื่อดูว่ามีการเรียกฟังก์ชั่นหรือไม่
ตามกฎแล้วเมื่อคุณเขียนรหัสที่ชัดเจนและชัดเจนจำเป็นต้องป้องกันไม่ให้ JavaScript เกิดข้อผิดพลาดและจำเป็นต้องป้องกันไม่ให้นักพัฒนารายอื่นโยนข้อผิดพลาดให้คุณ WTFerror!
บันทึกสถานะของการปิด
เช่นเดียวกับเมื่อมีการเรียกใช้ฟังก์ชันโดยชื่อพารามิเตอร์จะถูกส่งผ่านและเมื่อการแสดงออกของฟังก์ชันถูกเรียกทันทีพารามิเตอร์จะถูกส่งผ่าน นิพจน์ฟังก์ชั่นที่เรียกว่าทันทีสามารถใช้เพื่อล็อคค่าและบันทึกสถานะได้อย่างมีประสิทธิภาพในเวลานี้เนื่องจากฟังก์ชั่นใด ๆ ที่กำหนดภายในฟังก์ชั่นสามารถใช้พารามิเตอร์และตัวแปรที่ส่งผ่านโดยฟังก์ชันภายนอก (ความสัมพันธ์นี้เรียกว่าการปิด)
// มันอาจไม่ทำงานอย่างที่คุณคิดเพราะคุณค่าของ `ฉันไม่เคยล็อค // ในทางกลับกันเมื่อมีการคลิกลิงก์แต่ละครั้ง (ลูปถูกดำเนินการอย่างดี) ดังนั้นจำนวนองค์ประกอบทั้งหมดจะปรากฏขึ้น // เพราะนี่เป็นค่าที่แท้จริงของ `ฉันในเวลานี้ var elems = document.getElementsByTagname ('a'); สำหรับ (var i = 0; i <elems.length; i ++) {elems [i] .addeventListener ('คลิก', ฟังก์ชั่น (e) {e.preventDefault () ค่า `ฉันถูกล็อคใน` LockedInindex ' // เมื่อมีการดำเนินการลูปแม้ว่าค่าตัวเลขของค่า `i` คือผลรวมขององค์ประกอบทั้งหมดทุกครั้งที่มีการเรียกว่าฟังก์ชันนิพจน์ค่าใช้จ่ายค่า` LockedInindex` ใน IIFE คือค่าที่ส่งผ่านไปโดย `ฉัน 'ดังนั้นเมื่อลิงก์ถูกคลิกค่าที่ถูกต้องจะปรากฏขึ้น var elems = document.getElementsByTagname ('a'); สำหรับ (var i = 0; i <elems.length; i ++) {(ฟังก์ชั่น (lockedinindex) {elems [i] .addeventListener ('คลิก', ฟังก์ชัน (e) {e.preventdefault () }) (i);} // คุณยังสามารถใช้ IIFE เช่นต่อไปนี้เพียงแค่ใช้วงเล็บเพื่อรวมฟังก์ชั่นการประมวลผลคลิกและไม่รวมทั้ง `addeventListener` // ไม่ว่าจะไปทางไหนตัวอย่างทั้งสองสามารถล็อคด้วย IIFE แต่ฉันพบว่าตัวอย่างก่อนหน้านี้เป็น var elems ที่อ่านได้มากขึ้น = document.getElementSbyTagname ('a'); สำหรับ (var i = 0; i <elems.length; i ++) {elems [i] .addeventlistener การแจ้งเตือน ('ฉันเป็นลิงค์ #' + LockedInindex); -โปรดจำไว้ว่าในสองตัวอย่างสุดท้ายเหล่านี้ LockedInindex สามารถเข้าถึงฉันได้โดยไม่มีปัญหาใด ๆ แต่การใช้ตัวระบุชื่อที่แตกต่างกันเป็นพารามิเตอร์ของฟังก์ชั่นสามารถทำให้แนวคิดตีความง่ายขึ้น
หนึ่งในข้อดีที่สำคัญที่สุดของการดำเนินการฟังก์ชั่นทันทีคือแม้ว่าจะไม่ได้ชื่อหรือไม่ระบุชื่อการแสดงออกของฟังก์ชั่นสามารถเรียกได้ทันทีโดยไม่ต้องใช้ตัวระบุและการปิดสามารถใช้โดยไม่ต้องปนเปื้อนของตัวแปรปัจจุบัน
อะไรคือปัญหาเกี่ยวกับฟังก์ชั่นที่ไม่ระบุตัวตน ("ฟังก์ชั่นนิรนาม")?
คุณเคยเห็นว่ามันถูกกล่าวถึงหลายครั้ง แต่ก็ยังไม่ได้อธิบายอย่างชัดเจนฉันขอแนะนำให้เปลี่ยนคำว่า
นิพจน์ฟังก์ชั่นที่ถูกปลุกเร้าทันทีคืออะไร? มันทำให้ฟังก์ชั่นนิพจน์ที่เรียกว่าทันที มันเหมือนการแสดงออกของฟังก์ชั่นที่นำคุณไปสู่การโทร
ฉันคิดว่าสมาชิกของชุมชน JavaScript ควรจะสามารถยอมรับคำศัพท์การแสดงออกของฟังก์ชั่นที่ถูกปลุกเร้าทันทีและ iife ในบทความหรือข้อความของพวกเขาเพราะฉันรู้สึกว่ามันง่ายกว่าที่จะเข้าใจแนวคิดและคำว่า
// ต่อไปนี้เป็นฟังก์ชั่นที่กำลังดำเนินการด้วยตนเองซึ่งเรียกใช้ฟังก์ชั่นของตัวเอง foo () {foo ();}; // นี่เป็นฟังก์ชั่นที่ไม่ระบุตัวตน เนื่องจากไม่มีตัวระบุจึงต้องใช้ `อาร์กิวเมนต์. callee` คุณสมบัติเพื่อเรียกมันเอง var foo = function () {arguments.callee ();}; // นี่อาจถือว่าเป็นฟังก์ชั่นที่ไม่ระบุตัวเอง ฟังก์ชั่นที่ไม่ระบุชื่อ 'เช่นนี้แม้ว่ามันจะไม่ได้รับการดำเนินคดีด้วยตนเองเพราะมันไม่ได้เรียกตัวเอง จากนั้นมันก็ถูกเรียกทันที (function () {/*code*/} ()); // การเพิ่มตัวระบุลงในนิพจน์ฟังก์ชั่น (นั่นคือการสร้างฟังก์ชั่นที่มีชื่อ) จะช่วยดีในการดีบักของเรา เมื่อมีการตั้งชื่อแล้วฟังก์ชั่นจะไม่ไม่ระบุชื่ออีกต่อไป (ฟังก์ชั่น foo () {/ * รหัส */} ()); // iifes สามารถดำเนินการได้ด้วยตัวเองแม้ว่าบางทีมันอาจไม่ใช่รูปแบบที่มีประโยชน์ที่สุด (ฟังก์ชัน () {arguments.callee ();} ()) (ฟังก์ชัน foo () {foo ();} () น่ากลัวฮะ? (ฟังก์ชั่น foo () {foo ();} ());หวังว่าตัวอย่างข้างต้นจะให้ความเข้าใจที่ชัดเจนยิ่งขึ้นเกี่ยวกับคำที่ดำเนินการด้วยตนเองซึ่งค่อนข้างทำให้เข้าใจผิดเพราะมันไม่ได้ดำเนินการฟังก์ชั่นของตัวเองแม้ว่าฟังก์ชั่นจะถูกดำเนินการ ในทำนองเดียวกันไม่จำเป็นต้องชี้ให้เห็นฟังก์ชั่นที่ไม่ระบุชื่อเนื่องจากนิพจน์ฟังก์ชั่นที่เรียกทันทีอาจเป็นฟังก์ชันที่มีชื่อหรือฟังก์ชันที่ไม่ระบุชื่อ
สุดท้าย: โหมดโมดูล
เมื่อฉันเรียกนิพจน์ฟังก์ชั่นถ้าฉันเตือนตัวเองเกี่ยวกับรูปแบบโมดูลอย่างน้อยหนึ่งครั้งฉันมักจะเพิกเฉยต่อมัน หากคุณไม่มีรูปแบบโมดูลใน JavaScript มันจะคล้ายกับตัวอย่างของฉันด้านล่าง แต่ค่าส่งคืนจะใช้วัตถุแทนฟังก์ชัน
var counter = (function () {var i = 0; return {get: function () {return i;}, set: function (val) {i = val;}, เพิ่มขึ้น: function () {return ++ i;}}} ()); counter.get (); // 0 counter.set (3); counter.increment (); // 4 counter.increment (); // 5 conuter.i; // undefined (`ฉันไม่ใช่คุณสมบัติของวัตถุที่ส่งคืน) i; // referenterror: ฉันไม่ได้กำหนด (มีอยู่ภายในการปิดเท่านั้น)วิธีโหมดโมดูลไม่เพียง แต่มีประสิทธิภาพ แต่ยังง่าย ด้วยรหัสน้อยมากคุณสามารถใช้การตั้งชื่อที่เกี่ยวข้องกับวิธีการและคุณลักษณะได้อย่างมีประสิทธิภาพ ในวัตถุการจัดระเบียบรหัสโมดูลทั้งหมดลดมลพิษของตัวแปรทั่วโลกและสร้างการใช้ตัวแปร