บริบทใน _.each คืออะไร (รายการตัววนซ้ำ [บริบท])


คำตอบ:


220

พารามิเตอร์บริบทเพิ่งตั้งค่าของ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วัตถุ


7
ข้อดีของมันคืออะไร? ทำไมไม่เพียงแค่อ้างถึงsomeOtherArray[num]มากกว่าthis[num]?
csjacobs24

3
@ csjacobs24: เป็นเรื่องปกติที่จะมีชุดของฟังก์ชั่นที่นำกลับมาใช้ใหม่ซึ่งจะไม่สามารถเข้าถึงขอบเขตตัวแปรเฉพาะที่ได้ นี่คือตัวอย่างง่ายๆ: jsfiddle.net/a6Rx4/745

1
คำตอบนี้จะตอบคำถาม แต่มันจะดีกว่าถ้ามันให้ตัวอย่างของวิธีนี้จะมีประโยชน์
temporary_user_name

50

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);

7

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

ตัวอย่างบางส่วน:

// 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()เข้ากันได้พื้นเมืองเมื่อผ่านการดั้งเดิม ไม่เช่นนั้นจะถูกบังคับให้กลายเป็นวัตถุซึ่งโดยทั่วไปจะยังใช้งานได้ แต่จะเร็วกว่าและปลอดภัยกว่าสำหรับประเภทที่เฉพาะเจาะจง


5

ใช้งานง่ายของ _.each

_.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);

ตัวอย่างเต็มรูปแบบที่ใช้ _.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();
_.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 ถูกผูกไว้กับวัตถุบริบทถ้ามีการส่งผ่าน


4

ตามที่อธิบายไว้ในคำตอบอื่น ๆcontextเป็นบริบทที่จะใช้ในการเรียกกลับส่งผ่านไปยังthiseach

ฉันจะอธิบายสิ่งนี้ด้วยความช่วยเหลือของซอร์สโค้ดของวิธีการที่เกี่ยวข้องจากซอร์สโค้ดที่ขีดเส้นใต้

คำจำกัดความของ_.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ถูกนำมาใช้ภายในก็จะหมายถึงfunccontext

// 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

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