การใช้งานจริงสำหรับการปิด JavaScript คืออะไร?


279

ฉันพยายามอย่างเต็มที่ที่จะปิดหัวจาวาไว้ที่การปิด JavaScript

ฉันได้รับโดยการคืนค่าฟังก์ชันภายในมันจะสามารถเข้าถึงตัวแปรใด ๆ ที่กำหนดไว้ในพาเรนต์ทันที

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

บางคนสามารถแสดงให้ฉันเห็นว่าโลกแห่งความจริงใช้การปิดตัวหรือไม่?

เป็นเช่นนี้หรือไม่

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
+1 สำหรับการพยายามที่ยากที่สุดของคุณ :-) การปิดสามารถดูน่ากลัวจริง ๆ เริ่มต้นด้วยฉันรู้ว่าพวกเขามีไว้สำหรับฉัน เมื่อคุณได้รับการแขวนของพวกเขาคุณจะเป็น coder ที่ดีขึ้นทันที
Andy E

7
ฉันเพิ่งเขียนโพสต์บล็อกเกี่ยวกับการปิดใน JavaScript ที่คุณอาจพบ helfpul
Skilldrick

@Skilldrick ลิงก์นั้นตายแล้ว ... และฉันก็พบว่าตัวอย่างที่เป็นประโยชน์เช่นนี้มีประโยชน์มาก youtube.com/watch?v=w1s9PgtEoJs
Abhi

คำตอบ:


240

ฉันใช้การปิดบัญชีเพื่อทำสิ่งต่าง ๆ เช่น:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

อย่างที่คุณเห็นนั่นaคือตอนนี้เป็นออบเจ็กต์ที่มีเมธอดpublicfunction( a.publicfunction()) ที่เรียกprivatefunctionซึ่งมีอยู่ภายในการปิดเท่านั้น คุณสามารถไม่โทรprivatefunctionโดยตรง (เช่นa.privatefunction()) publicfunction()เพียง

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


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

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

9
ในทางเทคนิคทุกฟังก์ชั่นที่คุณทำใน Javascript บนเบราว์เซอร์เป็นการปิดเพราะวัตถุหน้าต่างถูกผูกไว้
Adam Gent

9
ฉันรู้ว่านี่เป็นคำถามเก่า แต่สำหรับฉันแล้วนี่ยังไม่ได้ให้คำตอบที่เพียงพอ ทำไมไม่เพียงแค่เรียกใช้ฟังก์ชันโดยตรง ทำไมคุณถึงต้องการฟังก์ชั่นส่วนตัว?
qodeninja

5
เพราะถึงแม้ว่าตัวอย่างจะมีเพียงฟังก์ชันเท่านั้น แต่ก็อาจมีตัวแปรที่ไม่สามารถเข้าถึงได้จากภายนอก พูดว่า: var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 ฯลฯ
Francisco Soto

211

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

<button onclick="updateClickCount()">click me</button>  

ตอนนี้อาจมีหลายวิธีเช่น:

1) คุณสามารถใช้ตัวแปรโกลบอลและฟังก์ชั่นเพื่อเพิ่มตัวนับ :

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

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


2) ทีนี้คุณอาจจะคิดว่าการประกาศตัวแปรภายในฟังก์ชั่น:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

แต่เฮ้! ทุกครั้งที่updateClickCount()เรียกใช้ฟังก์ชันตัวนับจะถูกตั้งค่าเป็น 1 อีกครั้ง


3) คิดเกี่ยวกับฟังก์ชั่นที่ซ้อนกัน ?

ฟังก์ชั่นที่ซ้อนกันมีการเข้าถึงขอบเขต "ด้านบน" พวกเขา
ในตัวอย่างนี้ฟังก์ชั่นภายในupdateClickCount()มีการเข้าถึงตัวแปรเคาน์เตอร์ในฟังก์ชั่นผู้ปกครองcountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

สิ่งนี้อาจแก้ไขปัญหาตัวนับถ้าคุณสามารถเข้าถึงupdateClickCount()ฟังก์ชั่นจากด้านนอกและคุณต้องหาวิธีที่จะดำเนินการcounter = 0เพียงครั้งเดียวไม่ใช่ทุกครั้ง


4) ปิดเพื่อช่วยเหลือ! (ฟังก์ชั่นเรียกตนเอง) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

ฟังก์ชั่นการเรียกใช้ตัวเองจะทำงานเพียงครั้งเดียว มันตั้งค่าเป็นcounterศูนย์ (0) และส่งกลับการแสดงออกของฟังก์ชั่น

วิธีนี้updateClickCountกลายเป็นฟังก์ชั่น ส่วน "วิเศษ" คือสามารถเข้าถึงตัวนับในขอบเขตหลัก

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

counterมีการป้องกันโดยขอบเขตของฟังก์ชั่นที่ไม่ระบุชื่อและสามารถเปลี่ยนแปลงได้โดยใช้ฟังก์ชั่นเพิ่ม!

ตัวอย่างที่มีชีวิตชีวามากขึ้นเกี่ยวกับการปิด:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


การอ้างอิง: https://www.w3schools.com/js/js_function_closures.asp


50
นี่เป็นคำตอบแรกที่ทำให้ฉันพูดว่า "โอ้นั่นเป็นเหตุผลที่ฉันจะใช้การปิด!"
ผู้สนับสนุนของ Devil ใน

6
คุณทำวันของฉัน :)
JerryGoyal

15
ฉันเพิ่งอ่านหน้า w3schools เมื่อปิดแล้วมาที่นี่สำหรับข้อมูลเพิ่มเติม นี่เป็นเหมือนกับหน้าw3schools
tyelford

1
@JerryGoyal คุณสามารถทำให้มันทำงานกับ 2 ปุ่มแยกกันได้หรือไม่? ฉันไม่สามารถหาวิธีที่จะใช้ตัวแปร 2 ตัว (สำเนาฟังก์ชั่น) ซึ่งดูเหมือนจะลบผลประโยชน์หลัก / ความสะดวกสบายบางส่วนออกไป
Tyler Collier

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

69

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

  1. การส่งพฤติกรรมแบบแปรตามพารามิเตอร์ไปสู่อัลกอริธึม (การโปรแกรมระดับสูงแบบดั้งเดิม):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. จำลองการเขียนโปรแกรมเชิงวัตถุ:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. ใช้การควบคุมการไหลที่แปลกใหม่เช่นการจัดการกิจกรรมของ jQuery และ AJAX API


3
( int?) ครั้งสุดท้ายที่ฉันตรวจสอบ JavaScript เป็นภาษาที่พิมพ์เป็ด บางทีคุณอาจกำลังคิดถึง Java อยู่ใช่ไหม
Hello71

1
@ Hello71: ฉันคิดว่า JavaScript แต่นิสัยเก่าตายยาก จับดี.
Marcelo Cantos

2
@MarceloCantos ดูเหมือนว่าคุณลืมเครื่องหมายจุลภาคในการใช้งานเคาน์เตอร์ ฉันแก้ไขโพสต์ของคุณเพื่อแก้ไข หวังว่าจะโอเค :)
Natan Streppel

2
@Streppel: จับได้ดี! ฉันมีความสุขมากกว่าที่คุณจะทำให้โค้ดของฉันดีขึ้น :-)
Marcelo Cantos

พยายามทำความเข้าใจ # 1 ... คุณจะโทรหา proximity_sort ได้อย่างไร?
Dave2081

26

ฉันรู้ว่าฉันสายเกินไปที่จะตอบคำถามนี้ แต่มันอาจช่วยให้ทุกคนยังคงค้นหาคำตอบในปี 2018

การปิดจาวาสคริปต์สามารถนำมาใช้ในการปรับใช้ฟังก์ชันการเค้นและdebounceในแอปพลิเคชันของคุณ

การควบคุมปริมาณ :

การควบคุมปริมาณทำให้ จำกัด จำนวนครั้งสูงสุดที่สามารถเรียกใช้ฟังก์ชันเมื่อเวลาผ่านไป เช่นเดียวกับ "เรียกใช้ฟังก์ชั่นนี้มากที่สุดทุกๆ 100 มิลลิวินาที"

รหัส:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

การเปิดตัว :

การเปิดใช้จะทำให้การ จำกัด ฟังก์ชั่นไม่ถูกเรียกอีกครั้งจนกว่าระยะเวลาหนึ่งผ่านไปโดยที่ไม่มีการเรียกใช้ เช่นเดียวกับใน "สั่งงานฟังก์ชั่นนี้เฉพาะเมื่อ 100 มิลลิวินาทีผ่านไปโดยที่ไม่มีการเรียกใช้"

รหัส:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

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

ฉันหวังว่ามันจะช่วยให้ใครบางคน


18

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

ปัญหาที่พบบ่อยที่สุดที่ฉันเห็นใน StackOverflow คือที่ใครบางคนต้องการที่จะ "ล่าช้า" การใช้ตัวแปรที่เพิ่มขึ้นในแต่ละวง แต่เนื่องจากตัวแปรที่ถูกกำหนดขอบเขตแล้วแต่ละอ้างอิงถึงตัวแปรจะเป็นหลังจากที่วงได้สิ้นสุดลงส่งผลให้ สถานะสิ้นสุดของตัวแปร:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

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

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

ที่น่าสนใจ -1 ฉันเดาว่านี่ไม่ใช่ "การใช้งานจริงสำหรับการปิดจาวาสคริปต์" ใช่ไหม
Andy E

1
ฉันพบว่ามีประโยชน์บางอย่างในการอ่านดังนั้นฉันจึงได้รับ +1 ก่อน downvote
alex

1
@alex: ขอบคุณฉันสังเกตเห็น upvote ฉันเกือบคุ้นเคยกับ downvotes ที่นี่ที่ SO มันทำให้ฉันรำคาญเพราะฉันอยากรู้ว่าฉันพูดอะไรผิดหรือผิดและพวกเขามักจะทำให้คุณคิดว่าคุณเพิ่งถูกวิพากษ์วิจารณ์จากผู้ตอบคนอื่นที่ต้องการทัศนวิสัยที่ดีกว่าสำหรับคำตอบของพวกเขาเอง โชคดีที่ฉันไม่ใช่คนอาฆาตแค้น ;-)
Andy E

1
ฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่มากขึ้นสำหรับขอบเขตบล็อกที่ไม่ทำงานของ JavaScript คุณควรจะสามารถเพิ่ม var j = i; ก่อน setTimeout แรกและรับการแจ้งเตือนให้ใช้ j การทำงานอื่นคือการใช้ 'กับ' like so: for (var i = 0; i <someVar.length; i ++) {กับ ({i: i}}) {window.setTimeout (ฟังก์ชัน () {alert ("Value of ฉันเป็น "+ i +" เมื่อตัวจับเวลานี้ถูกตั้งค่า ")}, 100);}}
davidbuttar

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

14

โดยเฉพาะอย่างยิ่งในภาษา JavaScript (หรือ ECMAScript ใด ๆ ) การปิดมีประโยชน์ในการซ่อนการใช้งานของฟังก์ชั่นในขณะที่ยังคงเปิดเผยอินเตอร์เฟส

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

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

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


2
นี่อาจฟังดูเป็นใบ้ แต่พวกเขาไม่สามารถเปิดไฟล์ JavaScript เองและเห็นการใช้งานของคุณหรือไม่
itmichaelwang

1
@Zapurdead: ใช่แน่นอนพวกเขาจะเห็นการใช้งาน แต่พวกเขาไม่สามารถเปลี่ยนการใช้งาน (โดยไม่ตั้งใจหรือตั้งใจ) โดยไม่ต้องแก้ไขซอร์สโค้ดของคุณโดยตรง ฉันคิดว่าคุณสามารถเปรียบเทียบกับสมาชิกที่ได้รับความคุ้มครองใน Java
maerics

6

มีส่วนบนเป็นปิดการปฏิบัติที่พัฒนาเครือข่าย Mozilla


เมื่อมองดูสิ่งนี้ฉันไม่เห็นว่า "ใช้งานจริง" ได้อย่างไรราวกับว่าฉันลบreturn function ()...รหัสทั้งหมดออกไปก็ยังใช้ได้ดี การปิดกิจการไม่ใช่สิ่งจำเป็น
ตัวที่

@James_Parsons จากนั้นคุณไม่สามารถกำหนดให้กับตัวจัดการเหตุการณ์เหมือนที่เคยทำในตัวอย่างแล้ว
alex

5

การใช้งานทั่วไปอื่น ๆ สำหรับการปิดคือการผูกthisไว้ในวิธีการกับวัตถุที่เฉพาะเจาะจงทำให้สามารถเรียกมันได้จากที่อื่น (เช่นตัวจัดการเหตุการณ์)

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

เมื่อใดก็ตามที่เหตุการณ์มูสทำการยิงwatcher.follow(evt)จะถูกเรียก

การปิดยังเป็นส่วนสำคัญของฟังก์ชั่นที่มีลำดับสูงกว่าซึ่งทำให้รูปแบบทั่วไปของการเขียนซ้ำฟังก์ชั่นที่คล้ายกันหลายฟังก์ชั่นเป็นคำสั่งที่สูงขึ้นเพียงครั้งเดียวโดยการกำหนดพารามิเตอร์ส่วนที่แตกต่างกัน เป็นตัวอย่างที่เป็นนามธรรม

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

กลายเป็น

fooer = function (x) {
    return function (...) {A x B}
}

โดยที่ A และ B ไม่ได้เป็นหน่วยสร้างประโยค แต่เป็นซอร์สโค้ดสตริง (ไม่ใช่ตัวอักษรของสตริง)

ดูที่ "การทำให้เพรียวลมจาวาสคริปต์ของฉันด้วยฟังก์ชัน " สำหรับตัวอย่างที่ชัดเจน


5

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

โดยไม่ต้องปิด ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

ด้วยการปิด ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
ฉันไม่แน่ใจ แต่ยังไม่มีการปิดคุณสามารถโทรในฐานะ var grretBilly = ทักทาย ("บิลลี่", "บ๊อบ"); และโทร grretBilly (); มันจะทำเช่นเดียวกัน? แม้ว่าคุณจะสร้างการปิดหรือไม่นั้นเป็นปัญหาที่แตกต่างกัน แต่การส่งชื่อในแต่ละครั้งไม่ใช่ปัญหาที่นี่
user2906608

4

หากคุณพอใจกับแนวคิดของการทำให้คลาสเป็นอินสแตนซ์ในเชิงวัตถุ (เช่นการสร้างวัตถุของคลาสนั้น) คุณจะเข้าใกล้ความเข้าใจในการปิด

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

ฉันคิดว่าการก้าวกระโดดเชิงแนวคิดของคุณถูกขัดขวางเล็กน้อยจากความจริงที่ว่าทุกฟังก์ชั่น / การปิดที่ส่งคืนโดยฟังก์ชั่น warnUser (นอกเหนือจาก: นั่นคือฟังก์ชั่นที่มีลำดับสูงกว่า ) การปิดเป็นการผูก ' มันมีประโยชน์มากกว่าในการส่ง initializers ที่แตกต่างกันไปยังฟังก์ชันลำดับสูงกว่าเหมือนกับการส่งค่าที่แตกต่างไปยัง constructor ของคลาส

ดังนั้นสมมติว่าเมื่อ 'callCount' ถึงค่าที่คุณต้องการจะจบเซสชันของผู้ใช้ คุณอาจต้องการค่าที่แตกต่างกันขึ้นอยู่กับว่าคำขอมาจากเครือข่ายท้องถิ่นหรืออินเทอร์เน็ตที่ไม่ดีขนาดใหญ่ (ใช่มันเป็นตัวอย่างที่ได้วางแผนไว้) เพื่อให้บรรลุสิ่งนี้คุณสามารถส่งค่าเริ่มต้นที่แตกต่างกันสำหรับ callCount ไปยัง warnUser (เช่น -3 หรือ 0?)

ส่วนหนึ่งของปัญหาเกี่ยวกับวรรณคดีคือศัพท์เฉพาะที่ใช้อธิบายพวกมัน ("ขอบเขตศัพท์", "ตัวแปรอิสระ") อย่าปล่อยให้มันหลอกคุณการปิดง่ายกว่าที่จะปรากฏ ... prima facie ;-)


3

ที่นี่ฉันมีตัวอย่างง่ายๆของแนวคิดการปิดซึ่งเราสามารถใช้สำหรับในเว็บไซต์อีคอมเมิร์ซของเราหรืออื่น ๆ อีกมากมายเช่นกัน ฉันกำลังเพิ่มลิงก์ jsfiddle ของฉันด้วยตัวอย่าง มันมีรายการผลิตภัณฑ์ขนาดเล็ก 3 รายการและหนึ่งรายการในรถเข็น

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

การใช้งานของการปิด:

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

ตัวอย่าง:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

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


2

ฉันเหมือนโรงงานฟังก์ชั่นของ Mozilla ตัวอย่างเช่น

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

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

2

รูปแบบโมดูล JavaScript ใช้การปิด รูปแบบที่ดีช่วยให้คุณมี vars "สาธารณะ" และ "ส่วนตัว"

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();

1

ฉันเขียนบทความสักครู่เกี่ยวกับวิธีการปิดสามารถใช้เพื่อลดความซับซ้อนของรหัสการจัดการเหตุการณ์ เปรียบเทียบการจัดการเหตุการณ์ ASP.NET กับ jQuery ฝั่งไคลเอ็นต์

http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/


1

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

โปรดผู้เชี่ยวชาญแจ้งให้เราทราบหากฉันได้ทำสิ่งไม่ดีที่นี่! ฉันยังคงเรียนรู้สิ่งนี้เอง

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

การอ้างอิง: การใช้งานจริงของการปิด

ในการฝึกปฏิบัติงานอาจสร้างการออกแบบที่หรูหราช่วยให้สามารถปรับแต่งการคำนวณที่หลากหลายการโทรรอการตัดบัญชีการโทรกลับการสร้างขอบเขตที่ห่อหุ้ม ฯลฯ

ตัวอย่างวิธีการเรียงลำดับของอาร์เรย์ที่ยอมรับว่าเป็นอาร์กิวเมนต์ของฟังก์ชันการเรียงลำดับ:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

การทำแผนที่ฟังก์ชั่นเป็นวิธีการแผนที่ของอาร์เรย์ซึ่งแผนที่อาร์เรย์ใหม่โดยเงื่อนไขของการโต้แย้งการทำงาน:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

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

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

นอกจากนี้เราอาจสังเกตการใช้ functionals เป็นตัวอย่างเช่นวิธี forEach ซึ่งใช้ฟังก์ชันกับอาร์เรย์ขององค์ประกอบ:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

ฟังก์ชั่นจะนำไปใช้กับข้อโต้แย้ง (กับรายการของข้อโต้แย้ง - นำไปใช้และข้อโต้แย้งในตำแหน่ง - ในการโทร):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

สายรอสาย:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

ฟังก์ชั่นการโทรกลับ:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

การสร้างขอบเขตที่ห่อหุ้มเพื่อวัตถุประสงค์ในการซ่อนวัตถุเสริม:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

โค้ดส่วนใหญ่ที่เราเขียนในจาวาสคริปต์ front-end นั้นเป็นแบบอิงเหตุการณ์ - เรากำหนดพฤติกรรมบางอย่างจากนั้นแนบไปกับเหตุการณ์ที่เกิดจากผู้ใช้ (เช่นคลิกหรือปุ่มกด) โดยทั่วไปรหัสของเราจะถูกแนบเป็น callback ซึ่งเป็นฟังก์ชั่นเดียวที่ถูกประมวลผลเพื่อตอบสนองต่อเหตุการณ์ size12, size14 และ size16 เป็นฟังก์ชันซึ่งจะปรับขนาดข้อความเป็น 12, 14 และ 16 พิกเซลตามลำดับ เราสามารถแนบมันเข้ากับปุ่ม (ในลิงก์กรณีนี้) ดังนี้:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

ซอ


ในขณะที่รหัสนี้อาจตอบคำถามให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและ / หรือทำไมมันแก้ปัญหาจะปรับปรุงค่าระยะยาวของคำตอบ
โดนัลด์ดั๊ก

1
ตัวอย่างนี้ดูเหมือนว่าสำหรับฉันสามารถใช้งานได้โดยไม่ต้องปิดผ่านฟังก์ชั่นมาตรฐาน ฉันพยายามที่จะหาตัวอย่างของสิ่งที่ COULDN ไม่สามารถนำไปใช้ได้โดยไม่ปิด
Zach Smith

0

การปิดเป็นวิธีที่มีประโยชน์ในการสร้าง ลำดับที่เพิ่มขึ้นตามความต้องการ:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

ความแตกต่างสรุปได้ดังนี้

ฟังก์ชั่นที่ไม่ระบุชื่อฟังก์ชั่นที่กำหนด

ไม่สามารถใช้เป็นวิธีได้สามารถใช้เป็นวิธีของวัตถุได้

มีอยู่เฉพาะในขอบเขตที่กำหนดไว้มีอยู่ภายในวัตถุที่กำหนดไว้

สามารถเรียกได้เฉพาะในขอบเขตที่กำหนดเท่านั้นสามารถเรียกได้ที่จุดใดก็ได้ในรหัส

สามารถกำหนดค่าใหม่หรือลบใหม่ไม่สามารถลบหรือเปลี่ยนแปลงได้

อ้างอิง


0

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

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

อธิบายการใช้งานจริงสำหรับการปิดใน JavaScript ---

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

ตัวอย่างง่ายๆของฟังก์ชั่นนี้คือ:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

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

แต่เมื่อเรามีลิงค์ไปยังขอบเขตนั้น

ลองดูฟังก์ชั่นถัดไป:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

ฟังก์ชั่น sayName () จากตัวอย่างนี้เป็นการปิด ฟังก์ชั่น sayName () มีขอบเขตภายในของตัวเอง (พร้อมการต้อนรับตัวแปร) และยังสามารถเข้าถึงขอบเขตของฟังก์ชั่นด้านนอก (ล้อมรอบ) ในกรณีนี้ตัวแปรทักทายจาก buildName ()

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


0

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

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

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