Зачем использовать Object.prototype.hasOwnProperty.call (myObj, prop) вместо myObj.hasOwnProperty (prop)?

104

Если я правильно понимаю, каждый объект в Javascript наследуется от прототипа Object, что означает, что каждый объект в Javascript имеет доступ к функции hasOwnProperty через свою цепочку прототипов.

Читая исходный код require.js, я наткнулся на эту функцию:

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

hasOwnэто ссылка на Object.prototype.hasOwnProperty. Есть ли практическая разница в написании этой функции как

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

И раз уж мы находимся, зачем мы вообще определяем эту функцию? Это просто вопрос ярлыков и локального кэширования доступа к свойствам для (небольшого) повышения производительности, или мне не хватает каких-либо случаев, когда hasOwnProperty может использоваться для объектов, у которых нет этого метода?

Timkg
источник

Ответы:

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)).

Алекс
источник
Ваша логика, вероятно, верна, но я думаю, что вы добрые. Если авторы require.js думают, что hasOwnProperty мог быть переопределен (что крайне маловероятно), то им следует таким образом вызывать все встроенные методы (возможно, они так и делают).
RobG
@Periback Правда? Я был почти уверен, что он поддерживал это.
Alex
Ярлык ES6, если используется часто. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Ричард
15

Если я правильно понимаю, каждый объект в Javascript наследуется от прототипа объекта

Это может показаться разделением волос, но есть разница между 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 , альтернативой является использование call и встроенного метода. Однако делать это в целом не кажется хорошей идеей по причинам, указанным выше.

Что касается хост-объектов, оператор 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, поскольку объект, скорее всего, не имеет схемы наследования, и все свойства находятся в объекте (хотя это всего лишь предположение), например Оператор 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'
};

Всегда возвращает false

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

Используйте hasOwnProperty другого объекта и вызовите его с этим значением foo

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

Также для этой цели можно использовать свойство hasOwnProperty из прототипа объекта.

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Судхаршан
источник
1
То, что вы делаете, уже было сделано в принятом ответе , за исключением того, что есть переопределение hasOwnPropertyвозвратов true.
Луи
1

Информация, представленная в обоих существующих ответах, верна. Однако использование:

('propertyName' in obj)

упоминается несколько раз. Следует отметить, что hasOwnPropertyреализации вернут истину только в том случае, если свойство непосредственно содержится в тестируемом объекте.

inОператор проверит вниз по цепочке прототипов тоже.

Это означает, что свойства экземпляра вернут true при передаче, hasOwnPropertyа свойства прототипа вернут false.

При использовании inоператора свойства экземпляра и прототипа вернут true.

Стив
источник