โครงสร้าง (ฟังก์ชัน () {}) () ใน JavaScript คืออะไร


790

ฉันเคยรู้ว่าสิ่งนี้หมายถึงอะไร แต่ตอนนี้ฉันกำลังดิ้นรน ...

นี่เป็นสิ่งที่พูดโดยทั่วไปdocument.onloadหรือไม่

(function () {

})();

20
btw ถึงแม้ว่าคุณจะเห็นคนเรียกฟังก์ชั่นนี้ว่า 'เรียกตัวเอง' แต่ก็ไม่เป็นความจริง คำว่าiifeมีข้อดีของความแม่นยำ
AakashM

6
สิ่งนี้ให้คำอธิบายที่ดีเกี่ยวกับโครงสร้างนี้ นอกจากนี้ยังเป็นที่มาของคำว่า "IIFE" benalman.com/news/2010/11/...
jeremysawesome


2
สำหรับการตั้งชื่อของโครงสร้างนี้ยังมีลักษณะที่นี่ อ่านเกี่ยวกับวัตถุประสงค์ของโครงสร้างนี้และคำอธิบายทางเทคนิค (รวมถึงที่นี่ ) สำหรับไวยากรณ์มีลักษณะที่ว่าทำไมวงเล็บเป็นสิ่งที่จำเป็นและที่พวกเขาควรจะไป
Bergi

คำตอบ:


854

มันเป็นฟังก์ชั่นการแสดงออกทันทีหรือ IIFEสำหรับระยะสั้น มันรันทันทีหลังจากที่มันถูกสร้างขึ้น

ไม่มีส่วนเกี่ยวข้องกับตัวจัดการเหตุการณ์สำหรับเหตุการณ์ใด ๆ (เช่นdocument.onload)
พิจารณาส่วนที่อยู่ในวงเล็บคู่แรก: .... มันเป็นฟังก์ชั่นการแสดงออกปกติ จากนั้นดูที่คู่สุดท้ายซึ่งโดยปกติจะถูกเพิ่มในนิพจน์เพื่อเรียกใช้ฟังก์ชัน ในกรณีนี้การแสดงออกก่อนหน้าของเรา(function(){})();(function(){})();

รูปแบบนี้มักใช้เมื่อพยายามหลีกเลี่ยงการสร้างเนมสเปซส่วนกลางเนื่องจากตัวแปรทั้งหมดที่ใช้ภายใน IIFE (เช่นในฟังก์ชั่นปกติอื่น ๆ) ไม่สามารถมองเห็นได้นอกขอบเขต
ด้วยเหตุนี้คุณอาจสับสนกับสิ่งก่อสร้างนี้ด้วยตัวจัดการเหตุการณ์window.onloadเพราะมันมักจะใช้สิ่งนี้:

(function(){
  // all your code here
  var foo = function() {};
  window.onload = foo;
  // ...
})();
// foo is unreachable here (it’s undefined)

การแก้ไขที่แนะนำโดยGuffa :

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

อัปเดต เนื่องจากนี่เป็นหัวข้อที่ได้รับความนิยมเป็นอย่างยิ่งมูลค่าการกล่าวขวัญว่า IIFE สามารถเขียนด้วยฟังก์ชั่นลูกศรของ ES6 (เช่นGajusได้ชี้ให้เห็นในความคิดเห็น ):

((foo) => {
 // do something with foo here foo
})('foo value')

@ gion_13 ความแตกต่างระหว่างเฟสการสร้างและเฟสการแยกวิเคราะห์คืออะไร
akantoword

1
@ jlei วิธีที่ฉันเห็นวงจรชีวิตของโปรแกรม js รวมถึงขั้นตอนต่อไปนี้: การแยกวิเคราะห์การสร้าง / การคอมไพล์การดำเนินการ แม้ว่าการใช้งานจริง (และการตั้งชื่อ :))) อาจแตกต่างจากเบราว์เซอร์ไปยังเบราว์เซอร์ แต่เราสามารถกำหนดขั้นตอนเหล่านี้ในรหัสของเราโดยระวังการแยกวิเคราะห์ข้อผิดพลาดการยกและข้อผิดพลาดเวลาทำงาน ฉันเองไม่ได้พบแหล่งข้อมูลมากมายเกี่ยวกับเรื่องนี้เพราะมันอยู่ในระดับต่ำเกินไปและไม่ใช่สิ่งที่โปรแกรมเมอร์สามารถควบคุมได้ คุณสามารถค้นหาคำอธิบายบางอย่างในโพสต์ SO นี้: stackoverflow.com/a/34562772/491075
gion_13

@sam firat ของทั้งหมดมีการประกาศ varianle และคำหลักใหม่ ซึ่งหมายความว่าในตัวอย่างของคุณคุณสร้างอินสแตนท์ของ obiect ใหม่ที่กำหนดโดยตัวสร้าง (นิพจน์ฟังก์ชันนิรนาม) และมันถูกเรียกใช้ผ่านโอเปอเรเตอร์ใหม่ไม่ใช่โดยเรียก finction ดังตัวอย่าง IIFE แน่นอนว่าฟังก์ชั่นนั้นทำหน้าที่เหมือนเป็นการปิดเนื้อหา แต่มันก็เป็นกรณีการใช้งานที่แตกต่างกันไป
gion_13

สิ่งนี้ก่อให้เกิดมลภาวะต่อเนมสเปซส่วนกลางอย่างไร foo ไม่สามารถใช้งานได้นอกฟังก์ชั่นอย่างไรก็ตาม function(){ var foo = '5'; }
Pankaj

1
@Pankaj - ถ่ายโดยตัวของมันเองนั่นไม่ใช่แม้แต่ JS ที่ถูกต้องทางไวยากรณ์ (มันคือการแสดงออกของฟังก์ชั่น แต่ไม่ได้อยู่ในบริบทของการแสดงออกดังนั้นถือว่าเป็นข้อผิดพลาดทางไวยากรณ์)
เควนติน

109

มันเป็นเพียงฟังก์ชั่นนิรนามที่ถูกเรียกใช้งานทันทีหลังจากสร้างขึ้น

มันเหมือนกับว่าคุณกำหนดให้ตัวแปรและใช้มันทันทีโดยไม่มีตัวแปร:

var f = function () {
};
f();

ใน jQuery มีโครงสร้างที่คล้ายกันซึ่งคุณอาจคิดถึง:

$(function(){
});

นั่นเป็นรูปแบบสั้น ๆ ของการผูกreadyเหตุการณ์:

$(document).ready(function(){
});

แต่ข้างต้นทั้งสองไม่ได้สร้างIIFE s


83
สองคนสุดท้ายไม่ใช่ IIFE จริง ๆ เนื่องจากพวกเขาถูกเรียกใช้เมื่อ DOM พร้อมและไม่ได้ในทันที
svvac

15
@swordofpain: ใช่ถูกต้องพวกเขาไม่ใช่ IIFE
Guffa

@swordofpain กำลังพิจารณาข้อมูลโค้ดที่สอง จะมีค่าใด ๆ ในการเพิ่ม () ต่อท้ายฟังก์ชั่นโดยเปลี่ยนเป็น IIFE หรือไม่?
timebandit

เครื่องหมายอัฒภาคจำเป็นหรือไม่?
FrenkyB

@FrenkyB ไม่จำเป็นไม่ได้ แต่ได้รับการสนับสนุน (อัฒภาคมักไม่จำเป็นจริงๆใน Javascript แต่เป็นแนวปฏิบัติที่ดี) แต่ละคำสั่งนั้นเป็นข้อความที่เกิดขึ้นเพื่อรวมฟังก์ชั่นที่ไม่ระบุชื่อแทนที่จะเป็นการประกาศฟังก์ชั่น
Ledivin

52

การแสดงออกของฟังก์ชั่นที่เรียกใช้ทันที (IIFE) จะเรียกใช้ฟังก์ชันทันที นี่หมายถึงว่าฟังก์ชั่นจะถูกดำเนินการทันทีหลังจากเสร็จสิ้นการกำหนด

คำพูดที่ใช้กันทั่วไปอีกสามข้อ:

// Crockford's preference - parens on the inside
(function() {
  console.log('Welcome to the Internet. Please follow me.');
}());

//The OPs example, parentheses on the outside
(function() {
  console.log('Welcome to the Internet. Please follow me.');
})();

//Using the exclamation mark operator
//https://stackoverflow.com/a/5654929/1175496
!function() {
  console.log('Welcome to the Internet. Please follow me.');
}();

หากไม่มีข้อกำหนดพิเศษสำหรับค่าส่งคืนเราสามารถเขียน:

!function(){}();  // => true
~function(){}(); // => -1
+function(){}(); // => NaN
-function(){}();  // => NaN

หรืออาจเป็น:

~(function(){})();
void function(){}();
true && function(){ /* code */ }();
15.0, function(){ /* code */ }();

คุณยังสามารถเขียน:

new function(){ /* code */ }
31.new function(){ /* code */ }() //If no parameters, the last () is not required

4
ส่วนสุดท้าย31.new'เป็นไวยากรณ์ที่ไม่ถูกต้อง
cat

9
ทำไมจึงมีหลายวิธีในการเขียนในสิ่งเดียวกัน? !! > _ <ฉันไม่ชอบภาษานี้
Awesome_girl

6
ผู้ชนะคือ;(function(){}());
Roko C. Buljan

คำอธิบายการตั้งค่า Crockford มีประโยชน์มาก - อธิบายความแตกต่างที่ฉันเคยเห็นในป่าเช่นjQuery จิ๋ว pubsub สรุปสาระสำคัญเปลี่ยนจากรุ่นหนึ่งเป็นรุ่นอื่น (คุณสามารถเห็นการเปลี่ยนแปลงในตอนท้ายของไฟล์) และฉันไม่สามารถ ' คิดออกว่าทำไม
icc97

1
@Awesome_girl: มันไม่ได้มีหลายวิธีที่จะเขียนสิ่งเดียวกัน; มันคือ JS มีระบบแบบหลวม ๆ พร้อมกับโอเปอเรเตอร์ที่สามารถทำงานกับประเภทค่าใดก็ได้ คุณสามารถทำได้และคุณสามารถได้อย่างง่ายดายเพียงทำ1 - 1 true - function(){}มันเป็นเพียงสิ่งเดียว (โอเปอเรเตอร์การลบ infix) แต่มีตัวถูกดำเนินการที่แตกต่างกันและไร้สาระ

31

มันประกาศฟังก์ชั่นที่ไม่ระบุชื่อแล้วเรียกมันว่า:

(function (local_arg) {
   // anonymous function
   console.log(local_arg);
})(arg);

ฉันเดา "อาร์กิวเมนต์" เป็นตัวแปรภายนอกที่อ้างถึงเป็น "arg" เพื่อใช้ในบริบทท้องถิ่นภายในฟังก์ชัน?
Dalibor

@Dalibor argumentsเป็นพิเศษ ; เดาของฉันคือผู้ตอบเพียงพลิกไปที่ชื่อ
แมว

29

นั่นคือคำพูดที่ดำเนินการทันที

ดังนั้นถ้าฉันทำ:

var val = (function(){
     var a = 0;  // in the scope of this function
     return function(x){
         a += x;
         return a;
     };
})();

alert(val(10)); //10
alert(val(11)); //21

ซอ: http://jsfiddle.net/maniator/LqvpQ/


ตัวอย่างที่สอง:

var val = (function(){
     return 13 + 5;
})();

alert(val); //18

1
ฉันไม่เข้าใจว่ามันพิสูจน์ตัวเองได้อย่างไร
Exitos

1
@ Exos เพราะมันคืนค่าฟังก์ชันนั้น ฉันจะยกตัวอย่างที่สอง
Naftali aka Neal

เข้าใจง่ายมาก +1
Adiii

24

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

กรณีการใช้งานบ่อยที่สุด:

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

ในตัวอย่างต่อไปนี้countจะไม่สามารถใช้ได้นอกฟังก์ชั่นที่เรียกใช้ทันทีเช่นขอบเขตของcountจะไม่รั่วไหลออกจากฟังก์ชั่น คุณควรได้รับ a ReferenceError, ถ้าคุณพยายามเข้าถึงมันนอกฟังก์ชั่นที่เรียกใช้ทันที

(function () { 
    var count = 10;
})();
console.log(count);  // Reference Error: count is not defined

ทางเลือก ES6 (แนะนำ)

ใน ES6 ตอนนี้เราสามารถมีตัวแปรที่สร้างผ่านและlet constทั้งสองอย่างนั้นมีการกำหนดขอบเขตแบบบล็อก ( varซึ่งต่างจากแบบขอบเขตหน้าที่)

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

{ 
    let count = 10;
}
console.log(count);  // ReferenceError: count is not defined

ในตัวอย่างนี้เราใช้letในการกำหนดcountตัวแปรซึ่งทำให้countจำกัด รหัสของบล็อกเราสร้างขึ้นด้วยวงเล็บปีกกา{...}ให้บล็อกของรหัสที่เราสร้างขึ้นด้วยวงเล็บปีกกา

ฉันเรียกมันว่า“ คุกอีลอน”


10
ฉันชอบการตั้งชื่อCurly Jail อาจจะติด :)
gion_13

15
(function () {
})();

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

JavaScript ก่อน ECMAScript 6 ใช้การกำหนดขอบเขตศัพท์ดังนั้น IIFE จึงถูกใช้เพื่อจำลองการกำหนดขอบเขตบล็อก (ด้วยการกำหนดขอบเขตบล็อก ECMAScript 6 เป็นไปได้ด้วยการเปิดตัวletและconstคำหลัก) การอ้างอิงสำหรับปัญหาที่มีการกำหนดขอบเขตคำศัพท์

จำลองการกำหนดขอบเขตของบล็อกด้วย IIFE

ประโยชน์ที่ประสิทธิภาพของการใช้ IIFE คือความสามารถที่จะผ่านวัตถุที่ทั่วโลกใช้กันทั่วไปเช่นwindow, documentฯลฯ เป็นอาร์กิวเมนต์โดยการลดขอบเขตการค้นหา (โปรดจำไว้ว่า JavaScript ค้นหาคุณสมบัติในขอบเขตท้องถิ่นและขยายขอบเขตจนกระทั่งขอบเขตทั่วโลก) ดังนั้นการเข้าถึงวัตถุทั่วโลกในขอบเขตท้องถิ่นจะช่วยลดเวลาการค้นหาเช่นด้านล่าง

(function (globalObj) {
//Access the globalObj
})(window);

ขอบคุณที่ให้ความสำคัญกับการเข้าใจวงเล็บที่สองใน IIFE นอกจากนี้สำหรับการชี้แจงเวลาในการค้นหาประโยชน์ของตัวแปรทั่วโลกโดยการกำหนดไว้ในคำนิยาม
Arsal

11

ไม่โครงสร้างนี้เพิ่งสร้างขอบเขตสำหรับการตั้งชื่อ หากคุณแบ่งเป็นส่วน ๆ คุณจะเห็นว่าคุณมีภายนอก

(...)();

นั่นคือการเรียกใช้ฟังก์ชัน ภายในวงเล็บคุณมี:

function() {}

นั่นคือฟังก์ชั่นนิรนาม ทุกสิ่งที่ถูกประกาศด้วยvarภายในสิ่งปลูกสร้างจะสามารถมองเห็นได้ภายในสิ่งปลูกสร้างเดียวกันเท่านั้นและจะไม่ทำให้เกิดมลภาวะต่อเนมสเปซส่วนกลาง


11

นี่คือการแสดงออกของฟังก์ชั่นที่ถูกเรียกทันทีใน Javascript:

หากต้องการทำความเข้าใจ IIFE ใน JS ให้แบ่งย่อย:

  1. นิพจน์ : สิ่งที่ส่งคืนค่า
    ตัวอย่าง: ลองทำตามในคอนโซล Chrome นี่เป็นนิพจน์ใน JS
a = 10 
output = 10 
(1+3) 
output = 4
  1. การแสดงออกของฟังก์ชั่น :
    ตัวอย่าง:
// Function Expression 
var greet = function(name){
   return 'Namaste' + ' ' + name;
}

greet('Santosh');

การทำงานของฟังก์ชั่นการแสดงออก:
- เมื่อเครื่องยนต์ JS ทำงานเป็นครั้งแรก (บริบทการดำเนินการ - สร้างเฟส) ฟังก์ชั่นนี้ (ทางด้านขวาของ = ข้างต้น) จะไม่ถูกดำเนินการหรือเก็บไว้ในหน่วยความจำ ตัวแปร 'ทักทาย' ถูกกำหนดค่า 'ไม่ได้กำหนด' โดยเอ็นจิน JS
- ระหว่างการดำเนินการ (Execution Context - Execute phase) วัตถุ funtion ถูกสร้างขึ้นทันที ( ยังไม่ได้ดำเนินการ ) ได้รับมอบหมายให้ตัวแปร 'ทักทาย' และสามารถเรียกใช้โดยใช้ 'greet (' somename ')'

3. การแสดงออกที่เรียกใช้ Funtion ทันที:

ตัวอย่าง:

// IIFE
var greeting = function(name) {
    return 'Namaste' + ' ' + name;
}('Santosh')

console.log(greeting)  // Namaste Santosh. 

วิธีการใช้งาน IIFE :
- สังเกต '()' ทันทีหลังจากประกาศฟังก์ชั่น วัตถุ funtion ทุกอันมีคุณสมบัติ 'CODE' ติดอยู่ซึ่งสามารถเรียกได้ และเราสามารถเรียกมันได้ (หรือเรียกใช้) โดยใช้เครื่องหมาย '()'
- ดังนั้นที่นี่ระหว่างการดำเนินการ (Execution Context - Execute Phase) ฟังก์ชั่นวัตถุจะถูกสร้างขึ้นและดำเนินการในเวลาเดียวกัน - ดังนั้นตอนนี้ตัวแปรทักทายแทนการมีวัตถุ funtion มีค่าตอบแทน (สตริง)

การใช้ IIFE ทั่วไปใน JS:

รูปแบบ IIFE ต่อไปนี้ใช้บ่อย

// IIFE 
// Spelling of Function was not correct , result into error
(function (name) {
   var greeting = 'Namaste';
   console.log(greeting + ' ' + name);
})('Santosh');
  • เรากำลังทำสองสิ่งตรงนี้ a) การห่อฟังก์ชั่นการแสดงออกของเราภายในวงเล็บปีกกา () สิ่งนี้จะบอกตัวแยกวิเคราะห์ไวยากรณ์สิ่งที่อยู่ภายใน () คือการแสดงออก (การแสดงออกฟังก์ชั่นในกรณีนี้) และเป็นรหัสที่ถูกต้อง
    b) เรากำลังเรียกใช้งาน funtion นี้ในเวลาเดียวกันโดยใช้ () ที่ส่วนท้ายของมัน

ดังนั้นฟังก์ชั่นนี้จึงถูกสร้างและดำเนินการในเวลาเดียวกัน (IIFE)

usecase สำคัญสำหรับ IIFE:

IIFE รักษารหัสของเราให้ปลอดภัย
- IIFE ซึ่งเป็นฟังก์ชั่นมีบริบทการดำเนินการของตัวเองซึ่งหมายถึงตัวแปรทั้งหมดที่สร้างขึ้นภายในมันเป็นแบบโลคัลสำหรับฟังก์ชันนี้และไม่ได้แชร์กับบริบทการดำเนินการทั่วโลก

สมมติว่าฉันมีไฟล์ JS อีกไฟล์หนึ่ง (test1.js) ที่ใช้ในการสมัครของฉันพร้อมกับ iife.js (ดูด้านล่าง)

// test1.js

var greeting = 'Hello';

// iife.js
// Spelling of Function was not correct , result into error
(function (name) { 
   var greeting = 'Namaste';
   console.log(greeting + ' ' + name);
})('Santosh');

console.log(greeting)   // No collision happens here. It prints 'Hello'.

ดังนั้น IIFE ช่วยให้เราเขียนรหัสที่ปลอดภัยที่เราไม่ได้ชนกับวัตถุทั่วโลกโดยไม่ได้ตั้งใจ


ถ้าเราสร้างฟังก์ชั่นภายใน IIFE เราจะสามารถเข้าถึงมันในไฟล์ js หรือ jsx อื่น ๆ เช่นในส่วนทำปฏิกิริยา
ศิลาหิน

แม้ว่าเราไม่ได้ใช้ IIFE ตัวแปรคำทักทายจะไม่ชนกับตัวแปรคำทักทายสากล ดังนั้นข้อดีคืออะไร
Willy David Jr Jr

6

นั่นคือตัวเองเรียกใช้ฟังก์ชั่นที่ไม่ระบุชื่อ

ตรวจสอบคำอธิบาย W3Schools ของฟังก์ชั่นการเรียกตนเอง

การแสดงออกของฟังก์ชั่นสามารถทำให้ "เรียกตนเอง"

นิพจน์ที่เรียกตนเองนั้นถูกเรียกใช้ (เริ่มต้น) โดยอัตโนมัติโดยไม่ถูกเรียก

การแสดงออกของฟังก์ชั่นจะดำเนินการโดยอัตโนมัติหากการแสดงออกตามมาด้วย ()

คุณไม่สามารถเรียกใช้การประกาศฟังก์ชันได้


3
(function named(){console.log("Hello");}());<- ฟังก์ชั่นตั้งชื่อตนเอง
bryc

@bryc ทำไมคุณต้องตั้งชื่อฟังก์ชั่นที่ไม่ต้องการชื่อ
RicardoGonzales

2
@RicardoGonzales Recursion ฉันเดา
bryc

5

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

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

(function(obj){
    // Do something with this obj
})(object);

ที่นี่ 'วัตถุ' ที่คุณกำลังผ่านจะสามารถเข้าถึงได้ภายในฟังก์ชั่นโดย 'obj' ในขณะที่คุณกำลังคว้ามันในลายเซ็นฟังก์ชั่น


2
คำถามนี้มีคำตอบที่ยอมรับแล้วและคำตอบของคุณไม่ได้เพิ่มอะไรที่ยังไม่ได้ครอบคลุมโดยคำตอบที่ยอมรับแล้ว ดังนั้นจึงไม่จำเป็นต้องเขียนคำตอบนี้อย่างแน่นอน
Aadit M Shah

3
ฉันชอบอ่านคำตอบหลาย ๆ ครั้งบางครั้งการใช้ถ้อยคำอย่างใดอย่างหนึ่งอาจทำให้เกิดความแตกต่าง

ฉันคิดว่ามันเพิ่มเพราะมันทำให้ฉันรู้ว่าวงเล็บชุดที่สองนั้นมีไว้เพื่ออะไร อย่างน้อยมันก็ชัดเจนกว่าที่ฉันเห็น
johnny

คำตอบที่ฉันชอบ ปลายทั้งสองของตัวอย่าง IIFE มีพารามิเตอร์และการทำแผนที่ระหว่างทั้งสองนั้นเป็นแบบธรรมดา
สตีเฟ่นดับเบิลยู. ไรท์

4

เริ่มที่นี่:

var b = 'bee';
console.log(b);  // global

วางไว้ในฟังก์ชั่นและไม่เป็นโลกอีกต่อไป - เป้าหมายหลักของคุณ

function a() {
  var b = 'bee';
  console.log(b);
}
a();
console.log(b);  // ReferenceError: b is not defined -- *as desired*

เรียกใช้ฟังก์ชันได้ทันที - โอ๊ะโอ:

function a() {
  var b = 'bee';
  console.log(b);
}();             // SyntaxError: Expected () to start arrow function, but got ';' instead of '=>'

ใช้วงเล็บเพื่อหลีกเลี่ยงข้อผิดพลาดทางไวยากรณ์:

(function a() {
  var b = 'bee';
  console.log(b);
})(); // OK now

คุณสามารถออกจากชื่อฟังก์ชั่น:

(function () {    // no name required
  var b = 'bee';
  console.log(b);
})();

ไม่จำเป็นต้องซับซ้อนกว่านี้อีกแล้ว


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

คุณช่วยตอบคำถาม @JCarlos ได้ไหม? ในขณะที่เขาชี้ให้เห็นอย่างถูกต้องว่า IIFE มามากก่อนฟังก์ชั่นลูกศรมันจะช่วยให้เข้าใจว่าทำไมต้องมีการห่อ
Script47

@ Script47 ฉันไม่มีคำตอบสำหรับคำถามของ JCarlos ในความคิดเห็น คุณสามารถกำหนดคำถามใหม่และโพสต์ได้และฉันแน่ใจว่าคุณจะได้รับคำตอบที่ดี
Jim Flood

@JCarlos เมื่อฉันรันสิ่งที่ผิดพลาดฉันได้รับUncaught SyntaxError: Unexpected token )มากกว่าการพูดถึงฟังก์ชั่นลูกศร คุณสามารถแบ่งปันซอกับมันโดยโยนข้อผิดพลาดฟังก์ชั่นลูกศร?
Script47

2

ฟังก์ชั่นที่ไม่ระบุชื่อดำเนินการเอง มันจะถูกดำเนินการทันทีที่มันถูกสร้างขึ้น

ตัวอย่างสั้น ๆ และมีตัวอย่างที่มีประโยชน์คือ:

function prepareList(el){
  var list = (function(){
    var l = []; 
    for(var i = 0; i < 9; i++){
     l.push(i);
    }
    return l;
  })();

  return function (el){
    for(var i = 0, l = list.length; i < l; i++){
      if(list[i] == el) return list[i];
    }
    return null;
  }; 
} 

var search = prepareList();
search(2);
search(3);

ดังนั้นแทนที่จะสร้างรายการทุกครั้งคุณสร้างเพียงครั้งเดียว (น้อยกว่าค่าใช้จ่าย)


1
ตามที่เขียนไว้การค้นหาของคุณจะสร้างรายการขึ้นใหม่ในแต่ละการเรียกใช้ เพื่อหลีกเลี่ยงปัญหาดังกล่าวคุณต้อง (1) สร้างรายการและ (2) ส่งคืนฟังก์ชันการค้นหาเพื่อเป็นการปิดการเข้าถึงรายการที่คุณเพิ่งทำ คุณสามารถทำได้อย่างง่ายดายโดยใช้แบบฟอร์มการเรียกตนเองที่ไม่ระบุชื่อ ดูjsfiddle.net/BV4bT
จอร์จ

คุณช่วยอธิบายได้ไหม ... ค่าใช้จ่ายน้อยลง .. ฉันเข้าใจส่วนนี้
HIRA THAKUR

2
ค่าใช้จ่ายหมายถึงงานใด ๆ ที่ไม่จำเป็น การเติมข้อมูลอาร์เรย์ในแต่ละการเรียกใช้ฟังก์ชันไม่จำเป็นนั่นคือสาเหตุที่อาร์เรย์ในตัวอย่างนั้นบรรจุด้วย self-exec ฟังก์ชั่นไม่ระบุชื่อเป็นครั้งแรกเท่านั้น อย่างไรก็ตามดูเหมือนว่าฉันทำผิดพลาดในคำตอบของฉันเองดูลิงค์ในความคิดเห็นของจอร์จเพื่อเป็นตัวอย่างที่เหมาะสม
usoban

2

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

รหัส

var same_name = 1;

var myVar = (function() {
    var same_name = 2;
    console.log(same_name);
})();

console.log(same_name);

สร้างผลลัพธ์นี้:

2
1

โดยใช้ไวยากรณ์นี้คุณหลีกเลี่ยงการชนกับตัวแปรทั่วโลกประกาศไว้ที่อื่นในรหัส JavaScript ของคุณ


1
ถูกต้องผลลัพธ์จะเป็น 2 แล้ว 1 เพราะ myVar จะทำงานก่อน
Dalibor

1
คำอธิบายของคุณทำได้ดีในการอธิบายขอบเขตของฟังก์ชัน แต่ยังอธิบายสั้น ๆ ว่าทำไมมันถึงถูกดำเนินการในทันที การกำหนดให้กับตัวแปรเป็นการเอาชนะตนเองและอาจตั้งใจว่าจะสามารถดำเนินการได้มากกว่าหนึ่งครั้ง var same_name = 1; var myVar = function() { var same_name = 2; console.log(same_name); }; myVar(); console.log(same_name); ก็จะได้ผลเช่นเดียวกัน
domenicr

2

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

(function () {
  function Question(q,a,c) {
    this.q = q;
    this.a = a;
    this.c = c;
  }

  Question.prototype.displayQuestion = function() {
    console.log(this.q);
    for (var i = 0; i < this.a.length; i++) {
      console.log(i+": "+this.a[i]);
    }
  }

  Question.prototype.checkAnswer = function(ans) {
    if (ans===this.c) {
      console.log("correct");
    } else {
      console.log("incorrect");
    }
  }

  var q1 = new Question('Is Javascript the coolest?', ['yes', 'no'], 0);
  var q2 = new Question('Is python better than Javascript?', ['yes', 'no', 'both are same'], 2);
  var q3 = new Question('Is Javascript the worst?', ['yes', 'no'], 1);

  var questions = [q1, q2, q3];

  var n = Math.floor(Math.random() * questions.length)

  var answer = parseInt(prompt(questions[n].displayQuestion()));
  questions[n].checkAnswer(answer);
})();

1

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

พิจารณาฟังก์ชั่นด้านล่างเขียนในไฟล์ชื่อ iife.js

(function(){
       console.log("Hello Stackoverflow!");
   })();

โค้ดด้านบนนี้จะทำงานทันทีที่คุณโหลด iife.js และจะพิมพ์ ' Hello Stackoverflow! คอนโซล 'ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์'

สำหรับคำอธิบายโดยละเอียดโปรดดูการแสดงออกของฟังก์ชันที่เรียกใช้ทันที (IIFE)


1

อีกหนึ่งกรณีการใช้งานคือการบันทึกความทรงจำที่วัตถุแคชไม่ได้อยู่ในระดับโลก:

var calculate = (function() {
  var cache = {};
  return function(a) {

    if (cache[a]) {
      return cache[a];
    } else {
      // Calculate heavy operation
      cache[a] = heavyOperation(a);
      return cache[a];
    }
  }
})();

0

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

(function() {
     // all your code here
     // ...
})();

ฟังก์ชันวงเล็บคู่แรก () {... } แปลงรหัสภายในวงเล็บให้เป็นนิพจน์วงเล็บคู่ที่สองเรียกฟังก์ชันที่เกิดจากนิพจน์

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


0

เหตุผลที่ใช้ฟังก์ชั่นนิรนามที่ไม่เปิดเผยตัวตนคือพวกเขาไม่ควรถูกเรียกด้วยรหัสอื่นเนื่องจากพวกเขา "ตั้งค่า" รหัสที่ควรจะเรียก (พร้อมกับขอบเขตของฟังก์ชันและตัวแปร)

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

เด็ดมาก


0

รหัสต่อไปนี้:

(function () {

})();

เรียกว่าการแสดงออกของฟังก์ชั่นที่เรียกใช้ทันที (IIFE)

มันถูกเรียกว่าการแสดงออกฟังก์ชั่นเพราะ( yourcode )ผู้ประกอบการใน Javascript บังคับให้มันแสดงออก ความแตกต่างระหว่างการแสดงออกของฟังก์ชั่นและการประกาศฟังก์ชั่นดังต่อไปนี้:

// declaration:
function declaredFunction () {}

// expressions:

// storing function into variable
const expressedFunction = function () {}

// Using () operator, which transforms the function into an expression
(function () {})

การแสดงออกเป็นเพียงพวงของรหัสที่สามารถได้รับการประเมินให้เป็นค่าเดียว ในกรณีของการแสดงออกในตัวอย่างข้างต้นค่านี้เป็นวัตถุฟังก์ชันเดียว

หลังจากที่เรามีการแสดงออกซึ่งประเมินเป็นวัตถุฟังก์ชั่นเราก็สามารถเรียกใช้ฟังก์ชั่นวัตถุกับ()ผู้ประกอบการได้ทันที ตัวอย่างเช่น:

(function() {

  const foo = 10;        // all variables inside here are scoped to the function block
  console.log(foo);

})();

console.log(foo);  // referenceError foo is scoped to the IIFE

ทำไมถึงมีประโยชน์

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


0

ในไวยากรณ์ ES6 (โพสต์เพื่อตัวเองเนื่องจากฉันยังคงเชื่อมโยงไปถึงในหน้านี้เพื่อดูตัวอย่างอย่างรวดเร็ว):

// simple
const simpleNumber = (() => {
  return true ? 1 : 2
})()

// with param
const isPositiveNumber = ((number) => {
  return number > 0 ? true : false
})(4)

0

ฟังก์ชันนี้เรียกว่าฟังก์ชันเรียกตนเอง ฟังก์ชั่นเรียกตนเอง (เรียกอีกอย่างว่าปฏิบัติการด้วยตนเอง) เป็นฟังก์ชั่นที่ไม่ระบุชื่อ (ไม่ระบุชื่อ) ที่ถูกเรียกใช้ (เรียกว่า) ทันทีหลังจากที่นิยามไว้ อ่านเพิ่มเติมได้ที่นี่

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

นี่คือตัวอย่าง:

(function() {
    var x = 5 + 4;
    console.log(x);
})();


0

นี่คือคำอธิบายเชิงลึกเพิ่มเติมว่าทำไมคุณจะใช้สิ่งนี้:

"เหตุผลหลักในการใช้ IIFE คือการได้รับความเป็นส่วนตัวของข้อมูลเนื่องจากตัวแปร var ของขอบเขต JavaScript ของฟังก์ชันที่มีอยู่ตัวแปรใด ๆ ที่ประกาศภายใน IIFE ไม่สามารถเข้าถึงได้โดยโลกภายนอก"

http://adripofjavascript.com/blog/drips/an-introduction-to-iffes-immediately-invoked-function-expressions.html


0

มันเป็นฟังก์ชั่นการแสดงออกมันหมายถึงการเรียกใช้ฟังก์ชั่นการแสดงออกทันที (IIFE) IIFE เป็นเพียงฟังก์ชั่นที่ถูกเรียกใช้งานหลังจากสร้าง ดังนั้นเมื่อติดตั้งฟังก์ชั่นที่ต้องรอจนกว่าจะมีการเรียกใช้งาน IIFE จะถูกดำเนินการทันที ลองสร้าง IIFE ด้วยตัวอย่าง สมมติว่าเรามีฟังก์ชั่นการเพิ่มซึ่งใช้จำนวนเต็มสองจำนวนเป็น args และส่งคืนผลรวมกันทำให้การเพิ่มฟังก์ชั่นเป็น IIFE

ขั้นตอนที่ 1: กำหนดฟังก์ชั่น

function add (a, b){
    return a+b;
}
add(5,5);

ขั้นที่ 2: เรียกใช้ฟังก์ชั่นโดยใส่คำสั่ง functtion ทั้งหมดไว้ในวงเล็บ

(function add (a, b){
    return a+b;
})
//add(5,5);

ขั้นตอนที่ 3: การเรียกใช้ฟังก์ชันทันทีเพียงลบข้อความ 'เพิ่ม' ออกจากการโทร

(function add (a, b){
    return a+b;
})(5,5);

เหตุผลหลักในการใช้ IFFE คือการรักษาขอบเขตส่วนตัวภายในฟังก์ชั่นของคุณ ภายในโค้ดจาวาสคริปต์ที่คุณต้องการให้แน่ใจว่าคุณไม่ได้แทนที่ตัวแปรทั่วโลก บางครั้งคุณอาจกำหนดตัวแปรที่แทนที่ตัวแปรส่วนกลางโดยไม่ตั้งใจได้ ลองเป็นตัวอย่าง สมมติว่าเรามีไฟล์ html ที่ชื่อว่า iffe.html และรหัสในส่วนของแท็ก body คือ -

<body>
    <div id = 'demo'></div>
    <script>
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
    </script> 
</body>

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

<body>
    <div id = 'demo'></div>
    <script>
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
        const document = "hi there";
        console.log(document);
    </script> 
</body>

คุณจะสิ้นสุดในSyntaxError : การประกาศเอกสารคุณสมบัติโกลบอลที่ไม่สามารถกำหนดค่าได้

แต่ถ้าความปรารถนาของคุณคือการลบ documet ชื่อตัวแปรคุณสามารถทำได้โดยใช้ IFFE

<body>
    <div id = 'demo'></div>
    <script>
        (function(){
            const document = "hi there";
            this.document.getElementById("demo").innerHTML = "Hello JavaScript!";
            console.log(document);
        })();
        document.getElementById("demo").innerHTML = "Hello JavaScript!";
    </script> 
</body>

เอาท์พุท:

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

ลองอีกตัวอย่างหนึ่งสมมุติว่าเรามีวัตถุเครื่องคิดเลขเช่นร้อง -

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        console.log(calculator.add(5,10));
    </script> 
</body>

มันใช้งานได้อย่างมีเสน่ห์ถ้าเราบังเอิญกำหนดค่าของวัตถุเครื่องคิดเลขอีกครั้ง

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        console.log(calculator.add(5,10));
        calculator = "scientific calculator";
        console.log(calculator.mul(5,5));
    </script> 
</body>

ใช่คุณจะลงเอยด้วย TypeError: calculator.mul ไม่ใช่ฟังก์ชัน iffe.html

แต่ด้วยความช่วยเหลือของ IFFE เราสามารถสร้างขอบเขตส่วนตัวที่เราสามารถสร้างเครื่องคิดเลขชื่อตัวแปรอื่น ๆ และใช้มันได้

<body>
    <script>
        var calculator = {
            add:function(a,b){
                return a+b;
            },
            mul:function(a,b){
                return a*b;
            }
        }
        var cal = (function(){
            var calculator = {
                sub:function(a,b){
                    return a-b;
                },
                div:function(a,b){
                    return a/b;
                }
            }
            console.log(this.calculator.mul(5,10));
            console.log(calculator.sub(10,5));
            return calculator;
        })();
        console.log(calculator.add(5,10));
        console.log(cal.div(10,5));
    </script> 
</body>

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


-1

ฉันคิดว่าวงเล็บ 2 ชุดทำให้สับสนเล็กน้อย แต่ฉันเห็นการใช้งานอื่นในตัวอย่างของ Google พวกเขาใช้สิ่งที่คล้ายกันฉันหวังว่าสิ่งนี้จะช่วยให้คุณเข้าใจได้ดีขึ้น:

var app = window.app || (window.app = {});
console.log(app);
console.log(window.app);

ดังนั้นหากwindows.appไม่ได้กำหนดไว้window.app = {}จะถูกดำเนินการทันทีดังนั้นจึงwindow.appถูกกำหนดด้วย{}ในระหว่างการประเมินสภาพดังนั้นผลลัพธ์จึงเป็นทั้งตอนนี้appและwindow.appตอนนี้{}ดังนั้นเอาต์พุตคอนโซลจึงเป็น:

Object {}
Object {}

-1

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


-1

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

เราสามารถสร้างIIFE ได้สองวิธี

(function () {
    "use strict";
    var app = angular.module("myModule", []);
}());

หรือ

(function () {
    "use strict";
    var app = angular.module("myModule", []);
})();

ในข้อมูลโค้ดด้านบน“ แอป var ” เป็นตัวแปรเฉพาะตอนนี้

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