Как определить метод в javascript на Array.prototype и Object.prototype, чтобы он не появлялся внутри цикла

Я хочу определить вспомогательные методы на 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?

Ответ 1

Это довольно просто: не используйте циклы for-in с массивами. Обвините всех, кто так поступает, - вот хороший фрагмент, который нужно рассказать им во время разработки.

Конечно, если кто-то выполняет перечисление в универсальной функции и не знает, получает ли он массив, простой объект или объект с пользовательским прототипом, вы можете использовать hasOwnProperty следующим образом:

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

Обратите внимание на явное использование Object.prototype для получения функции - могут быть объекты, которые ее перезаписывают (особенно в картах данных, значение может даже не быть функцией), объекты, которые его не поддерживают, или объекты, которые не наследуются от Object.prototype вообще. Смотрите также здесь.

Тем не менее, только автор сценария, который знает о проблеме, отфильтрует все свои циклы for-in-loop - а некоторые делают это только потому, что это рекомендуется - и делает это в основном неправильно, ему следовало бы использовать вместо этого итерацию массива for-loop. Но наша проблема - это те авторы, которые об этом не знают.

Интересным, но только для 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
    }
});

Ответ 2

В зависимости от ваших ограничений:

// 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/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty

Ответ 3

Это потому, что нужно проверить hasOwnProperty:

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

Теперь этот журнал 1, 2, 3.

Ответ 4

Приведенные выше ответы упускают точку:

enumerable... по умолчанию false (мдн)

Поэтому простое использование Object.defineProperty(Array.prototype, 'myFunc' myFunc) вместо Array.prototype.myFunc = myFunc решит проблему.