JavaScript中的数组filter()方法与bind()函数的使用

8

我正在使用filter()数组帮助程序遍历数组中的一些对象。我的想法是创建一个动态过滤函数,使用bind()遍历数组中的对象,但是bind中的参数使用方式与我预期的不同。代码如下:

var products = [
  {name:"lettuce", type:"vegetable"},
  {name:"apple", type:"fruit"},
  {name:"carrot", type:"vegetable"},
  {name:"orange", type:"fruit"}
];

// this is the function used in filter()
function filterProducts(cat, product){
  return product.type === cat;
}

// new array
var vegetables = products.filter(filterProducts.bind(products, "vegetable"));

我假设filter助手会在绑定方法中将每个对象逐个传递,因此首先是产品,这代表回调函数中的this,然后是我想要检查每个对象的类型,最后是对象本身。
问题是:您是否建议这样做?我的意思是,这种方式可否被视为良好的实践,还是最好创建一个函数来过滤每种类型而不是像这样做?

你为什么想要使用bind函数呢? - Jean-François Beauchamp
这有点涉及个人观点... - lonewarrior556
为什么要绑定“products”?它已经作为第三个参数在“filter”回调函数中了。 - Mr_Green
2
如果自引用的参数没有被使用,为了清晰起见最好将其设置为null。 - lonewarrior556
@Jean-FrançoisBeauchamp 这个想法是只使用一个函数来过滤数组。在现实生活中,数组中的对象有几个键。但是使用 filter(myFunc) 只会将对象传递给函数,而不是我想要检查的键。 - Rodrigo
5个回答

11

考虑使用工厂函数:

var products = [
  {name:"lettuce", type:"vegetable"},
  {name:"apple", type:"fruit"},
  {name:"carrot", type:"vegetable"},
  {name:"orange", type:"fruit"}
];

function filterProducts(category) {
  // this is the function used in filter()
  return function filter(product) {
    return product.type === category;
  };
}

// new array
var vegetables = products.filter(filterProducts("vegetable"));

console.log(vegetables);

我会推荐使用这种模式而不是使用bind,因为在我看来它更容易理解一些。

澄清

如果您打算将filterProducts仅用作Array#filter()的工厂,则恭喜您,您已经完成了阅读此答案。如果您抱怨以下内容“不好看”:

// product to be validated
var product = {name:"lettuce", type:"vegetable"};

// validate that product is a vegetable
if (filterProduct('vegetable')(product)) {
  // code
}

那么请继续阅读。


工厂函数适合定义一类只有一两个关键变量不同的函数。在这种情况下,关键变量是category。如果您想要一个看起来很好并且只需一行代码的一次性函数,除非您是lodash爱好者或其他什么...否则你可能会很难找到一个,但对于这种情况,考虑使用以下代码而不是上面的"icky"代码块:

// product to be validated
var product = {name:"lettuce", type:"vegetable"};
// vegetable validator
var isVegetable = filterProduct('vegetable');

// validate that product is a vegetable
if (isVegetable(product)) {
  // code
}

当然,在某些情况下,bind是很好的选择,我肯定不会建议你不惜一切代价避免使用它,但在使用之前,你应该首先问自己是否真的有必要或者说这种做法是否更加简单易懂。

2
我也会选择这种方法...只是要提一下,"内部函数记得它被创建的环境!" - Rayon
这是一个相当不错的答案。我总是忘记我可以随意创建函数。 - JD E
与上面相同,filterProduct作为独立函数失去了所有价值,您永远无法仅调用filterProducts('orange', someObj)。使用常规绑定,filterProducts既可用作常规函数又可用于过滤器中。由于这是关于最佳实践的问题,这种方法也比简单绑定更糟糕。 - lonewarrior556
这可能是@Rodrigo实际想要的简化版本。但即使是这样,他在代码的某个地方可能想要检查当前产品是否为“蔬菜”类型。然后他可以在if语句中重复使用它。例如 if( filterProducts(prod, 'vegtable) )。也许不太可能,但将其放在构造函数中除了避免绑定和失去此选项外,没有其他好处。 - lonewarrior556
@lonewarrior556 如果这是你想要的,那很简单:if (filterProducts('vegetable')(product)) {... 所以你的论点还是没有意义的。 - Patrick Roberts
显示剩余5条评论

1
这实际上是一个观点问题。有些人比其他人更喜欢使用bind。使用es6箭头符号,在回调中编写函数可能比绑定更受青睐。
唯一需要注意的是,为了清晰起见,使用bind时,保留原文中的语句。
var sayName = function(name){
  console.log(name)
}
var sayTony = sayName.bind(null,'Tony')

如果函数内部没有使用第一个参数,最佳实践是将null作为第一个参数传入。

如果你真的注重可读性,可以更明确地声明另一个函数。

function filterProducts(cat, product){
  return product.type === cat;
}
var filterVegtables = filterProducts.bind(null, 'vegtable');

然后你可以做以下事情。
var vegetables = products.filter(filterVegtables);

是的,我从你的评论中注意到了。我会编辑代码以避免这种情况。谢谢! - Rodrigo

0
你对这样的东西感兴趣吗?
var products = [
  {name:"lettuce", type:"vegetable"},
  {name:"apple", type:"fruit"},
  {name:"carrot", type:"vegetable"},
  {name:"orange", type:"fruit"}
];

// this is the function used in filter()
function filterProducts(cat){
  return this.type === cat[0];
}
// new array
var veggies = products.filter(function(value){

  return filterProducts.bind(value, ['vegetable'])();
});
console.log(veggies);

我仍然使用了bind,但它变成了两行代码。现在我们的值是这个(所以数组中的每个元素),而我们的参数(cat)是一个数组,这就是为什么我抓取[0]的原因。

http://codepen.io/anon/pen/KrBaLB


2
但是这可以被替换为products.filter(function(value){ return value.type === 'vegetable';});,所以我真的看不到它的价值。 - JD E
你是在否定自己的答案吗? - lonewarrior556
答案是可行的,不是很糟糕。他想用bind和filter,所以我就这样做了。我认为,在filter函数中有一点点复杂度时使用bind有点毫无意义。 - JD E

0

Array.filter() 方法并不是那么神奇,它只会遍历数组并为每个条目调用函数。 别误会了 - 我喜欢优雅的解决方案,但你试图做的事情是没有意义的。 你应该做的是(此代码未经测试,仅供说明);

function filterCondition(obj, arg1, arg2, arg3,.. ) {
  //TODO: define your individual filter logic
}

var filter = function(products, arg1, arg2, arg3,.. ) {
  var filtered = [];
  for (var i= 0, l = products.length; i < l; i++) {
    var product = products[i];
    if (filterCondition(product, arg1, arg2, arg3,.. ))
      filtered.push(product);
  return filtered;
}

使用bind()意味着在绑定期间定义的作用域中执行函数..第一个参数。 因此,如果您需要绑定此函数,可以使用它:

filter.bind(products, arg1, arg2, arg3,..);

但这意味着,过滤函数在产品数组的作用域中执行。 因此,您必须修改参数:

var filter = function(arg1, arg2, arg3,.. ) {
  var filtered = [];
  for (var i= 0, l = this.length; i < l, i++) {
    var product = this[i];
    if (filterCondition(product, arg1, arg2, arg3,.. ))
      filtered.push(product);
  return filtered;
}

顺便说一句:Jean-François 在数组入口使用了这个.. 这并没有错,只是看起来有点混淆


-1
我相信这会给你想要的结果:
var products = [
  {name:"lettuce", type:"vegetable"},
  {name:"apple", type:"fruit"},
  {name:"carrot", type:"vegetable"},
  {name:"orange", type:"fruit"}
];

// this is the function used in filter()
function filterProducts(product) {
  return product.type === this.toString();
}

// new array
var vegetables = products.filter(filterProducts, "vegetable");

但我没有使用绑定函数。


不要过度使用和滥用 this 关键字,这样会显得代码质量较差且不必要。 - Rayon
@Rayon 也许你是对的,我不知道。如果你能解释一下你的推理,那就可以让事情更清晰了。 - Jean-François Beauchamp
@JDE 你怎么传递筛选器类型呢?你可以将其设置为全局变量,但这样不够简洁。 - Jean-François Beauchamp
当您在代码的其他部分使用filterProduct函数时,您正在将自己锁定为使用bind(或call,apply)。 - lonewarrior556
@lonewarrior556 使用 bind 或 call,apply... 你是什么意思? - Jean-François Beauchamp
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接