ทำความเข้าใจกับ $ .proxy () ใน jQuery


167

จากเอกสารฉันเข้าใจว่า.proxy()จะเปลี่ยนขอบเขตของฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ ใครช่วยอธิบายสิ่งนี้ให้ฉันดีกว่านี้ได้ไหม ทำไมเราควรทำเช่นนี้?


1
ตามเอกสารอธิบาย "วิธีนี้มีประโยชน์มากที่สุดสำหรับการแนบตัวจัดการเหตุการณ์กับองค์ประกอบที่บริบทนั้นชี้กลับไปที่วัตถุอื่นนอกจากนี้ jQuery ทำให้แน่ใจว่าแม้ว่าคุณจะผูกฟังก์ชันที่ส่งคืนจาก jQuery.proxy () ยังคงยกเลิกการผูกฟังก์ชันที่ถูกต้องถ้าผ่านต้นฉบับ " มีอะไรพิเศษเกี่ยวกับถ้อยคำที่คุณพบว่าขาดไปหรือไม่?
bzlm

1
นี่ยังไม่ชัดเจนที่นี่นอกจากนี้ jQuery ทำให้แน่ใจว่าแม้ว่าคุณจะผูกฟังก์ชันที่ส่งคืนจาก jQuery.proxy () มันจะยังคง unbind ฟังก์ชั่นที่ถูกต้องถ้าผ่านต้นฉบับ "สิ่งเดิมหมายถึงอะไร
Aditya Shukla

ต้นฉบับคือสิ่งที่สร้างพรอกซี แต่เนื่องจากคุณไม่เข้าใจสิ่งนี้อย่างเต็มที่คุณแน่ใจหรือไม่ว่าคุณต้องการใช้มัน
bzlm

1
นี่คือวิดีโอสอนการใช้งานที่ยอดเยี่ยมโดย nettuts แสดงให้เห็นว่า $ .proxy ทำงานอย่างไร http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-learning-jquery-1-4s-proxy/
ฮุสเซน

1
@bzlm ฉันอ่านเอกสาร jquery เมื่อฉันมาที่วิธีนี้
Aditya Shukla

คำตอบ:


381

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

ตัวอย่างทั่วไปอยู่ในตัวsetTimeoutที่เกิดขึ้นภายในclickตัวจัดการ

รับสิ่งนี้:

$('#myElement').click(function() {
        // In this function, "this" is our DOM element.
    $(this).addClass('aNewClass');
});

ความตั้งใจนั้นง่ายพอสมควร เมื่อมีการคลิกก็ควรจะได้รับในชั้นเรียนmyElement aNewClassภายในตัวจัดการthisแสดงองค์ประกอบที่ถูกคลิก

แต่ถ้าหากเราต้องการความล่าช้าเล็กน้อยก่อนที่จะเพิ่มชั้นเรียน เราอาจใช้ a setTimeoutเพื่อทำมันให้สำเร็จ แต่ปัญหาก็คือฟังก์ชั่นอะไรก็ตามที่เรามอบให้setTimeoutคุณค่าของthisฟังก์ชั่นภายในนั้นจะเป็นwindowองค์ประกอบของเราแทน

$('#myElement').click(function() {
    setTimeout(function() {
          // Problem! In this function "this" is not our element!
        $(this).addClass('aNewClass');
    }, 1000);
});

ดังนั้นสิ่งที่เราสามารถทำได้คือการโทร$.proxy()ส่งฟังก์ชั่นและค่าที่เราต้องการมอบหมายให้thisและมันจะคืนค่าฟังก์ชั่นที่จะเก็บค่านั้นไว้

$('#myElement').click(function() {
   // ------------------v--------give $.proxy our function,
    setTimeout($.proxy(function() {
        $(this).addClass('aNewClass');  // Now "this" is again our element
    }, this), 1000);
   // ---^--------------and tell it that we want our DOM element to be the
   //                      value of "this" in the function
});

ดังนั้นหลังจากที่เราให้$.proxy()ฟังก์ชั่นและค่าที่เราต้องการthisแล้วมันจะส่งคืนฟังก์ชั่นที่จะให้แน่ใจว่าthisได้ตั้งค่าไว้อย่างถูกต้อง

มันทำยังไง? มันก็ส่งกลับฟังก์ชั่นที่ไม่ระบุชื่อที่เรียกฟังก์ชั่นของเราใช้วิธีการซึ่งจะช่วยให้มันชัดเจนกำหนดค่าของ.apply()this

รูปลักษณ์ที่เรียบง่ายของฟังก์ชันที่ส่งคืนอาจมีลักษณะดังนี้:

function() {
    // v--------func is the function we gave to $.proxy
    func.apply( ctx );
    // ----------^------ ctx is the value we wanted for "this" (our DOM element)
}

ดังนั้นฟังก์ชั่นที่ไม่ระบุตัวตนนี้มอบให้setTimeoutและสิ่งที่มันทำก็คือเรียกใช้ฟังก์ชันดั้งเดิมของเราด้วยthisบริบทที่เหมาะสม


คุณค่าของการใช้งาน$.proxy(function () {...}, this)มากกว่า(function() {...}).call(this)อะไร? มีความแตกต่างหรือไม่?
Justin Morgan

11
@JustinMorgan: เมื่อ.callคุณเรียกใช้ฟังก์ชันนี้ในทันที ด้วย$.proxyมันเป็นเหมือนFunction.prototype.bindที่มันจะส่งกลับฟังก์ชั่นใหม่ ฟังก์ชั่นใหม่นั้นมีthisค่าที่ถูกผูกไว้อย่างถาวรดังนั้นเมื่อมันถูกส่งไปยังsetTimeoutและsetTimeoutเรียกใช้ฟังก์ชั่นในภายหลังมันจะยังคงมีthisค่าที่ถูกต้อง
สถานะสีเทากำลังจะมา

2
อะไรคือข้อดีของเทคนิคนี้ในบางสิ่งเช่นนี้? $ ('# myElement'). คลิก (ฟังก์ชั่น () {var el = $ (นี่); setTimeout (ฟังก์ชั่น () {el.addClass ('aNewClass');}, 1000);});
เกร็ก

1
คุณไม่จำเป็นต้องใช้วิธีการ $ .proxy สำหรับตัวอย่างนี้แทนคุณสามารถเขียนมันซ้ำอีกครั้งเช่น $ ('# myElement') นี้คลิก (function () {var นั้น = นี้; setTimeout (function () {/ / บริบทใหม่ผ่านตัวแปรที่ประกาศในขอบเขตของวิธีจัดการ $ (ที่) .addClass ('aNewClass');}, 1000);});
paul

4
ผู้ใช้ที่ไม่ระบุชื่อที่มีความรู้ 112k, มีความรู้เกี่ยวกับ JavaScript / jQuery ที่น่ากลัวและไม่เคยเห็นมาตั้งแต่เดือนตุลาคม 2011 ... John Resig อาจจะ?
cantera

49

โดยไม่ต้องลงรายละเอียดมากขึ้น (ซึ่งจำเป็นเพราะนี่เป็นเรื่องเกี่ยวกับบริบทใน ECMAScript ตัวแปรบริบทนี้เป็นต้น)

มี "บริบท" สามประเภทใน ECMA- / Javascript:

  • บริบทระดับโลก
  • บริบทของฟังก์ชัน
  • บริบท eval

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

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


ขอบเขตขอบเขตการเปิดใช้งานเชน
ค่านี้

ดังนั้นนี้คุ้มค่าเป็นวัตถุพิเศษที่เกี่ยวข้องกับบริบทการดำเนินการ มีสองฟังก์ชันใน ECMA- / Javascript ซึ่งอาจเปลี่ยนค่านี้ในบริบทการทำงานของฟังก์ชัน:

.call()
.apply()

หากเรามีฟังก์ชั่นfoobar()เราสามารถเปลี่ยนค่านี้โดยการโทร:

foobar.call({test: 5});

ตอนนี้เราสามารถเข้าถึงfoobarวัตถุที่เราส่งผ่านไป:

function foobar() { 
    this.test // === 5
}

นี่คือสิ่งที่jQuery.proxy()ไม่ มันใช้เวลาfunctionและและcontext(ซึ่งไม่มีอะไรอื่นนอกเหนือจากวัตถุ) และเชื่อมโยงฟังก์ชั่นโดยการเรียกใช้.call()หรือ.apply()และส่งกลับฟังก์ชั่นใหม่ที่


1
คำอธิบายที่ดีเยี่ยมเรียบง่าย / ดีกว่า jQuery docs อย่างเป็นทางการสำหรับฟังก์ชั่น
higuaro

4

ฉันได้เขียนฟังก์ชั่นนี้:

function my_proxy (func,obj)
{
    if (typeof(func)!="function")
        return;

    // If obj is empty or another set another object 
    if (!obj) obj=this;

    return function () { return func.apply(obj,arguments); }
}

1

สามารถบรรลุเป้าหมายเดียวกันได้โดยใช้ฟังก์ชั่นการดำเนินการด้วยตนเอง " การเรียกใช้ฟังก์ชันที่เรียกใช้ทันที, สั้น: IIFE" :

    $('#myElement').click(function() {  
      (function(el){
         setTimeout(function() {
              // Problem! In this function "this" is not our element!
            el.addClass('colorme');
        }, 1000);
      })($(this)); // self executing function   
    });
.colorme{
  color:red;
  font-size:20px;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

  <div id="myElement">Click me</div>
</body>
</html>


2
ที่มักจะเรียกว่า "ฟังก์ชั่นการแสดงออกทันที-เรียก" (IIFE) แทนที่จะเป็น "ตัวเองฟังก์ชั่นการดำเนินการ" ดูen.wikipedia.org/wiki/Immediately-invoked_function_expression
Chris Seed
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.