Bối cảnh trong _.each (danh sách, iterator, [bối cảnh]) là gì?


Câu trả lời:


220

Tham số ngữ cảnh chỉ đặt giá trị của thishàm 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);

Ví dụ hoạt động: http://jsfiddle.net/a6Rx4/

Nó sử dụng số từ mỗi thành viên của Mảng được lặp để lấy mục ở chỉ mục someOtherArrayđó, được biểu thị bởi thisvì chúng tôi đã chuyển nó làm tham số ngữ cảnh.

Nếu bạn không đặt bối cảnh, thì thissẽ tham chiếu đến windowđối tượng.


7
Lợi thế của điều đó là gì? Tại sao không chỉ tham khảo someOtherArray[num]chứ không phải this[num]?
csjacobs24

3
@ csjacobs24: Thông thường có một bộ các hàm có thể sử dụng lại sẽ không có quyền truy cập vào phạm vi biến cục bộ. Đây là một ví dụ đơn giản: jsfiddle.net/a6Rx4/745

1
Câu trả lời này trả lời câu hỏi, nhưng sẽ tốt hơn nếu nó cung cấp các ví dụ về cách điều này có thể hữu ích.
tạm

50

contextlà nơi thisđề cập đến trong chức năng lặp của bạn. Ví dụ:

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

Ngữ cảnh cho phép bạn cung cấp các đối số tại thời điểm cuộc gọi, cho phép dễ dàng tùy chỉnh các hàm trợ giúp được xây dựng trước chung chung.

vài ví dụ:

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

Ngay cả từ các ví dụ hạn chế, bạn có thể thấy "đối số bổ sung" có thể mạnh đến mức nào để tạo mã có thể sử dụng lại. Thay vì thực hiện một chức năng gọi lại khác nhau cho từng tình huống, bạn thường có thể điều chỉnh một trình trợ giúp cấp thấp. Mục tiêu là để logic tùy chỉnh của bạn kết hợp một động từ và hai danh từ, với bản tóm tắt tối thiểu.

Phải thừa nhận rằng, các hàm mũi tên đã loại bỏ rất nhiều lợi thế "mã golf" của các hàm thuần túy chung, nhưng các lợi thế về ngữ nghĩa và tính nhất quán vẫn còn.

Tôi luôn thêm "use strict"vào các trình trợ giúp để cung cấp [].map()khả năng tương thích riêng khi vượt qua các nguyên thủy. Mặt khác, chúng bị ép buộc vào các đối tượng, thường vẫn hoạt động, nhưng nó nhanh hơn và an toàn hơn để được cụ thể theo kiểu.


5

Cách sử dụng đơn giản của _.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>

Đây là ví dụ đơn giản có thể sử dụng _.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();

Đầu ra:

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

Thay vì gọi addItemnhiều lần, bạn có thể sử dụng gạch dưới theo cách này:

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

giống hệt như gọi addItemba lần liên tiếp với các mục này. Về cơ bản, nó lặp lại mảng của bạn và cho mỗi mục gọi hàm gọi lại ẩn danh của bạn mà gọi x.addItem(item). Hàm gọi lại ẩn danh tương tự như addItemhàm thành viên (ví dụ: nó lấy một mục) và là loại vô nghĩa. Vì vậy, thay vì đi qua chức năng ẩn danh, tốt hơn hết là _.eachtránh sự gián tiếp này và gọi addItemtrực tiếp:

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

nhưng điều này sẽ không hoạt động, vì addItemchức năng thành viên của giỏ thissẽ không đề cập đến xgiỏ của bạn mà bạn đã tạo. Đó là lý do tại sao bạn có một tùy chọn để vượt qua giỏ hàng của mình xđể được sử dụng như [context]:

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

Ví dụ đầy đủ sử dụng _.each và bối cảnh:

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>

Nói tóm lại, nếu chức năng gọi lại mà bạn chuyển đến _.eachbằng bất kỳ cách nào sử dụng thisthì bạn cần chỉ định những gì thissẽ được đề cập bên trong chức năng gọi lại của bạn. Nó có vẻ như xlà không cần thiết trong ví dụ của tôi, nhưng x.addItemchỉ là một chức năng và có thể hoàn toàn không liên quan đến xhoặc basket hoặc bất kỳ đối tượng khác, ví dụ :

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>

Nói cách khác, bạn liên kết một số giá trị vào thisbên trong cuộc gọi lại của mình hoặc bạn cũng có thể sử dụng liên kết trực tiếp như thế này:

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

Làm thế nào tính năng này có thể hữu ích với một số phương pháp gạch dưới khác nhau?

Nói chung, nếu một underscorejsphương thức nào đó có chức năng gọi lại và nếu bạn muốn cuộc gọi đó được gọi trên một số chức năng thành viên của một đối tượng nào đó (ví dụ như một hàm sử dụng this) thì bạn có thể liên kết chức năng đó với một đối tượng hoặc truyền đối tượng đó làm [context]tham số và đó là ý định chính. Và ở đầu tài liệu underscorejs, đó chính xác là những gì họ nêu: iteratee bị ràng buộc với đối tượng bối cảnh, nếu một được thông qua


4

Như đã giải thích trong câu trả lời khác, contextthisbối cảnh được sử dụng bên trong gọi lại truyền choeach .

Tôi sẽ giải thích điều này với sự trợ giúp của mã nguồn của các phương thức liên quan từ mã nguồn gạch dưới

Định nghĩa của _.eachhoặc _.forEachnhư sau:

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

Tuyên bố thứ hai rất quan trọng cần lưu ý ở đây

iteratee = optimizeCb(iteratee, context);

Ở đây, contextđược truyền cho một phương thức khác optimizeCbvà hàm trả về từ nó sau đó được gán cho iterateecái được gọi sau này.

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

Như có thể thấy từ định nghĩa phương thức trên optimizeCb, nếu contextkhông được thông qua thì funcđược trả về như hiện tại. Nếu contextđược thông qua, hàm gọi lại được gọi là

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

funcđược gọi với call()cái được sử dụng để gọi một phương thức bằng cách đặt thisbối cảnh của nó. Vì vậy, khi thisđược sử dụng bên trong func, nó sẽ đề cập đến 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>

Bạn có thể coi contextlà tham số tùy chọn cuối cùng forEachtrong JavaScript.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.