Apa konteks dalam _.each (daftar, iterator, [konteks])?

Jawaban:

220

Parameter konteks hanya menetapkan nilai thisdalam fungsi iterator.

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

Contoh Kerja: http://jsfiddle.net/a6Rx4/

Ia menggunakan nomor dari setiap anggota Array yang di-iterasi untuk mendapatkan item pada indeks tersebut someOtherArray, yang diwakili oleh thissejak kami memberikannya sebagai parameter konteks.

Jika Anda tidak mengatur konteksnya, maka thisakan merujuk ke windowobjek.

pengguna113716
sumber
7
Apa untungnya? Mengapa tidak hanya mengacu someOtherArray[num]bukan this[num]?
csjacobs24
3
@ csjacobs24: Sudah umum memiliki satu set fungsi yang dapat digunakan kembali yang tidak akan memiliki akses ke ruang lingkup variabel lokal. Berikut ini contoh sederhana: jsfiddle.net/a6Rx4/745
1
Jawaban ini memang menjawab pertanyaan itu, tetapi akan lebih baik jika memberikan contoh bagaimana ini bisa bermanfaat.
temporary_user_name
50

contextadalah di mana thismengacu pada fungsi iterator Anda. Sebagai contoh:

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

Konteksnya memungkinkan Anda memberikan argumen saat panggilan berlangsung, memungkinkan penyesuaian mudah dari fungsi pembantu umum yang dibuat sebelumnya.

beberapa contoh:

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

Bahkan dari contoh terbatas, Anda dapat melihat betapa dahsyatnya "argumen tambahan" dapat untuk membuat kode yang dapat digunakan kembali. Alih-alih membuat fungsi panggilan balik yang berbeda untuk setiap situasi, Anda biasanya dapat mengadaptasi pembantu tingkat rendah. Tujuannya adalah agar logika kustom Anda menggabungkan kata kerja dan dua kata benda, dengan pelat ketel minimum.

Memang, fungsi panah telah menghilangkan banyak "kode golf" keunggulan fungsi murni generik, tetapi keunggulan semantik dan konsistensi tetap.

Saya selalu menambahkan "use strict"bantuan untuk memberikan [].map()kompatibilitas asli ketika melewati primitif. Kalau tidak, mereka dipaksa menjadi objek, yang biasanya masih berfungsi, tetapi lebih cepat dan aman untuk menjadi tipe spesifik.

Dandavis
sumber
5

Penggunaan _.each secara mudah

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

Inilah contoh sederhana yang dapat digunakan _.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();

Keluaran:

items:  [ 'banana', 'apple', 'kiwi' ]

Alih-alih menelepon addItembeberapa kali, Anda dapat menggunakan garis bawah dengan cara ini:

_.each(['banana', 'apple', 'kiwi'], function(item) { x.addItem(item); });

yang identik dengan memanggil addItemtiga kali berurutan dengan barang-barang ini. Pada dasarnya itu array Anda dan untuk setiap item memanggil fungsi panggilan balik anonim Anda yang memanggil x.addItem(item). Fungsi panggilan balik anonim mirip dengan addItemfungsi anggota (misalnya mengambil item) dan agak tidak berguna. Jadi, alih-alih melalui fungsi anonim, lebih baik _.eachmenghindari tipuan ini dan menelepon addItemlangsung:

_.each(['banana', 'apple', 'kiwi'], x.addItem);

tetapi ini tidak akan berfungsi, karena addItemfungsi anggota di dalam keranjang thistidak akan merujuk ke xkeranjang yang Anda buat. Itu sebabnya Anda memiliki opsi untuk meneruskan keranjang Anda xuntuk digunakan sebagai [context]:

_.each(['banana', 'apple', 'kiwi'], x.addItem, x);

Contoh lengkap yang menggunakan _.each dan konteks:

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>

Singkatnya, jika fungsi panggilan balik yang Anda _.eachgunakan menggunakan cara apa pun thismaka Anda perlu menentukan apa yang thisharus dirujuk di dalam fungsi panggilan balik Anda. Mungkin tampak seperti xberlebihan dalam contoh saya, tetapi x.addItemhanya sebuah fungsi dan bisa sama sekali tidak terkait dengan xatau basket atau objek lain, misalnya :

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>

Dengan kata lain, Anda mengikat beberapa nilai ke thisdalam panggilan balik Anda, atau Anda juga dapat menggunakan mengikat secara langsung seperti ini:

_.each(['banana', 'apple', 'kiwi'], addItem.bind(x));

bagaimana fitur ini dapat berguna dengan beberapa metode garis bawah yang berbeda?

Secara umum, jika beberapa underscorejsmetode mengambil fungsi panggilan balik dan jika Anda ingin panggilan balik itu dipanggil pada beberapa fungsi anggota dari beberapa objek (misalnya fungsi yang menggunakan this) maka Anda dapat mengikat fungsi itu ke beberapa objek atau meneruskan objek itu sebagai [context]parameter dan itu niat utama. Dan di bagian atas dokumentasi underscorejs, itulah yang mereka nyatakan: Iteratee terikat ke objek konteks, jika satu dilewatkan

Pavel P
sumber
4

Seperti yang dijelaskan dalam jawaban lain, contextapakah thiskonteks yang akan digunakan dalam callback diteruskan ke each.

Saya akan menjelaskan ini dengan bantuan kode sumber metode yang relevan dari kode sumber garis bawah

Definisi _.eachatau _.forEachadalah sebagai berikut:

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

Pernyataan kedua penting untuk diperhatikan di sini

iteratee = optimizeCb(iteratee, context);

Di sini, contextditeruskan ke metode lain optimizeCbdan fungsi yang dikembalikan darinya kemudian ditugaskan iterateeyang kemudian disebut.

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

Seperti dapat dilihat dari definisi metode di atas optimizeCb, jika contexttidak lulus maka funcdikembalikan sebagaimana adanya. Jika contextdilewati, fungsi panggilan balik disebut sebagai

func.call(context, other_parameters);
          ^^^^^^^

funcdipanggil dengan call()yang digunakan untuk memanggil metode dengan mengatur thiskonteksnya. Jadi, ketika thisdigunakan di dalam func, itu akan merujuk 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>

Anda dapat mempertimbangkan contextsebagai parameter opsional terakhir forEachdalam JavaScript.

Tushar
sumber