ฉันใหม่สำหรับ underscore.js มีจุดประสงค์อะไร[context]
ใน_.each()
? ควรใช้อย่างไร?
ฉันใหม่สำหรับ underscore.js มีจุดประสงค์อะไร[context]
ใน_.each()
? ควรใช้อย่างไร?
คำตอบ:
พารามิเตอร์บริบทเพิ่งตั้งค่าของthis
ในฟังก์ชั่นตัววนซ้ำ
var someOtherArray = ["name","patrick","d","w"];
_.each([1, 2, 3], function(num) {
// In here, "this" refers to the same Array as "someOtherArray"
alert( this[num] ); // num is the value from the array being iterated
// so this[num] gets the item at the "num" index of
// someOtherArray.
}, someOtherArray);
ตัวอย่างการทำงาน: http://jsfiddle.net/a6Rx4/
มันใช้หมายเลขจากสมาชิกของ Array แต่ละตัวที่ถูกทำซ้ำเพื่อรับไอเท็มที่ดัชนีsomeOtherArray
นั้นซึ่งแทนด้วยthis
เนื่องจากเราส่งผ่านเป็นพารามิเตอร์บริบท
หากคุณไม่ได้ตั้งค่าบริบทแล้วthis
จะอ้างถึงwindow
วัตถุ
context
คือที่ที่this
อ้างถึงในฟังก์ชันตัววนซ้ำของคุณ ตัวอย่างเช่น:
var person = {};
person.friends = {
name1: true,
name2: false,
name3: true,
name4: true
};
_.each(['name4', 'name2'], function(name){
// this refers to the friends property of the person object
alert(this[name]);
}, person.friends);
บริบทช่วยให้คุณสามารถกำหนดอาร์กิวเมนต์ในเวลาโทรได้ช่วยให้สามารถปรับแต่งฟังก์ชันตัวช่วยทั่วไปที่สร้างไว้ล่วงหน้าได้อย่างง่ายดาย
ตัวอย่างบางส่วน:
// stock footage:
function addTo(x){ "use strict"; return x + this; }
function pluck(x){ "use strict"; return x[this]; }
function lt(x){ "use strict"; return x < this; }
// production:
var r = [1,2,3,4,5,6,7,8,9];
var words = "a man a plan a canal panama".split(" ");
// filtering numbers:
_.filter(r, lt, 5); // elements less than 5
_.filter(r, lt, 3); // elements less than 3
// add 100 to the elements:
_.map(r, addTo, 100);
// encode eggy peggy:
_.map(words, addTo, "egg").join(" ");
// get length of words:
_.map(words, pluck, "length");
// find words starting with "e" or sooner:
_.filter(words, lt, "e");
// find all words with 3 or more chars:
_.filter(words, pluck, 2);
แม้จากตัวอย่างที่ จำกัด คุณสามารถดูว่า "อาร์กิวเมนต์พิเศษ" มีประสิทธิภาพเพียงใดสำหรับการสร้างรหัสที่ใช้งานได้อีกครั้ง แทนที่จะสร้างฟังก์ชันการโทรกลับที่แตกต่างกันสำหรับแต่ละสถานการณ์คุณสามารถปรับตัวช่วยในระดับต่ำได้ เป้าหมายคือการให้ตรรกะที่กำหนดเองของคุณรวมคำกริยาและคำนามสองคำพร้อมด้วยสำเร็จรูปน้อยที่สุด
เป็นที่ยอมรับกันว่าฟังก์ชั่นลูกศรได้ขจัดข้อได้เปรียบของ "รหัสกอล์ฟ" ของฟังก์ชั่นล้วน ๆ ออกมามากมาย แต่ข้อดีของความหมายและความสอดคล้องยังคงอยู่
ฉันมักจะเพิ่ม"use strict"
ให้กับผู้ช่วยเหลือเพื่อให้[].map()
เข้ากันได้พื้นเมืองเมื่อผ่านการดั้งเดิม ไม่เช่นนั้นจะถูกบังคับให้กลายเป็นวัตถุซึ่งโดยทั่วไปจะยังใช้งานได้ แต่จะเร็วกว่าและปลอดภัยกว่าสำหรับประเภทที่เฉพาะเจาะจง
_.each(['Hello', 'World!'], function(word){
console.log(word);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
นี่คือตัวอย่างง่ายๆที่สามารถใช้_.each
:
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
x.addItem('banana');
x.addItem('apple');
x.addItem('kiwi');
x.show();
เอาท์พุท:
items: [ 'banana', 'apple', 'kiwi' ]
แทนที่จะโทรaddItem
หลายครั้งคุณสามารถใช้ขีดเส้นใต้ด้วยวิธีนี้:
_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });
ซึ่งเหมือนกับการโทรaddItem
สามครั้งติดต่อกันกับรายการเหล่านี้ โดยทั่วไปจะ iterates x.addItem(item)
อาร์เรย์ของคุณและสำหรับแต่ละรายการเรียกฟังก์ชันการเรียกกลับของคุณที่ไม่ระบุชื่อที่โทร ฟังก์ชั่นการโทรกลับไม่ระบุชื่อคล้ายกับaddItem
ฟังก์ชั่นสมาชิก (เช่นใช้รายการ) และเป็นชนิดไม่มีจุดหมาย ดังนั้นแทนที่จะไปผ่านฟังก์ชั่นนิรนามจะดีกว่าที่_.each
จะหลีกเลี่ยงการอ้อมและการโทรaddItem
โดยตรง:
_.each(['banana', 'apple', 'kiwi'], x.addItem);
แต่สิ่งนี้จะไม่ทำงานเนื่องจากaddItem
ฟังก์ชั่นสมาชิกของตะกร้าภายในthis
ไม่ได้หมายถึงx
ตะกร้าของคุณที่คุณสร้างขึ้น นั่นเป็นเหตุผลที่คุณมีตัวเลือกในการส่งตะกร้าของคุณx
เพื่อใช้เป็น[context]
:
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
function basket() {
this.items = [];
this.addItem = function(item) {
this.items.push(item);
};
this.show = function() {
console.log('items: ', this.items);
}
}
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], x.addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
กล่าวโดยย่อหากฟังก์ชันการโทรกลับที่คุณส่งไปใช้ไม่ว่า_.each
ในกรณีใดthis
คุณจำเป็นต้องระบุสิ่งที่this
ควรอ้างอิงถึงภายในฟังก์ชันการโทรกลับ มันอาจดูเหมือนx
จะซ้ำซ้อนในตัวอย่างของฉัน แต่x.addItem
เป็นเพียงฟังก์ชั่นและอาจจะโดยสิ้นเชิงไม่เกี่ยวข้องกับx
หรือbasket
หรือวัตถุอื่นใด ๆ ตัวอย่างเช่น :
function basket() {
this.items = [];
this.show = function() {
console.log('items: ', this.items);
}
}
function addItem(item) {
this.items.push(item);
};
var x = new basket();
_.each(['banana', 'apple', 'kiwi'], addItem, x);
x.show();
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
คุณผูกค่าบางอย่างไว้this
ในการติดต่อกลับหรือคุณอาจใช้การผูกเช่นนี้โดยตรง:
_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));
คุณลักษณะนี้มีประโยชน์อย่างไรกับวิธีขีดล่างที่แตกต่างกัน
โดยทั่วไปหากunderscorejs
วิธีการบางอย่างใช้ฟังก์ชั่นการโทรกลับและถ้าคุณต้องการที่จะเรียกว่าโทรกลับในฟังก์ชั่นสมาชิกของวัตถุบางอย่าง (เช่นฟังก์ชั่นที่ใช้this
) จากนั้นคุณอาจผูกฟังก์ชั่น[context]
นั้น ความตั้งใจหลัก และที่ด้านบนของเอกสารขีดล่างนั่นคือสิ่งที่พวกเขาระบุ: iteratee ถูกผูกไว้กับวัตถุบริบทถ้ามีการส่งผ่าน
ตามที่อธิบายไว้ในคำตอบอื่น ๆcontext
เป็นบริบทที่จะใช้ในการเรียกกลับส่งผ่านไปยังthis
each
ฉันจะอธิบายสิ่งนี้ด้วยความช่วยเหลือของซอร์สโค้ดของวิธีการที่เกี่ยวข้องจากซอร์สโค้ดที่ขีดเส้นใต้
คำจำกัดความของ_.each
หรือ_.forEach
เป็นดังนี้:
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
ข้อความที่สองเป็นสิ่งสำคัญที่ควรทราบที่นี่
iteratee = optimizeCb(iteratee, context);
ที่นี่context
จะถูกส่งไปยังวิธีอื่นoptimizeCb
และฟังก์ชั่นที่ส่งคืนจากมันจะถูกกำหนดให้iteratee
ซึ่งเรียกว่าในภายหลัง
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1:
return function(value) {
return func.call(context, value);
};
case 2:
return function(value, other) {
return func.call(context, value, other);
};
case 3:
return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4:
return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
ที่สามารถเห็นได้จากคำนิยามวิธีการข้างต้นของoptimizeCb
ถ้าcontext
ไม่ผ่านก็func
จะถูกส่งกลับตามที่มันเป็น หากcontext
ถูกส่งผ่านฟังก์ชันการโทรกลับจะถูกเรียกว่า
func.call(context, other_parameters);
^^^^^^^
func
ถูกเรียกด้วยcall()
ซึ่งจะใช้ในการเรียกใช้วิธีการโดยการตั้งค่าthis
บริบทของมัน ดังนั้นเมื่อthis
ถูกนำมาใช้ภายในก็จะหมายถึงfunc
context
// Without `context`
_.each([1], function() {
console.log(this instanceof Window);
});
// With `context` as `arr`
var arr = [1, 2, 3];
_.each([1], function() {
console.log(this);
}, arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
คุณสามารถพิจารณาcontext
ว่าเป็นพารามิเตอร์ทางเลือกสุดท้ายforEach
ใน JavaScript
someOtherArray[num]
มากกว่าthis[num]
?