จากเอกสารฉันเข้าใจว่า.proxy()
จะเปลี่ยนขอบเขตของฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ ใครช่วยอธิบายสิ่งนี้ให้ฉันดีกว่านี้ได้ไหม ทำไมเราควรทำเช่นนี้?
จากเอกสารฉันเข้าใจว่า.proxy()
จะเปลี่ยนขอบเขตของฟังก์ชันที่ส่งผ่านเป็นอาร์กิวเมนต์ ใครช่วยอธิบายสิ่งนี้ให้ฉันดีกว่านี้ได้ไหม ทำไมเราควรทำเช่นนี้?
คำตอบ:
สิ่งที่มันทำในท้ายที่สุดคือทำให้แน่ใจว่าคุณค่าของ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)
อะไร? มีความแตกต่างหรือไม่?
.call
คุณเรียกใช้ฟังก์ชันนี้ในทันที ด้วย$.proxy
มันเป็นเหมือนFunction.prototype.bind
ที่มันจะส่งกลับฟังก์ชั่นใหม่ ฟังก์ชั่นใหม่นั้นมีthis
ค่าที่ถูกผูกไว้อย่างถาวรดังนั้นเมื่อมันถูกส่งไปยังsetTimeout
และsetTimeout
เรียกใช้ฟังก์ชั่นในภายหลังมันจะยังคงมีthis
ค่าที่ถูกต้อง
โดยไม่ต้องลงรายละเอียดมากขึ้น (ซึ่งจำเป็นเพราะนี่เป็นเรื่องเกี่ยวกับบริบทใน ECMAScript ตัวแปรบริบทนี้เป็นต้น)
มี "บริบท" สามประเภทใน ECMA- / Javascript:
รหัสทุกคนจะถูกดำเนินการในของบริบทการดำเนินการ มีบริบทเดียวทั่วโลกและอาจมีบริบทของฟังก์ชัน (และ eval) ได้หลายอินสแตนซ์ ตอนนี้ส่วนที่น่าสนใจ:
การเรียกใช้ฟังก์ชันทุกครั้งจะเข้าสู่บริบทการดำเนินการของฟังก์ชัน บริบทการดำเนินการของฟังก์ชั่นดูเหมือนว่า:
ขอบเขตขอบเขตการเปิดใช้งานเชน
ค่านี้
ดังนั้นนี้คุ้มค่าเป็นวัตถุพิเศษที่เกี่ยวข้องกับบริบทการดำเนินการ มีสองฟังก์ชันใน ECMA- / Javascript ซึ่งอาจเปลี่ยนค่านี้ในบริบทการทำงานของฟังก์ชัน:
.call()
.apply()
หากเรามีฟังก์ชั่นfoobar()
เราสามารถเปลี่ยนค่านี้โดยการโทร:
foobar.call({test: 5});
ตอนนี้เราสามารถเข้าถึงfoobar
วัตถุที่เราส่งผ่านไป:
function foobar() {
this.test // === 5
}
นี่คือสิ่งที่jQuery.proxy()
ไม่ มันใช้เวลาfunction
และและcontext
(ซึ่งไม่มีอะไรอื่นนอกเหนือจากวัตถุ) และเชื่อมโยงฟังก์ชั่นโดยการเรียกใช้.call()
หรือ.apply()
และส่งกลับฟังก์ชั่นใหม่ที่
ฉันได้เขียนฟังก์ชั่นนี้:
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); }
}
สามารถบรรลุเป้าหมายเดียวกันได้โดยใช้ฟังก์ชั่นการดำเนินการด้วยตนเอง " การเรียกใช้ฟังก์ชันที่เรียกใช้ทันที, สั้น: 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>