JavaScript:如何在for循环中隐藏原型方法?

50

假设我已经向 Array 类添加了一些原型方法:



Array.prototype.containsKey = function(obj) {
    for(var key in this)
        if (key == obj) return true;
    return false;
}

Array.prototype.containsValue = function(obj) {
    for(var key in this)
        if (this[key] == obj) return true;
    return false;
}

然后我创建了一个关联数组,并尝试循环遍历它的键:



var arr = new Array();
arr['One'] = 1;
arr['Two'] = 2;
arr['Three'] = 3;

for(var key in arr)
   alert(key);

这将返回五个条目:

  - One
  - Two
  - Three
  - containsKey
  - containsValue

但我只希望(预期?)得到三个。我的方法是否正确?是否有一种方法可以“隐藏”原型方法?还是我应该采取不同的方法?


请参考此回答 - Bergi
7个回答

63

通过使原型方法不可枚举,您可以从另一端实现所需的结果:

Object.defineProperty(Array.prototype, "containsKey", {
  enumerable: false,
  value: function(obj) {
      for(var key in this)
        if (key == obj) return true;
      return false;
    }
});

如果您可以控制方法的定义,尤其是在库代码开发中,如果您无法控制其他人如何调用您的代码,那么通常情况下这样会更有效。


4
当你无法控制循环代码时,这将特别有用。 - Aurimas
不幸的是,defineProperty 只适用于 IE8 的 DOM 元素。http://kangax.github.io/compat-table/es5/#define-property-ie-note - Camilo Sanchez
4
这比在每个循环中检查hasOwnProperty要优雅得多。对于旧版浏览器,你可以自己编写实现,例如像这个:https://github.com/inexorabletash/polyfill/blob/master/es5.js#L71 - ZolaKt
是的,这是一种方法...但往往缺乏基本数组方法的浏览器也缺乏Object.defineProperty,所以这是一种竞争条件 ;) - rupps
如果想在自定义带有方法的对象上应用jQuery的$.param方法,这将非常有用。天真实现的add函数https://github.com/jquery/jquery/blob/1.12-stable/src/serialize.js#L58只是盲目地运行一次方法,这会导致“Uncaught TypeError: Cannot read property of undefined”错误。这个解决方案也很优雅! - Marecky

57
你可以在循环中使用JavaScript的hasOwnProperty方法来实现这一点,就像这样:
for(var key in arr) {
    if (arr.hasOwnProperty(key)) {
        ...
    }
}

参考文献:这篇YUI博客文章


15
具有讽刺意味的是,我试图创建hasOwnProperty的简短版本时遇到了这个问题:Object.prototype.has = Object.prototype.hasOwnProperty - Moss
1
显然,在对象上直接调用 hasOwnProperty 被认为是不安全的(参考)。安全的方式应该是 Object.prototype.hasOwnProperty.call(foo, "bar") - Thibaut

4
Javascript不支持你想象中的关联数组。使用"for (var i in ...)"可以获取对象的所有属性(数组只是另一个对象),这就是为什么你会看到其他已经被原型化的对象。正如文章建议的那样,你应该使用对象。 http://ajaxian.com/archives/javascript-associative-arrays-considered-harmful

var assoc = {'One' : 1, 'Two' : 2};
assoc['Three'] = 3;

for(var key in assoc)
   alert(key+' => '+assoc[key]);

3
你可以这样做:
for(var key in arr)
{
   if (typeof(arr[key]) == "function")
      continue;
   alert(key);
}

但这只是一个糟糕的解决方法。


1
您可以通过以下方式隐藏添加到原型中的方法,使其不在for-in循环中显示:
Object.defineProperty(Array.prototype, "containsKey", { enumerable: false });

在你添加方法后,"containsKey"是你所添加的方法。

1

方法1:使用Object.keys(不返回原型属性)和循环

Object.keys(arr); // ['One', 'Two', 'Three']
Object.keys(arr).forEach(key => console.log(key))
方法2: 在for循环中使用hasOwnProperty
 for(var key in arr) {
   if (arr.hasOwnProperty(key)) {
     ...
   }
 }

0

为了高效地迭代JavaScript数组,请使用forwhile循环。Nicholas Zakas在他的Tech Talk Speed Up Your JavaScript中讨论了迭代数组的最佳性能选项。

你最好选择像这样的东西:

for (var i = collection.length - 1; i >= 0; i--) {
  if (obj == collection[i]) return true;
}

这种方法之所以表现最佳,有以下几个原因:

  • 只分配了一个本地变量
  • 集合的length属性仅在循环初始化时访问一次
  • 每次迭代,将本地变量与常量(i >= 0)进行比较,而不是与另一个变量进行比较

1
根据他使用数组的方式,你不能像那样迭代,因为他没有使用数字作为键,所以当他调用collection['one']时,collection[1]将不存在。 - rezzif
这是向后迭代,v8 无法优化。您应该始终按正确的方式向前迭代。 - user9016207

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