Po co używać Object.prototype.hasOwnProperty.call (myObj, prop) zamiast myObj.hasOwnProperty (prop)?

104

Jeśli dobrze rozumiem, każdy obiekt w JavaScript dziedziczy po prototypie Object, co oznacza, że ​​każdy obiekt w Javascript ma dostęp do funkcji hasOwnProperty poprzez swój łańcuch prototypów.

Podczas czytania kodu źródłowego require.js natknąłem się na tę funkcję:

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

hasOwnjest odniesieniem do Object.prototype.hasOwnProperty. Czy jest jakaś praktyczna różnica w zapisywaniu tej funkcji jako

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

A skoro już o tym mowa, dlaczego w ogóle definiujemy tę funkcję? Czy jest to tylko kwestia skrótów i lokalnego buforowania dostępu do właściwości w celu (niewielkiego) wzrostu wydajności, czy też brakuje mi przypadków, w których hasOwnProperty może być używane na obiektach, które nie mają tej metody?

timkg
źródło

Odpowiedzi:

109

Czy jest jakaś praktyczna różnica [między moimi przykładami]?

Użytkownik może mieć utworzony obiekt JavaScript za pomocą Object.create(null), który będzie miał null [[Prototype]]łańcuch, a zatem nie będzie hasOwnProperty()na nim dostępny. Z tego powodu korzystanie z drugiego formularza nie zadziała.

Jest to również bezpieczniejsze odniesienie do Object.prototype.hasOwnProperty()(a także krótsze).

Możesz sobie wyobrazić, że ktoś mógł zrobić ...

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

Co spowodowałoby hasProp(someObject)niepowodzenie, gdyby zostało zaimplementowane tak, jak w drugim przykładzie (znalazłby tę metodę bezpośrednio w obiekcie i wywołałby ją zamiast delegowania Object.prototype.hasOwnProperty).

Ale jest mniej prawdopodobne, że ktoś zastąpi Object.prototype.hasOwnPropertyodniesienie.

A skoro już o tym mowa, dlaczego w ogóle definiujemy tę funkcję?

Patrz wyżej.

Czy to tylko kwestia skrótów i lokalnego buforowania dostępu do własności w celu (niewielkiego) wzrostu wydajności ...

W teorii może to przyspieszyć , ponieważ [[Prototype]]łańcuch nie musi być przestrzegany, ale podejrzewam, że jest to pomijalne, a nie powodem, dla którego jest to implementacja.

... czy brakuje mi przypadków, w których hasOwnPropertymożna by użyć na obiektach, które nie mają tej metody?

hasOwnProperty()istnieje w dniu Object.prototype, ale można go zastąpić. Każdy natywny obiekt JavaScript (ale obiekty hosta nie gwarantują, że będzie to przestrzegać, zobacz szczegółowe wyjaśnienie RobG ) ma Object.prototypejako ostatni obiekt w łańcuchu wcześniej null(z wyjątkiem oczywiście obiektu zwróconego przez Object.create(null)).

Alex
źródło
Twoja logika jest prawdopodobnie poprawna, ale myślę, że jesteś miły. Jeśli autorzy require.js uważają, że hasOwnProperty mogło zostać nadpisane (co jest bardzo mało prawdopodobne), powinni w ten sposób wywoływać wszystkie wbudowane metody (być może tak jest).
RobG
@Periback Naprawdę? Byłem prawie pewien, że to obsługuje.
Alex
Skrót ES6, jeśli jest często używany. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Jeśli dobrze rozumiem, każdy obiekt w JavaScript dziedziczy po prototypie Object

Może się to wydawać rozszczepianiem włosów, ale istnieje różnica między javascript (ogólny termin na implementacje ECMAScript) a ECMAScript (język używany do implementacji javascript). To ECMAScript definiuje schemat dziedziczenia, a nie javascript, więc tylko natywne obiekty ECMAScript muszą implementować ten schemat dziedziczenia.

Działający program javascript składa się przynajmniej z wbudowanych obiektów ECMAScript (obiekt, funkcja, liczba itp.) I prawdopodobnie z niektórych obiektów natywnych (np. Funkcji). Może również zawierać obiekty hosta (takie jak obiekty DOM w przeglądarce lub inne obiekty w innych środowiskach hostów).

Podczas gdy obiekty wbudowane i natywne muszą implementować schemat dziedziczenia zdefiniowany w ECMA-262, obiekty hosta tego nie robią. Dlatego nie wszystkie obiekty w środowisku javascript muszą dziedziczyć po Object.prototype . Na przykład obiekty hosta w IE zaimplementowane jako obiekty ActiveX będą generować błędy, jeśli będą traktowane jako obiekty natywne (dlatego try..catch jest używany do inicjowania obiektów MS XMLHttpRequest). Niektóre obiekty DOM (takie jak NodeLists w IE w trybie dziwactw), jeśli zostaną przekazane do metod Array, będą powodować błędy, obiekty DOM w IE 8 i niższych nie mają schematu dziedziczenia podobnego do ECMAScript, i tak dalej.

Dlatego nie należy zakładać, że wszystkie obiekty w środowisku javascript dziedziczą po Object.prototype.

co oznacza, że ​​każdy obiekt w JavaScript ma dostęp do funkcji hasOwnProperty poprzez swój łańcuch prototypów

Co nie jest prawdą w przypadku niektórych obiektów hosta w IE w trybie dziwactw (i IE 8 i niższych zawsze) przynajmniej.

Biorąc pod uwagę powyższe, warto zastanowić się, dlaczego obiekt może mieć własną metodę hasOwnProperty i celowość wywołania innej metody hasOwnProperty bez wcześniejszego sprawdzenia, czy jest to dobry pomysł, czy nie.

Edytować

Podejrzewam, że powodem używania Object.prototype.hasOwnProperty.calljest to, że w niektórych przeglądarkach obiekty hosta nie mają metody hasOwnProperty , użycie wywołania i wbudowana metoda jest alternatywą. Jednak zrobienie tego ogólnie nie wydaje się dobrym pomysłem z powodów wymienionych powyżej.

Jeśli chodzi o obiekty hosta, operator in może być używany do testowania ogólnie właściwości, np

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

Alternatywa (przetestowana w IE6 i innych):

function ownProp(o, prop) {

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

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

W ten sposób wywołujesz wbudowaną właściwość hasOwnProperty tylko wtedy, gdy obiekt jej nie ma (dziedziczony lub inny).

Jeśli jednak obiekt nie ma hasOwnPropertymetody, prawdopodobnie równie dobrze jest użyć operatora in , ponieważ obiekt prawdopodobnie nie ma schematu dziedziczenia, a wszystkie właściwości są na obiekcie (to tylko założenie), np. operator in jest powszechnym (i pozornie skutecznym) sposobem testowania obsługi właściwości obiektów DOM.

RobG
źródło
Dzięki. Object.prototype.hasOwnProperty.call (o, 'bar') nie działa w FF 18.0 (przynajmniej w moim przypadku). Postanowiłem więc użyć („bar” w o) - i to pomogło.
Max
@Max innie wyszukuje hasOwnProperty(), podejrzewam, że poszukiwana właściwość istniała w łańcuchu prototypów.
Alex
To jest interesujący przykład z eslint.org/docs/rules/no-prototype-builtins : na przykład byłoby niebezpieczne, gdyby serwer internetowy przeanalizował dane wejściowe JSON od klienta i hasOwnProperty{"hasOwnProperty": 1}
naught101
Jasne, ale rozsądnie byłoby przetestować lub zweryfikować dowolny JSON dostarczony przez klienta ze schematem JSON, aby zapobiec takim problemom, nawet jeśli chodziło tylko o jakość danych. I nie powinno to powodować awarii serwera. :-)
RobG
8

JavaScript nie chroni nazwy właściwości hasOwnProperty

Jeśli istnieje możliwość, że obiekt może mieć właściwość o tej nazwie, konieczne jest użycie zewnętrznej właściwości hasOwnProperty, aby uzyskać poprawne wyniki:

Możesz skopiować i wkleić poniższe fragmenty kodu do konsoli przeglądarki, aby uzyskać lepsze zrozumienie

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

Zawsze zwraca fałsz

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

Użyj właściwości hasOwnProperty innego obiektu i wywołaj ją z tym ustawieniem na foo

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

W tym celu można również użyć właściwości hasOwnProperty z prototypu Object

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
źródło
1
To, co robisz, zostało już powiedziane w zaakceptowanej odpowiedzi , z wyjątkiem tego, że istnieje zastąpienie hasOwnPropertyzwrotów true.
Louis
1

Informacje podane w obu istniejących odpowiedziach są na miejscu. Jednak użycie:

('propertyName' in obj)

pojawia się kilka razy. Należy zauważyć, że hasOwnPropertyimplementacje zwrócą wartość true tylko wtedy, gdy właściwość jest bezpośrednio zawarta w testowanym obiekcie.

inOperator poddadzą dół łańcucha prototypów zbyt.

Oznacza to, że właściwości instancji zwrócą wartość true, gdy zostaną przekazane do hasOwnPropertymiejsca, w którym właściwości prototypu zwrócą wartość false.

Użycie inoperatora zarówno właściwości instancji, jak i prototypu zwróci wartość true.

steve
źródło