如何在 Array.prototype 和 Object.prototype 上的 javascript 中定义方法,使其不会出现在 for in 循环中

Posted

技术标签:

【中文标题】如何在 Array.prototype 和 Object.prototype 上的 javascript 中定义方法,使其不会出现在 for in 循环中【英文标题】:How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop 【发布时间】:2012-10-29 01:55:42 【问题描述】:

我想在 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 来显示值的人(尤其是在对象上)。更高版本的 .map 和 .filter 函数内置在数组中,但这些函数不会出现在 for in 循环中。如何创建更多不会出现在 for in 循环中的方法?

【问题讨论】:

这就是不在数组上使用for-in循环的原因 Don't modify objects you don't own. 【参考方案1】:

以上答案漏掉了一点:

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

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

【讨论】:

【参考方案2】:

这很简单:不要使用for-in loops with Arrays。责备所有这样做的人 - here is a nice snippet 在开发过程中告诉他们。

当然,如果一个人在泛型函数中进行枚举,但不知道他得到的是数组、普通对象还是具有自定义原型的对象,你可以像这样使用hasOwnProperty

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

注意显式使用 Object.prototype 来获取函数 - 可能有对象覆盖它(特别是在数据映射中,值甚至可能不是函数)、do not support it 的对象或没有的对象完全继承自 Object.prototype。另见here。

然而,只有意识到这个问题的脚本作者才会过滤他所有的 for-in-loops - 有些只是因为it gets recommended 而这样做 - 而且大部分都是错误的,他应该使用 for-loop 数组而是迭代。但我们的问题是那些不知道它的作者。

一种有趣但仅限 Mozilla 的方法是通过__iterate__ 将数组上的枚举行为覆盖为demonstrated here。

幸运的是,EcmaScript 5.1 允许我们将属性设置为不可枚举。当然,这在旧浏览器中是不支持的,但何必呢?对于所有很酷的高阶数组,我们无论如何都需要使用es5-shims :-) 像这样使用defineProperty

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

【讨论】:

我喜欢将这么长的 hasOwnProperty 放在辅助函数中。像hop(anyObj, prop) 这样的速记使我不太可能使用您提到的较短但次优的替代方案之一。 当然,您可以定义var hop = Function.prototype.call.bind(Object.prototype.hasOwnProperty),但由于普通程序员永远不需要它,我没有提到这一点。当然,那些提供高度通用功能的库在内部使用它。【参考方案3】:

取决于您的限制:

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

在此处https://developer.mozilla.org/en-US/docs/javascript/Reference/Global_Objects/Object/defineProperty

了解有关 Object.defineProperty 的更多信息

【讨论】:

不,你不想为这个原型方法定义一个getter函数。看看我的回答... 你不能相信任何其他人使用你的代码会做 hasOwnProperty 检查,所以这是一种安全的方法。【参考方案4】:

这是因为必须检查hasOwnProperty

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

现在记录 1、2、3。

【讨论】:

bergi 试图说如果有人向 arr 或其原型添加“hasOwnProperty”属性,则此代码会中断。

以上是关于如何在 Array.prototype 和 Object.prototype 上的 javascript 中定义方法,使其不会出现在 for in 循环中的主要内容,如果未能解决你的问题,请参考以下文章

实现对象的 Map 函数类似 Array.prototype.map

js 判断值为Array or Object的方法

一道类数组的面试题

一道类数组的面试题

浅谈Array.prototype.slice.call(arguments,0)

Review系列前端经典题之对象调用数组方法