Навіщо використовувати Object.prototype.hasOwnProperty.call (myObj, prop) замість myObj.hasOwnProperty (prop)?


104

Якщо я правильно розумію, кожен об'єкт у Javascript успадковується від прототипу Object, це означає, що кожен об'єкт у Javascript має доступ до функції hasOwnProperty через ланцюг прототипу.

Читаючи вихідний код Requ.js, я натрапив на цю функцію:

function hasProp(obj, prop) {
    return hasOwn.call(obj, prop);
}

hasOwnє посиланням на Object.prototype.hasOwnProperty. Чи є якась практична різниця в написанні цієї функції як

function hasProp(obj, prop) {
    return obj.hasOwnProperty(prop);
}

І оскільки ми в ньому, чому ми взагалі визначаємо цю функцію? Це лише питання ярликів та локального кешування доступу до властивостей для (незначного) підвищення продуктивності, чи я пропускаю випадки, коли hasOwnProperty може використовуватися для об'єктів, у яких немає цього методу?

Відповіді:


109

Чи є якась практична різниця [між моїми прикладами]?

У користувача може бути створений об’єкт JavaScript Object.create(null), який матиме null [[Prototype]]ланцюжок, і тому він не буде hasOwnProperty()доступний на ньому. Використання другої форми з цієї причини не вдасться.

Це також більш безпечне посилання на Object.prototype.hasOwnProperty()(а також коротше).

Ви можете уявити, що хтось, можливо, зробив ...

var someObject = {
    hasOwnProperty: function(lol) {
        return true;
    }
};

Що може призвести до hasProp(someObject)помилки, якби він був реалізований як ваш другий приклад (він знайшов би цей метод безпосередньо на об'єкті і попросив би його замість делегувати Object.prototype.hasOwnProperty).

Але менше шансів, що хтось перекриє Object.prototype.hasOwnPropertyпосилання.

І оскільки ми в ньому, чому ми взагалі визначаємо цю функцію?

Дивись вище.

Це лише питання ярликів та локального кешування доступу до власності для (незначного) підвищення продуктивності ...

Теоретично це може зробити це швидше , оскільки [[Prototype]]ланцюжок не повинен дотримуватися, але я підозрюю, що це незначно, і це не причина реалізації.

... чи я пропускаю випадки, коли hasOwnPropertyможна використовувати об’єкти, у яких немає цього методу?

hasOwnProperty()існує на Object.prototype, але може бути відмінено. Кожен власний об’єкт JavaScript (але хост-об’єкти не гарантовано дотримуються цього, див. Поглиблене пояснення RobG ) є Object.prototypeсвоїм останнім об’єктом у ланцюжку раніше null(за винятком, звичайно, об'єкта, який повертається Object.create(null)).


Ваша логіка, ймовірно, правильна, але я думаю, ти добрий. Якщо автори Requ.js вважають, що hasOwnProperty може бути відмінено (що вкрай малоймовірно), тоді вони повинні викликати всі вбудовані методи таким чином (можливо, вони є).
RobG

@Пербек Справді? Я був майже впевнений, що це підтримав.
алекс

Ярлик ES6, якщо використовується часто. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Річард Айотт

15

Якщо я правильно розумію, кожен об'єкт у Javascript успадковується від прототипу Object

Це може здатися розщепленням волосся, але є різниця між javascript (загальним терміном для реалізації ECMAScript) та ECMAScript (мовою, що використовується для реалізації javascript). Саме ECMAScript визначає схему успадкування, а не javascript, тому лише власні об’єкти ECMAScript повинні реалізовувати цю схему успадкування.

Запущена програма javascript складається щонайменше із вбудованих об’єктів ECMAScript (об’єкт, функція, число тощо) та, можливо, деяких власних об’єктів (наприклад, функцій). Також можуть бути деякі хост-об'єкти (наприклад, DOM-об’єкти у браузері або інші об'єкти в інших середовищах хоста).

Хоча вбудовані та власні об'єкти повинні реалізовувати схему успадкування, визначену в ECMA-262, хост-об'єкти цього не виконують. Тому не всі об'єкти в середовищі javascript повинні успадковувати від Object.prototype . Наприклад, хост-об'єкти в IE, реалізовані як об'єкти ActiveX, видаватимуть помилки, якщо їх розглядатимуть як власні об'єкти (отже, чому try..catch використовується для ініціалізації об'єктів MS XMLHttpRequest). Деякі об'єкти DOM (наприклад, NodeLists в IE в режимі примх), якщо передані методам Array, будуть видаляти помилки, DOM-об'єкти в IE 8 і нижче не мають схеми успадкування, подібної ECMAScript тощо.

Тому не слід вважати, що всі об'єкти в середовищі javascript успадковуються від Object.prototype.

що означає, що кожен об'єкт у Javascript має доступ до функції hasOwnProperty через ланцюг прототипу

Що не відповідає правилам певних об'єктів хостингу в IE в режимі примх (і IE 8 і нижче завжди).

З огляду на вищесказане, варто задуматися, чому об’єкт може мати власний метод hasOwnProperty та доцільність виклику замість нього іншого методу hasOwnProperty, не попередньо перевіряючи, чи це гарна ідея чи ні.

Редагувати

Я підозрюю, що причиною використання Object.prototype.hasOwnProperty.callє те, що в деяких браузерах хост-об’єкти не мають методу hasOwnProperty , використовуючи виклик, а вбудований метод є альтернативою. Однак робити це загалом не здається гарною ідеєю з причин, зазначених вище.

Що стосується хост-об'єктів, оператор in може бути використаний для перевірки властивостей, наприклад

var o = document.getElementsByTagName('foo');

// false in most browsers, throws an error in IE 6, and probably 7 and 8
o.hasOwnProperty('bar');

// false in all browsers
('bar' in o);

// false (in all browsers? Do some throw errors?)
Object.prototype.hasOwnProperty.call(o, 'bar');

Альтернатива (перевірена в IE6 та інших):

function ownProp(o, prop) {

  if ('hasOwnProperty' in o) {
    return o.hasOwnProperty(prop);

  } else {
    return Object.prototype.hasOwnProperty.call(o, prop);
  }
}

Таким чином, ви тільки викликуєте вбудовану hasOwnProperty там, де у об'єкта його немає (успадковане чи інше).

Однак якщо об’єкт не має hasOwnPropertyметоду, він, мабуть, так само підходить використовувати оператор in, оскільки, ймовірно, об’єкт не має схеми успадкування, і всі властивості є на об'єкті (хоча це лише припущення), наприклад в операторі є поширеним (і, здавалося б, успішним) способом тестування підтримки DOM-об'єктів для властивостей.


Дякую. Object.prototype.hasOwnProperty.call (o, 'bar') не працює у FF 18.0 (принаймні в моєму випадку). Тому я вирішив використовувати ('bar' in o) - і це допомогло.
Макс

@Max inне здійснює hasOwnProperty()пошук, я підозрюю, що властивість, яку ви шукали, існувала в прототипі ланцюга.
alex

Це цікавий приклад із eslint.org/docs/rules/no-prototype-builtins : Наприклад, було б небезпечно для веб-сервера проаналізувати вхід JSON від клієнта та зателефонуватиhasOwnProperty безпосередньо на об'єкт, що виникає, тому що зловмисний клієнт може надіслати значення типу JSON як {"hasOwnProperty": 1}і призведе до збою сервера.
naught101

Звичайно, але було б розумно перевірити або перевірити будь-який клієнтський JSON за допомогою схеми JSON, щоб запобігти подібним проблемам, навіть якщо ваша проблема стосувалася лише якості даних. І це не повинно викликати збій сервера. :-)
RobG

8

JavaScript не захищає ім'я властивості hasOwnProperty

Якщо існує можливість, що об’єкт може мати властивість з цим іменем, для отримання правильних результатів необхідно використовувати зовнішню hasOwnProperty:

Ви можете скопіювати вставити наведені нижче фрагменти коду на консоль браузера, щоб краще зрозуміти

var foo = {
  hasOwnProperty: function() {
    return false;
  },
  bar: 'I belong to foo'
};

Завжди повертає помилкове

foo.hasOwnProperty('bar'); // false

Використовуйте hasOwnProperty іншого об'єкта і називайте його з цим набором до foo

({}).hasOwnProperty.call(foo, 'bar'); // true

Також можливо використовувати властивість hasOwnProperty з Object прототипу

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true

1
Точка, яку ви робите, вже зафіксована у прийнятій відповіді , за винятком того, що там перевищення hasOwnPropertyповернень true.
Луї

1

Інформація, надана в обох існуючих відповідях, знаходиться на місці. Однак використання:

('propertyName' in obj)

згадується кілька разів. Слід зазначити, що hasOwnPropertyреалізація поверне істину лише у тому випадку, якщо властивість безпосередньо міститься на тестованому об'єкті.

inОператор перевірить вниз по ланцюжку прототипів теж.

Це означає, що властивості екземпляра повернуть істинне, коли його передадуть туди, hasOwnPropertyде властивості прототипу повернуть помилкові.

Використання inоператора як властивостей екземпляра, так і прототипу поверне вірно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.