Warum sollte Object.prototype.hasOwnProperty.call (myObj, prop) anstelle von myObj.hasOwnProperty (prop) verwendet werden?

103

Wenn ich das richtig verstehe, erbt jedes Objekt in Javascript vom Objektprototyp, was bedeutet, dass jedes Objekt in Javascript über seine Prototypkette Zugriff auf die Funktion hasOwnProperty hat.

Beim Lesen des Quellcodes von require.js bin ich auf diese Funktion gestoßen:

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

hasOwnist ein Verweis auf Object.prototype.hasOwnProperty. Gibt es einen praktischen Unterschied zum Schreiben dieser Funktion als

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

Und da wir gerade dabei sind, warum definieren wir diese Funktion überhaupt? Handelt es sich nur um Verknüpfungen und lokales Caching des Eigenschaftszugriffs für (geringfügige) Leistungssteigerungen, oder fehlen mir Fälle, in denen hasOwnProperty für Objekte verwendet werden kann, die diese Methode nicht haben?

timkg
quelle

Antworten:

109

Gibt es einen praktischen Unterschied [zwischen meinen Beispielen]?

Der Benutzer hat möglicherweise ein JavaScript-Objekt erstellt Object.create(null), das eine null [[Prototype]]Kette hat und daher nicht hasOwnProperty()verfügbar ist. Die Verwendung Ihres zweiten Formulars funktioniert aus diesem Grund nicht.

Es ist auch ein sicherer Hinweis auf Object.prototype.hasOwnProperty()(und auch kürzer).

Sie können sich vorstellen, dass jemand getan hat ...

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

Was einen hasProp(someObject)Fehler verursachen würde, wenn es wie Ihr zweites Beispiel implementiert worden wäre (es würde diese Methode direkt auf dem Objekt finden und diese aufrufen, anstatt an delegiert zu werden Object.prototype.hasOwnProperty).

Es ist jedoch weniger wahrscheinlich, dass jemand die Object.prototype.hasOwnPropertyReferenz überschrieben hat .

Und da wir gerade dabei sind, warum definieren wir diese Funktion überhaupt?

Siehe oben.

Ist es nur eine Frage von Verknüpfungen und lokalem Caching des Immobilienzugriffs für (leichte) Leistungssteigerungen ...

Theoretisch mag es schneller gehen , da die [[Prototype]]Kette nicht befolgt werden muss, aber ich vermute, dass dies vernachlässigbar ist und nicht der Grund, warum die Implementierung der Grund dafür ist.

... oder fehlen mir Fälle, in denen hasOwnPropertyObjekte verwendet werden könnten, die diese Methode nicht haben?

hasOwnProperty()existiert am Object.prototype, kann aber überschrieben werden. Jedes native JavaScript-Objekt (aber Host-Objekte können dies nicht garantieren) siehe RobGs ausführliche Erklärung ) hat Object.prototypeals letztes Objekt in der Kette zuvor null(außer natürlich für das von zurückgegebene Objekt Object.create(null)).

Alex
quelle
Ihre Logik ist wahrscheinlich richtig, aber ich denke, Sie sind freundlich. Wenn die Autoren von require.js der Meinung sind, dass hasOwnProperty möglicherweise überschrieben wurde (was äußerst unwahrscheinlich ist), sollten sie alle integrierten Methoden auf diese Weise aufrufen (möglicherweise auch).
RobG
@Periback Wirklich? Ich war mir ziemlich sicher, dass es das unterstützt.
Alex
ES6-Verknüpfung bei häufiger Verwendung. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Wenn ich das richtig verstehe, erbt jedes Objekt in Javascript vom Objektprototyp

Es mag wie Haarspalterei erscheinen, aber es gibt einen Unterschied zwischen Javascript (der Oberbegriff für ECMAScript-Implementierungen) und ECMAScript (die Sprache, die für Javascript-Implementierungen verwendet wird). Es ist ECMAScript, das ein Vererbungsschema definiert, nicht Javascript, sodass nur native ECMAScript-Objekte dieses Vererbungsschema implementieren müssen.

Ein laufendes Javascript-Programm besteht mindestens aus den integrierten ECMAScript-Objekten (Objekt, Funktion, Nummer usw.) und wahrscheinlich einigen nativen Objekten (z. B. Funktionen). Möglicherweise sind auch einige Hostobjekte vorhanden (z. B. DOM-Objekte in einem Browser oder andere Objekte in anderen Hostumgebungen).

Während integrierte und native Objekte das in ECMA-262 definierte Vererbungsschema implementieren müssen, tun dies Host-Objekte nicht. Daher sind nicht alle Objekte in einer JavaScript - Umgebung muss von erben Object.prototype . Beispielsweise lösen Hostobjekte im IE, die als ActiveX-Objekte implementiert sind, Fehler aus, wenn sie als native Objekte behandelt werden (daher wird try..catch zum Initialisieren von MS XMLHttpRequest-Objekten verwendet). Einige DOM-Objekte (wie NodeLists im IE im Quirks-Modus), die an Array-Methoden übergeben werden, werfen Fehler auf. DOM-Objekte in IE 8 und niedriger haben kein ECMAScript-ähnliches Vererbungsschema usw.

Daher sollte nicht davon ausgegangen werden, dass alle Objekte in einer Javascript-Umgebung von Object.prototype erben.

Dies bedeutet, dass jedes Objekt in Javascript über seine Prototypkette Zugriff auf die Funktion hasOwnProperty hat

Dies gilt zumindest nicht für bestimmte Hostobjekte im IE im Mackenmodus (und IE 8 und niedriger immer).

Vor diesem Hintergrund lohnt es sich zu überlegen, warum ein Objekt möglicherweise über eine eigene hasOwnProperty- Methode verfügt und ob es ratsam ist, eine andere aufzurufen ratsam ist , stattdessen hasOwnProperty- Methode aufzurufen , ohne vorher zu testen, ob dies eine gute Idee ist oder nicht.

Bearbeiten

Ich vermute, dass der Grund für die Verwendung darin Object.prototype.hasOwnProperty.callbesteht, dass Host-Objekte in einigen Browsern keine hasOwnProperty- Methode haben. Die Verwendung von call und der integrierten Methode ist eine Alternative. Dies generisch zu tun, scheint jedoch aus den oben genannten Gründen keine gute Idee zu sein.

Wo Host - Objekte betroffen sind, die in kann Operator Objekte allgemein Test verwendet werden, zB

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

Eine Alternative (getestet in IE6 und anderen):

function ownProp(o, prop) {

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

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

Auf diese Weise rufen Sie nur die integrierte hasOwnProperty auf wo das Objekt sie nicht hat (geerbt oder anderweitig).

Wenn jedoch ein Objekt keinen hat hasOwnPropertyMethode, ist es wahrscheinlich genauso geeignet die die Verwendung in Bediener als das Objekt mit hohen Wahrscheinlichkeit kein Erbteil Schema hat und alle Eigenschaften sind auf dem Objekt (das ist nur eine Annahme , obwohl), zB die In Operator ist eine gängige (und scheinbar erfolgreiche) Methode zum Testen der DOM-Objektunterstützung für Eigenschaften.

RobG
quelle
Vielen Dank. Object.prototype.hasOwnProperty.call (o, 'bar') funktioniert in FF 18.0 nicht (zumindest in meinem Fall). Also habe ich mich für ('bar' in o) entschieden - und es hat geholfen.
Max
@Max inführt keine hasOwnProperty()Suche durch. Ich vermute, dass die von Ihnen gesuchte Eigenschaft in der Prototypenkette vorhanden war.
alex
Dies ist ein interessantes Beispiel von eslint.org/docs/rules/no-prototype-builtins : Zum Beispiel wäre es für einen Webserver unsicher, JSON-Eingaben von einem Client zu analysieren und hasOwnPropertydas resultierende Objekt direkt aufzurufen , da dies ein böswilliger Client könnte Senden Sie einen JSON-Wert wie {"hasOwnProperty": 1}und führen Sie zum Absturz des Servers.
naught101
Sicher, aber es wäre ratsam, von Clients bereitgestelltes JSON mit einem JSON-Schema zu testen oder zu validieren, um solche Probleme zu vermeiden, selbst wenn Sie sich nur um die Datenqualität kümmern. Und es sollte nicht dazu führen, dass der Server abstürzt. :-)
RobG
8

JavaScript schützt den Eigenschaftsnamen hasOwnProperty nicht

Wenn die Möglichkeit besteht, dass ein Objekt eine Eigenschaft mit diesem Namen hat, muss eine externe hasOwnProperty verwendet werden, um korrekte Ergebnisse zu erhalten:

Sie können die folgenden Codefragmente kopieren und in die Browserkonsole einfügen, um ein besseres Verständnis zu erhalten

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

Gibt immer false zurück

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

Verwenden Sie die hasOwnProperty eines anderen Objekts und rufen Sie sie mit diesem Set auf foo auf

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

Zu diesem Zweck kann auch die Eigenschaft hasOwnProperty aus dem Object- Prototyp verwendet werden

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
quelle
1
Der Punkt, den Sie machen, wurde bereits in der akzeptierten Antwort gemacht , außer dass dort die Überschreibung der hasOwnPropertyRetouren erfolgt true.
Louis
1

Die Informationen in beiden vorhandenen Antworten sind genau richtig. Die Verwendung von:

('propertyName' in obj)

wird ein paar mal erwähnt. Es ist zu beachten, dass die hasOwnPropertyImplementierungen nur dann true zurückgeben, wenn die Eigenschaft direkt auf dem zu testenden Objekt enthalten ist.

Der inBediener wird auch die Prototypenkette überprüfen.

Dies bedeutet, dass Instanzeigenschaften true zurückgeben, wenn sie an hasOwnPropertywhere übergeben werden, da die Prototypeigenschaften false zurückgeben.

Wenn Sie den inOperator verwenden, geben sowohl die Instanz- als auch die Prototyp-Eigenschaften true zurück.

Steve
quelle