如何在Array.prototype和Object.prototype中定义方法,以使其不出现在for in循环中?

20

我希望在Array.prototype和Object.prototype上定义一些辅助方法。我的当前计划是像这样做:

Array.prototype.find = function(testFun) {
   // code to find element in array
};

为了能够做到这一点:

var arr = [1, 2, 3];
var found = arr.find(function(el) { return el > 2; });

它运行良好,但如果我在for in循环中遍历数组,则方法会显示为值:

for (var prop in arr) { console.log(prop); }
// prints out:
// 1
// 2
// 3
// find

这将会影响到任何依赖于for in仅显示值(特别是对象)的人。后来版本的JavaScript已经内置了.map和.filter函数到数组中,但这些函数不会出现在for in循环中。我该如何创建更多像这样的方法,使其不会在for in循环中显示?


2
这就是“不要在数组上使用for-in循环”的原因! - Bergi
2
不要修改你没有所有权的对象。 (http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/) - Evan Davis
4个回答

27
很简单:不要使用for-in 循环遍历数组。责怪其他人这样做 - 这里有一个好的代码片段,可以在开发期间告诉他们。

当然,如果在通用函数中进行枚举,并且不知道是否获得数组、纯对象或具有自定义原型的对象,则可以像这样使用hasOwnProperty

for (var prop in anyObj )
    if (Object.prototype.hasOwnProperty.call(anyObj, prop))
        // do something

请注意显式使用Object.prototype来获取函数——可能会有对象覆盖它(特别是在数据映射中,该值甚至可能不是函数),不支持它的对象或根本不继承自Object.prototype的对象。另请参见这里

然而,只有意识到问题的脚本作者才会过滤他所有的for-in循环,并且一些人之所以这样做仅仅是因为建议这样做,而且他们大多数情况下都做得不对,他们应该使用for循环数组迭代。但我们的问题是那些不知道这个问题的作者。

一个有趣的、但只在Mozilla中适用的方法是通过__iterate__覆盖数组上的枚举行为,如此处演示

幸运的是,EcmaScript 5.1 允许我们将属性设置为不可枚举。当然,这在旧版浏览器中不受支持,但为什么要烦恼呢?我们需要使用es5-shims来获取所有很酷的高阶数组功能 :-) 使用defineProperty如下:

Object.defineProperty(Array.prototype, "find", {
    enumerable: false,
    writable: true,
    value: function(testFun) {
        // code to find element in array
    }
});

1
我喜欢将那个冗长的 hasOwnProperty 放在一个帮助函数中。拥有像 hop(anyObj, prop) 这样的简写使我不太可能使用你提到的较短但次优的替代方案。 - hugomg
当然,你可以定义 var hop = Function.prototype.call.bind(Object.prototype.hasOwnProperty),但由于普通程序员不应该需要它,所以我没有提到它。那些提供高度通用函数的库当然会在内部使用它。 - Bergi

7

根据您的限制:

// In EcmaScript 5 specs and browsers that support it you can use the Object.defineProperty
// to make it not enumerable set the enumerable property to false
Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,  // this will make it not iterable
    get: function(testFun) {
       // code to find element in array
    };
});

在这里阅读更多关于Object.defineProperty的内容 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

不,你不想为这个原型方法定义一个getter函数。看看我的答案... - Bergi
你不能相信其他人使用你的代码会进行 hasOwnProperty 检查,所以这是一种安全的方式。 - Greg Hornby

2
上面的答案忽略了一个重点:

可枚举性...默认为false。(mdn

因此,只需使用Object.defineProperty(Array.prototype,'myFunc' myFunc)而不是Array.prototype.myFunc = myFunc即可解决问题。

-1

这是因为必须检查hasOwnProperty

for (var prop in arr) { 
  if (arr.hasOwnProperty(prop)) { 
    console.log(prop) 
  }
}

现在这个日志输出了1、2、3。


bergi 的意思是,如果有人向 arr 或其原型添加了一个“hasOwnProperty”属性,那么这段代码就会出错。 - hugomg
真的是个糟糕的建议 - Alex Craft

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