Pourquoi utiliser Object.prototype.hasOwnProperty.call (myObj, prop) au lieu de myObj.hasOwnProperty (prop)?

104

Si je comprends bien, chaque objet en Javascript hérite du prototype Object, ce qui signifie que chaque objet en Javascript a accès à la fonction hasOwnProperty via sa chaîne de prototypes.

En lisant le code source de require.js, je suis tombé sur cette fonction:

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

hasOwnest une référence à Object.prototype.hasOwnProperty. Y a-t-il une différence pratique à écrire cette fonction comme

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

Et puisque nous y sommes, pourquoi définissons-nous cette fonction? Est-ce juste une question de raccourcis et de mise en cache locale de l'accès aux propriétés pour de (légers) gains de performances, ou est-ce que je manque des cas où hasOwnProperty pourrait être utilisé sur des objets qui n'ont pas cette méthode?

timkg
la source

Réponses:

109

Y a-t-il une différence pratique [entre mes exemples]?

L'utilisateur peut avoir un objet JavaScript créé avec Object.create(null), qui aura une null [[Prototype]]chaîne, et donc ne sera pas hasOwnProperty()disponible dessus. L'utilisation de votre deuxième formulaire échouerait pour cette raison.

C'est aussi une référence plus sûre Object.prototype.hasOwnProperty()(et aussi plus courte).

Vous pouvez imaginer que quelqu'un a pu faire ...

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

Ce qui ferait un hasProp(someObject)échec s'il avait été implémenté comme votre deuxième exemple (il trouverait cette méthode directement sur l'objet et l'invoquerait, au lieu d'être délégué à Object.prototype.hasOwnProperty).

Mais il est moins probable que quelqu'un ait remplacé la Object.prototype.hasOwnPropertyréférence.

Et puisque nous y sommes, pourquoi définissons-nous cette fonction?

Voir au dessus.

Est-ce juste une question de raccourcis et de mise en cache locale de l'accès aux propriétés pour de (légers) gains de performances ...

Cela peut le rendre plus rapide en théorie, car la [[Prototype]]chaîne n'a pas à être suivie, mais je soupçonne que cela est négligeable et non la raison pour laquelle la mise en œuvre est la raison pour laquelle elle l'est.

... ou est-ce que je manque des cas où hasOwnPropertypourraient être utilisés sur des objets qui n'ont pas cette méthode?

hasOwnProperty()existe Object.prototype, mais peut être remplacé. Chaque objet JavaScript natif (mais les objets hôtes ne sont pas garantis de suivre cela, voir l'explication détaillée de RobG ) a Object.prototypecomme dernier objet sur la chaîne avant null(sauf bien sûr pour l'objet renvoyé par Object.create(null)).

Alex
la source
Votre logique est probablement correcte, mais je pense que vous êtes gentil. Si les auteurs de require.js pensent que hasOwnProperty a pu être surchargé (ce qui est extrêmement improbable), alors ils devraient appeler toutes les méthodes intégrées de cette façon (peut-être le font-ils).
RobG
@Periback Vraiment? J'étais à peu près sûr qu'il le soutenait.
alex
Raccourci ES6 s'il est utilisé souvent. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Si je comprends bien, chaque objet en Javascript hérite du prototype Object

Cela peut sembler couper les cheveux, mais il y a une différence entre javascript (le terme générique pour les implémentations ECMAScript) et ECMAScript (le langage utilisé pour les implémentations javascript). C'est ECMAScript qui définit un schéma d'héritage, et non javascript, donc seuls les objets ECMAScript natifs doivent implémenter ce schéma d'héritage.

Un programme javascript en cours d'exécution comprend au moins les objets ECMAScript intégrés (objet, fonction, nombre, etc.) et probablement certains objets natifs (par exemple des fonctions). Il peut également avoir des objets hôtes (tels que des objets DOM dans un navigateur ou d'autres objets dans d'autres environnements hôtes).

Alors que les objets intégrés et natifs doivent implémenter le schéma d'héritage défini dans ECMA-262, les objets hôtes ne le font pas. Par conséquent, tous les objets d'un environnement javascript ne doivent pas hériter d' Object.prototype . Par exemple, les objets hôte dans IE implémentés en tant qu'objets ActiveX généreront des erreurs s'ils sont traités comme des objets natifs (d'où la raison pour laquelle try..catch est utilisé pour initialiser les objets MS XMLHttpRequest). Certains objets DOM (comme NodeLists dans IE en mode quirks), s'ils sont passés aux méthodes Array, génèrent des erreurs, les objets DOM dans IE 8 et les versions antérieures n'ont pas de schéma d'héritage de type ECMAScript, etc.

Par conséquent, il ne faut pas supposer que tous les objets dans un environnement javascript héritent d'Object.prototype.

ce qui signifie que chaque objet en Javascript a accès à la fonction hasOwnProperty via sa chaîne de prototypes

Ce qui n'est pas vrai pour certains objets hôtes dans IE en mode quirks (et IE 8 et inférieur toujours) au moins.

Compte tenu de ce qui précède, il vaut la peine de se demander pourquoi un objet peut avoir sa propre méthode hasOwnProperty et l'opportunité d'appeler une autre méthode hasOwnProperty à la place sans tester au préalable si c'est une bonne idée ou non.

Éditer

Je soupçonne que la raison de l'utilisation Object.prototype.hasOwnProperty.callest que dans certains navigateurs, les objets hôtes n'ont pas de méthode hasOwnProperty , l'utilisation de call et la méthode intégrée est une alternative. Cependant, le faire de manière générique ne semble pas être une bonne idée pour les raisons mentionnées ci-dessus.

En ce qui concerne les objets hôtes, l' opérateur in peut être utilisé pour tester les propriétés en général, par exemple

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

Une alternative (testée dans IE6 et autres):

function ownProp(o, prop) {

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

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

De cette façon, vous n'appelez spécifiquement le hasOwnProperty intégré que là où l'objet ne l'a pas (hérité ou autre).

Cependant, si un objet n'a pas de hasOwnPropertyméthode, il est probablement tout aussi approprié d'utiliser l' opérateur in car l'objet n'a probablement pas de schéma d'héritage et toutes les propriétés sont sur l'objet (ce n'est cependant qu'une hypothèse), par exemple le in est une manière courante (et apparemment réussie) de tester la prise en charge des objets DOM pour les propriétés.

RobG
la source
Merci. Object.prototype.hasOwnProperty.call (o, 'bar') ne fonctionne pas dans FF 18.0 (du moins dans mon cas). J'ai donc décidé d'utiliser ('bar' en o) - et cela m'a aidé.
Max
@Max inn'effectue pas de hasOwnProperty()recherche, je soupçonne que la propriété que vous recherchiez existait sur la chaîne de prototypes.
alex le
Ceci est un exemple intéressant de eslint.org/docs/rules/no-prototype-builtins : par exemple, il serait dangereux pour un serveur Web d'analyser l'entrée JSON d'un client et d'appeler hasOwnPropertydirectement l'objet résultant, car un client malveillant pourrait envoyer une valeur JSON comme {"hasOwnProperty": 1}et provoquer le plantage du serveur.
naught101
Bien sûr, mais il serait sage de tester ou de valider tout JSON fourni par le client avec un schéma JSON pour éviter de tels problèmes, même si votre préoccupation était uniquement la qualité des données. Et cela ne devrait pas faire planter le serveur. :-)
RobG
8

JavaScript ne protège pas le nom de la propriété hasOwnProperty

S'il est possible qu'un objet ait une propriété avec ce nom, il est nécessaire d'utiliser un hasOwnProperty externe pour obtenir des résultats corrects:

Vous pouvez copier-coller les extraits de code ci-dessous dans la console de votre navigateur pour mieux comprendre

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

Renvoie toujours false

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

Utilisez hasOwnProperty d'un autre objet et appelez-le avec cet ensemble à foo

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

Il est également possible d'utiliser la propriété hasOwnProperty du prototype Object à cette fin

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
la source
1
Le point que vous faites valoir a déjà été fait dans la réponse acceptée , sauf qu'il y a le remplacement des hasOwnPropertyretours true.
Louis
1

Les informations données dans les deux réponses existantes sont parfaites. Cependant, l'utilisation de:

('propertyName' in obj)

est mentionné à quelques reprises. Il est à noter que les hasOwnPropertyimplémentations ne renverront true que si la propriété est directement contenue sur l'objet testé.

L' inopérateur inspectera également la chaîne du prototype.

Cela signifie que les propriétés de l'instance renverront true lorsqu'elles seront transmises à hasOwnPropertyoù les propriétés du prototype renverront false.

En utilisant l' inopérateur, les propriétés d'instance et de prototype renverront true.

Steve
la source