Как передать дополнительный параметр функции обратного вызова в методе Javascript .filter ()?

106

Я хочу сравнить каждую строку в массиве с заданной строкой. Моя текущая реализация:

function startsWith(element) {
    return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith);

Эта простая функция работает, но только потому, что прямо сейчас wordToCompare устанавливается как глобальная переменная, но, конечно, я хочу избежать этого и передать его как параметр. Моя проблема в том, что я не уверен, как определить startWith (), поэтому он принимает один дополнительный параметр, потому что я действительно не понимаю, как передаются параметры по умолчанию, которые он принимает. Я перепробовал все возможные способы, но ни один из них не помог.

Если бы вы также могли объяснить, как работают переданные параметры «встроенным» функциям обратного вызова (извините, я не знаю лучшего термина для них), это было бы здорово

Agentte_secreto
источник

Ответы:

153

Сделать startsWithпринять слово для сравнения и возвращает функцию , которая затем будет использоваться в качестве функции фильтра / обратного вызова:

function startsWith(wordToCompare) {
    return function(element) {
        return element.indexOf(wordToCompare) === 0;
    }
}

addressBook.filter(startsWith(wordToCompare));

Другой вариант - использовать Function.prototype.bind [MDN] (доступно только в браузере, поддерживающем ECMAScript 5, перейдите по ссылке для прокладки для старых браузеров) и «исправьте» первый аргумент:

function startsWith(wordToCompare, element) {
    return element.indexOf(wordToCompare) === 0;
}

addressBook.filter(startsWith.bind(this, wordToCompare));

Я действительно не понимаю, как передаются параметры по умолчанию

В этом нет ничего особенного. В какой-то момент filterпросто вызывает обратный вызов и передает текущий элемент массива. Итак, это функция, вызывающая другую функцию, в данном случае обратный вызов, который вы передаете в качестве аргумента.

Вот пример подобной функции:

function filter(array, callback) {
    var result = [];
    for(var i = 0, l = array.length; i < l; i++) {
        if(callback(array[i])) {  // here callback is called with the current element
            result.push(array[i]);
        }
    }
    return result;
}
Феликс Клинг
источник
1
Хорошо, теперь я понимаю. Я пытался передать параметры непосредственно в функцию обратного вызова ... Мне действительно нужно поработать над своим JavaScript. Спасибо, Феликс, ваш ответ очень полезен
agentte_secreto
А как насчет передачи дополнительных аргументов? Я попытался передать массив аргументов, но это, похоже, не
удалось
@geotheory: а что с ними? вы передаете несколько аргументов, как и любую другую функцию.
Феликс Клинг
bind (this) после имени функции вместе с цепочкой filter () помогли мне использовать .this внутри функции. Спасибо.
Сагар Хатри
109

Второй параметр фильтра установит это внутри обратного вызова.

arr.filter(callback[, thisArg])

Итак, вы могли бы сделать что-то вроде:

function startsWith(element) {
    return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
Джефф
источник
7
Я нашел, что это лучший ответ.
Джеф Гилберт
так что теперь новый массив будет назначен объекту wordToCompare, верно? Как можно позже получить доступ к новому массиву с помощью объекта wordToCompare?
Badhon Jain
лучший ответ. идеально подходит как для фильтрации, так и для поиска. И в соответствии с документацией WC3 для обоих: thisValue - Необязательно. Значение, передаваемое функции, которое будет использоваться в качестве ее значения «this». Если этот параметр пуст, значение undefined будет передано как его значение
this
1
@TarekEldeeb просто передайте созданный вами объект{one: 'haha', two:'hoho'}
toddmo
2
Это отличный пример того, как могут быть огромные различия между ответами относительно сложности и насколько запутанными они могут быть по сравнению с тем, насколько простыми они могут быть
toddmo
14

Для тех, кто ищет альтернативу ES6 с использованием стрелочных функций, вы можете сделать следующее.

let startsWith = wordToCompare => (element, index, array) => {
  return element.indexOf(wordToCompare) === 0;
}

// where word would be your argument
let result = addressBook.filter(startsWith("word"));

Обновленная версия с использованием включает :

const startsWith = wordToCompare => (element, index, array) => {
  return element.includes(wordToCompare);
}
jhamPac
источник
Есть ли способ передать другой параметр из элемента, индекса и массива? Например, я хочу передать переменную X.
leandrotk
@leandrotk в этом случае "wordToCompare" - это переменная "X", которая должна быть передана.
GetBackerZ
11
function startsWith(element, wordToCompare) {
    return element.indexOf(wordToCompare) === 0;
}

// ...
var word = "SOMETHING";

addressBook.filter(function(element){
    return startsWith(element, word);
});
Джеймс Монтань
источник
4

Вы можете использовать функцию стрелки внутри фильтра, например:

result = addressBook.filter(element => element.indexOf(wordToCompare) === 0);

Стрелочные функции на MDN

Выражение функции стрелки имеет более короткий синтаксис по сравнению с выражениями функций и лексически связывает значение this (не связывает свои собственные this, arguments, super или new.target). Стрелочные функции всегда анонимны. Эти функциональные выражения лучше всего подходят для функций, не являющихся методами, и их нельзя использовать в качестве конструкторов.

oddRaven
источник
Примечание: не поддерживается в IE
Луис Лавьери
1

Для всех, кто задается вопросом, почему их функция толстой стрелки игнорирует [, thisArg], например, почему

["DOG", "CAT", "DOG"].filter(animal => animal === this, "DOG") возвращается []

это потому, что thisвнутри этих стрелочных функций связываются, когда функция создается, и им присваивается значение thisв более широкой охватывающей области, поэтому thisArgаргумент игнорируется. Я довольно легко обошел это, объявив новую переменную в родительской области:

let bestPet = "DOG"; ["DOG", "CAT", "DOG"].filter(animal => animal === bestPet); => ["DOG", "DOG"]

Вот ссылка на дополнительную литературу: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this

Дж. Кеттлер
источник
0

на основе ответа oddRaven и https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Я сделал это двумя разными способами. 1) с использованием функционального способа. 2) встроенным способом.

//Here  is sample codes : 

var templateList   = [
{ name: "name1", index: 1, dimension: 1 }  ,
{ name: "name2", index: 2, dimension: 1 }  ,
{ name: "name3", index: 3, dimension: 2 }  ];


//Method 1) using function : 

function getDimension1(obj) {
                if (obj.dimension === 1) // This is hardcoded . 
                    return true;
                else return false;
            } 

var tl = templateList.filter(getDimension1); // it will return 2 results. 1st and 2nd objects. 
console.log(tl) ;

//Method 2) using inline way 
var tl3 = templateList.filter(element => element.index === 1 || element.dimension === 2  ); 
// it will return 1st and 3rd objects 
console.log(tl3) ;

Ашиш
источник
0

Есть простой способ использовать функцию фильтра, получить доступ ко всем параметрам и не усложнять его.

Если thisArg обратного вызова не установлен в другую область видимости, фильтр не создает свою собственную область видимости, и мы можем получить доступ к параметрам в текущей области. Мы можем установить this, чтобы определить другую область видимости, чтобы при необходимости получить доступ к другим значениям, но по умолчанию она установлена ​​в ту область, из которой вызывается. Вы можете увидеть, как это используется для областей видимости Angular в этом стеке .

Использование indexOf противоречит цели фильтрации и увеличивает накладные расходы. Фильтр уже проходит через массив, так зачем нам снова перебирать его? Вместо этого мы можем сделать его простой чистой функцией .

Вот сценарий варианта использования в методе класса React, где состояние имеет массив с именем items , и с помощью фильтра мы можем проверить существующее состояние:

checkList = (item) => {  // we can access this param and globals within filter
  var result = this.state.filter(value => value === item); // returns array of matching items

  result.length ? return `${item} exists` : this.setState({
    items: items.push(item) // bad practice, but to keep it light
  });
}
DBrown
источник