ฉันได้อ่านจาวาสคริปต์จำนวนมากเมื่อเร็ว ๆ นี้และฉันได้สังเกตเห็นว่าไฟล์ทั้งหมดถูกห่อเหมือนไฟล์ต่อไปนี้ในไฟล์. js ที่จะนำเข้า
(function() {
    ... 
    code
    ...
})();อะไรคือเหตุผลในการทำสิ่งนี้มากกว่าฟังก์ชั่นคอนสตรัคเตอร์ที่เรียบง่าย?
ฉันได้อ่านจาวาสคริปต์จำนวนมากเมื่อเร็ว ๆ นี้และฉันได้สังเกตเห็นว่าไฟล์ทั้งหมดถูกห่อเหมือนไฟล์ต่อไปนี้ในไฟล์. js ที่จะนำเข้า
(function() {
    ... 
    code
    ...
})();อะไรคือเหตุผลในการทำสิ่งนี้มากกว่าฟังก์ชั่นคอนสตรัคเตอร์ที่เรียบง่าย?
คำตอบ:
โดยปกติจะเป็นเนมสเปซ (ดูในภายหลัง) และควบคุมการแสดงผลของฟังก์ชันสมาชิกและ / หรือตัวแปร คิดว่ามันเหมือนคำนิยามของวัตถุ ชื่อทางเทคนิคของมันคือนิพจน์ฟังก์ชันที่เรียกใช้ทันที (IIFE) ปลั๊กอิน jQuery มักเขียนเช่นนี้
ใน Javascript คุณสามารถซ้อนฟังก์ชันได้ ดังนั้นสิ่งต่อไปนี้ถูกต้องตามกฎหมาย:
function outerFunction() {
   function innerFunction() {
      // code
   }
}ตอนนี้คุณสามารถโทรหาouterFunction()แต่การแสดงของinnerFunction()จะถูก จำกัด ขอบเขตของความหมายมันคือส่วนตัวถึงouterFunction() outerFunction()มันเป็นไปตามหลักการเดียวกันกับตัวแปรใน Javascript:
var globalVariable;
function someFunction() {
   var localVariable;
}ที่สอดคล้องกัน:
function globalFunction() {
   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };
   function localFunction2() {
      //I'm named!
   }
}ในสถานการณ์ดังกล่าวข้างต้นคุณสามารถโทรglobalFunction()ได้จากทุกที่ แต่คุณไม่สามารถเรียกหรือlocalFunction1localFunction2
สิ่งที่คุณทำเมื่อคุณเขียน(function() { ... })()คือคุณกำลังสร้างโค้ดภายในวงเล็บชุดแรกเป็นตัวอักษรฟังก์ชั่น (หมายถึง "วัตถุทั้งหมด" เป็นจริงฟังก์ชัน) หลังจากนั้นคุณจะเรียกใช้ฟังก์ชัน (ตัวสุดท้าย()) ที่คุณเพิ่งกำหนดเอง ดังนั้นข้อได้เปรียบที่สำคัญของสิ่งนี้ตามที่ฉันได้กล่าวไว้ก่อนหน้าคือคุณสามารถมีวิธี / ฟังก์ชันและคุณสมบัติส่วนตัว:
(function() {
   var private_var;
   function private_function() {
     //code
   }
})();ในตัวอย่างแรกคุณจะต้องเรียกใช้globalFunctionชื่ออย่างชัดเจนเพื่อเรียกใช้ นั่นคือคุณจะทำglobalFunction()เพื่อเรียกใช้ แต่ในตัวอย่างข้างต้นคุณไม่เพียงแค่นิยามฟังก์ชัน คุณกำลังกำหนดและเรียกมันในครั้งเดียว ซึ่งหมายความว่าเมื่อโหลดไฟล์ JavaScript ของคุณแล้วจะมีการเรียกใช้งานทันที แน่นอนคุณสามารถทำได้:
function globalFunction() {
    // code
}
globalFunction();พฤติกรรมส่วนใหญ่จะเหมือนกันยกเว้นความแตกต่างที่สำคัญอย่างหนึ่ง: คุณหลีกเลี่ยงการสร้างมลภาวะระดับโลกเมื่อคุณใช้ IIFE (ดังนั้นมันก็หมายความว่าคุณไม่สามารถเรียกใช้ฟังก์ชันได้หลายครั้งเนื่องจากไม่มีชื่อ แต่เนื่องจาก ฟังก์ชั่นนี้มีขึ้นเพื่อให้ทำงานเท่านั้นเมื่อไม่มีปัญหา)
สิ่งที่ประณีตด้วย IIFEs คือคุณสามารถกำหนดสิ่งต่าง ๆ ภายในและเปิดเผยเฉพาะส่วนที่คุณต้องการกับโลกภายนอกดังนั้น (ตัวอย่างของการตั้งชื่อเพื่อให้คุณสามารถสร้างไลบรารี่ / ปลั๊กอินของคุณเอง):
var myPlugin = (function() {
 var private_var;
 function private_function() {
 }
 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()ตอนนี้คุณสามารถโทรmyPlugin.public_function1()แต่คุณไม่สามารถเข้าถึงได้private_function()! ค่อนข้างคล้ายกับนิยามของคลาส เพื่อความเข้าใจที่ดีขึ้นนี้ฉันขอแนะนำลิงก์ต่อไปนี้สำหรับการอ่านเพิ่มเติม:
แก้ไข
ฉันลืมที่จะพูดถึง ในขั้นตอนสุดท้าย()คุณสามารถผ่านสิ่งที่คุณต้องการภายใน ตัวอย่างเช่นเมื่อคุณสร้างปลั๊กอิน jQuery คุณจะผ่านjQueryหรือ$ชอบ:
(function(jQ) { ... code ... })(jQuery) ดังนั้นสิ่งที่คุณทำที่นี่คือการกำหนดฟังก์ชั่นที่ใช้ในหนึ่งพารามิเตอร์ (เรียกว่าjQตัวแปรท้องถิ่นและเป็นที่รู้จักเฉพาะฟังก์ชั่นนั้น) จากนั้นคุณจะเรียกใช้ฟังก์ชันด้วยตนเองและส่งผ่านพารามิเตอร์ (หรือที่เรียกว่าjQueryแต่นี่มาจากโลกภายนอกและมีการอ้างอิงถึง jQuery จริง ๆ ) ไม่จำเป็นต้องกดปุ่มนี้ แต่มีข้อดีบางประการ:
ก่อนหน้านี้ฉันอธิบายว่าฟังก์ชั่นเหล่านี้ทำงานโดยอัตโนมัติอย่างไรเมื่อเริ่มต้น แต่ถ้ามันทำงานโดยอัตโนมัติใครจะผ่านการโต้แย้ง เทคนิคนี้อนุมานว่าพารามิเตอร์ทั้งหมดที่คุณต้องการมีการกำหนดไว้แล้วว่าเป็นตัวแปรทั่วโลก ดังนั้นหาก jQuery ไม่ได้ถูกนิยามเป็นตัวแปรโกลบอลตัวอย่างนี้จะไม่ทำงาน ดังที่คุณอาจคาดเดาได้สิ่งหนึ่งที่ jquery.js ทำในระหว่างการเริ่มต้นคือการกำหนดตัวแปรทั่วโลก 'jQuery' รวมถึงตัวแปรทั่วโลกที่มีชื่อเสียง '$' ซึ่งอนุญาตให้โค้ดนี้ทำงานหลังจากรวม jQuery แล้ว
;(function(jQ) { ... code ... })(jQuery);ด้วยวิธีนี้ถ้ามีคนทิ้งเซมิโคลอนไว้ในสคริปต์ของพวกเขามันจะไม่ทำให้คุณเสียโดยเฉพาะอย่างยิ่งถ้าคุณวางแผนที่จะย่อขนาดและต่อสคริปต์ของคุณกับผู้อื่น
                    (function (context) { ..... })(this)ซึ่งจะช่วยให้คุณสามารถแนบสิ่งที่คุณต้องการกับบริบทหลักดังนั้นจึงเผยให้เห็น
                    ในรูปแบบที่ง่ายที่สุดเทคนิคนี้มีจุดมุ่งหมายที่จะตัดรหัสภายในขอบเขตฟังก์ชั่น
ช่วยลดโอกาสในการ:
มันไม่ตรวจจับเมื่อเอกสารพร้อม - ไม่ใช่document.onloadหรือwindow.onload
มันเป็นที่รู้จักกันทั่วไปว่าเป็นหรือImmediately Invoked Function Expression (IIFE)Self Executing Anonymous Function
var someFunction = function(){ console.log('wagwan!'); };
(function() {                   /* function scope starts here */
  console.log('start of IIFE');
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the consoleในตัวอย่างข้างต้นตัวแปรใด ๆ ที่กำหนดไว้ในฟังก์ชั่น (เช่นประกาศโดยใช้var) จะเป็น "ส่วนตัว" และสามารถเข้าถึงได้ภายในขอบเขตฟังก์ชั่นเท่านั้น (ตามที่ Vivin Paliath ใส่ไว้) กล่าวอีกนัยหนึ่งตัวแปรเหล่านี้ไม่สามารถมองเห็น / เข้าถึงได้นอกฟังก์ชั่น ดูการสาธิตสด
Javascript มีฟังก์ชันกำหนดขอบเขต "พารามิเตอร์และตัวแปรที่กำหนดในฟังก์ชั่นไม่สามารถมองเห็นได้นอกฟังก์ชั่นและตัวแปรที่กำหนดไว้ที่ใดก็ได้ภายในฟังก์ชั่นสามารถมองเห็นได้ทุกที่ภายในฟังก์ชัน" (จาก "Javascript: The Good Parts")
ในท้ายที่สุดโค้ดที่โพสต์ก่อนหน้าสามารถทำได้ดังนี้:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
  console.log('start of IIFE');
  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};
myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the consoleอยู่มาวันหนึ่งบางคนอาจคิดว่า "ต้องมีวิธีที่จะหลีกเลี่ยงการตั้งชื่อ 'myMainFunction' เนื่องจากสิ่งที่เราต้องการคือการดำเนินการทันที
หากคุณกลับไปสู่พื้นฐานคุณจะพบว่า:
expression: สิ่งที่ประเมินค่า กล่าวคือ3+11/xstatement: บรรทัดของโค้ดที่ทำอะไรบางอย่าง แต่มันไม่ได้ประเมินค่า กล่าวคือif(){}ในทำนองเดียวกันฟังก์ชั่นการแสดงออกประเมินเป็นค่า และสิ่งหนึ่งที่เป็นผล (ฉันสมมติว่า?) คือพวกเขาสามารถเรียกใช้ได้ทันที:
 var italianSayinSomething = function(){ console.log('mamamia!'); }();ดังนั้นตัวอย่างที่ซับซ้อนมากขึ้นของเราจะกลายเป็น:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
  console.log('start of IIFE');
  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the consoleขั้นตอนต่อไปคือความคิด "ทำไมมีvar myMainFunction =ถ้าเราไม่แม้แต่ใช้มัน!"
คำตอบนั้นง่าย: ลองลบออกเช่นด้านล่าง:
 function(){ console.log('mamamia!'); }();มันจะไม่ทำงานเพราะ"การประกาศฟังก์ชั่นไม่ได้ invokable"
เคล็ดลับคือว่าโดยการเอาvar myMainFunction =เราเปลี่ยนการแสดงออกของฟังก์ชั่นเป็นประกาศฟังก์ชัน ดูลิงค์ใน "ทรัพยากร" สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้
คำถามต่อไปคือ "ทำไมฉันไม่สามารถเก็บมันไว้เป็นนิพจน์ฟังก์ชั่นกับสิ่งอื่นที่ไม่ใช่var myMainFunction =?
คำตอบคือ "คุณทำได้" และมีหลายวิธีที่คุณสามารถทำได้: การเพิ่ม a +, !a -, หรืออาจจะรวมอยู่ในวงเล็บคู่หนึ่ง ตัวอย่างเช่น
 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.หรือ
 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,consoleหรือ
 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,consoleดังนั้นเมื่อมีการเพิ่มการแก้ไขที่เกี่ยวข้องกับสิ่งที่ครั้งหนึ่งเคยเป็น "รหัสทางเลือก" ของเราเราจะกลับไปที่รหัสเดียวกันกับที่ใช้ในตัวอย่าง "รหัสอธิบาย"
var someFunction = function(){ console.log('wagwan!'); };
(function() {
  console.log('start of IIFE');
  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the consoleอ่านเพิ่มเติมเกี่ยวกับExpressions vs Statements:
สิ่งหนึ่งที่อาจสงสัยคือ "จะเกิดอะไรขึ้นเมื่อคุณไม่นิยามตัวแปร 'อย่างถูกต้อง' ในฟังก์ชั่น - นั่นคือทำการมอบหมายอย่างง่ายแทน?
(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the consoleโดยพื้นฐานแล้วหากตัวแปรที่ไม่ได้ประกาศในขอบเขตปัจจุบันนั้นได้รับการกำหนดค่าดังนั้น "การค้นหาห่วงโซ่ขอบเขตจะเกิดขึ้นจนกว่าจะพบตัวแปรหรือกระทบกับขอบเขตทั่วโลก
เมื่ออยู่ในสภาพแวดล้อมเบราว์เซอร์ (เทียบกับสภาพแวดล้อมเซิร์ฟเวอร์เช่น nodejs) ขอบเขตทั่วโลกจะถูกกำหนดโดยwindowวัตถุ window.myOtherFunction()ดังนั้นเราสามารถทำได้
เคล็ดลับ "แนวทางปฏิบัติที่ดี" ของฉันในหัวข้อนี้คือใช้เสมอvarเมื่อกำหนดสิ่งต่าง ๆ : ไม่ว่าจะเป็นตัวเลขวัตถุหรือฟังก์ชั่น & แม้ในขอบเขตทั่วโลก ทำให้รหัสง่ายขึ้นมาก
บันทึก:
block scope(ปรับปรุง: บล็อกขอบเขตตัวแปรท้องถิ่นเพิ่มเข้ามาในES6 .)function scope& global scope( windowขอบเขตในสภาพแวดล้อมของเบราว์เซอร์)อ่านเพิ่มเติมเกี่ยวกับJavascript Scopes:
เมื่อคุณได้รับIIFEแนวคิดนี้มันจะนำไปสู่module patternซึ่งมักทำโดยใช้ประโยชน์จากรูปแบบ IIFE นี้ มีความสุข :)
Javascript ในเบราว์เซอร์มีขอบเขตที่มีประสิทธิภาพเพียงสองอย่างเท่านั้น: ขอบเขตฟังก์ชั่นและขอบเขตทั่วโลก
หากตัวแปรไม่ได้อยู่ในขอบเขตของฟังก์ชันมันจะอยู่ในขอบเขตส่วนกลาง และตัวแปรทั่วโลกมักจะไม่ดีดังนั้นนี่คือโครงสร้างเพื่อให้ตัวแปรของไลบรารีเป็นของตัวเอง
ที่เรียกว่าการปิด โดยทั่วไปแล้วมันจะปิดผนึกรหัสภายในฟังก์ชั่นเพื่อให้ห้องสมุดอื่นไม่รบกวนมัน มันคล้ายกับการสร้างเนมสเปซในภาษาที่รวบรวม
ตัวอย่าง. สมมติว่าฉันเขียน:
(function() {
    var x = 2;
    // do stuff with x
})();ตอนนี้ห้องสมุดอื่น ๆ ไม่สามารถเข้าถึงตัวแปรที่xฉันสร้างขึ้นเพื่อใช้ในห้องสมุดของฉัน
(function(){ ... return { publicProp1: 'blah' }; })();แต่คุณสามารถให้ทำงานที่คล้ายกันโดยกลับวัตถุที่มีคุณสมบัติที่คุณต้องการที่จะเผยแพร่ต่อไปนี้: เห็นได้ชัดว่าไม่ขนานกับการกำหนดเนมอย่างสมบูรณ์ แต่อาจช่วยให้คิดแบบนั้นได้
                    คุณสามารถใช้ฟังก์ชั่นการปิดเป็นข้อมูลในนิพจน์ที่ใหญ่ขึ้นเช่นเดียวกับในวิธีนี้ในการพิจารณาการรองรับเบราว์เซอร์สำหรับวัตถุ html5 บางส่วน
   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }นอกเหนือจากการรักษาตัวแปรในตัวเครื่องแล้วการใช้งานที่สะดวกมากอย่างหนึ่งคือเมื่อเขียนไลบรารีโดยใช้ตัวแปรส่วนกลางคุณสามารถตั้งชื่อตัวแปรให้สั้นลงเพื่อใช้ภายในไลบรารี มักใช้ในการเขียนปลั๊กอิน jQuery เนื่องจาก jQuery อนุญาตให้คุณปิดการใช้งานตัวแปร $ ที่ชี้ไปที่ jQuery โดยใช้ jQuery.noConflict () ในกรณีที่มันถูกปิดใช้งานรหัสของคุณยังคงสามารถใช้ $ และไม่ทำลายถ้าคุณทำ:
(function($) { ...code...})(jQuery);เราควรใช้ 'ใช้เข้มงวด' ในฟังก์ชันขอบเขตเพื่อให้แน่ใจว่ารหัสควรถูกเรียกใช้ใน "โหมดเข้มงวด" รหัสตัวอย่างที่แสดงด้านล่าง
(function() {
    'use strict';
    //Your code from here
})();