Perché utilizzare Object.prototype.hasOwnProperty.call (myObj, prop) invece di myObj.hasOwnProperty (prop)?


104

Se ho capito bene, ogni oggetto in Javascript eredita dal prototipo Object, il che significa che ogni oggetto in Javascript ha accesso alla funzione hasOwnProperty attraverso la sua catena di prototipi.

Durante la lettura del codice sorgente di require.js, mi sono imbattuto in questa funzione:

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

hasOwnè un riferimento a Object.prototype.hasOwnProperty. C'è qualche differenza pratica nello scrivere questa funzione come

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

E visto che ci siamo, perché definiamo questa funzione? È solo una questione di scorciatoie e memorizzazione nella cache locale dell'accesso alle proprietà per (lievi) miglioramenti delle prestazioni o mi mancano i casi in cui hasOwnProperty potrebbe essere utilizzato su oggetti che non hanno questo metodo?

Risposte:


109

C'è qualche differenza pratica [tra i miei esempi]?

L'utente potrebbe avere un oggetto JavaScript creato con Object.create(null), che avrà una null [[Prototype]]catena e quindi non sarà hasOwnProperty()disponibile su di esso. L'utilizzo del secondo modulo non funzionerebbe per questo motivo.

È anche un riferimento più sicuro a Object.prototype.hasOwnProperty()(e anche più breve).

Puoi immaginare che qualcuno possa aver fatto ...

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

Il che hasProp(someObject)fallirebbe se fosse stato implementato come il tuo secondo esempio (troverebbe quel metodo direttamente sull'oggetto e lo invocerebbe, invece di essere delegato a Object.prototype.hasOwnProperty).

Ma è meno probabile che qualcuno abbia ignorato il Object.prototype.hasOwnPropertyriferimento.

E visto che ci siamo, perché definiamo questa funzione?

Vedi sopra.

È solo una questione di scorciatoie e memorizzazione nella cache locale dell'accesso alla proprietà per (lievi) miglioramenti delle prestazioni ...

Potrebbe renderlo più veloce in teoria, poiché la [[Prototype]]catena non deve essere seguita, ma sospetto che questo sia trascurabile e non il motivo per cui l'implementazione lo è.

... o mi manca qualche caso in cui hasOwnPropertypotrebbe essere utilizzato su oggetti che non hanno questo metodo?

hasOwnProperty()esiste su Object.prototype, ma può essere sovrascritto. Ogni oggetto JavaScript nativo (ma non è garantito che gli oggetti host lo seguano, vedi la spiegazione approfondita di RobG ) ha Object.prototypecome ultimo oggetto sulla catena prima null(tranne ovviamente per l'oggetto restituito da Object.create(null)).


La tua logica è probabilmente corretta, ma penso che tu sia gentile. Se gli autori di require.js pensano che hasOwnProperty potrebbe essere stato sovrascritto (il che è estremamente improbabile), allora dovrebbero chiamare tutti i metodi incorporati in questo modo (forse lo fanno).
RobG

@ Periback Davvero? Ero abbastanza sicuro che lo supportasse.
alex

Scorciatoia ES6 se usata spesso. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte

15

Se ho capito bene, ogni oggetto in Javascript eredita dal prototipo Object

Potrebbe sembrare una rottura di peli, ma c'è una differenza tra javascript (il termine generico per le implementazioni ECMAScript) e ECMAScript (il linguaggio utilizzato per le implementazioni javascript). È ECMAScript che definisce uno schema di ereditarietà, non javascript, quindi solo gli oggetti ECMAScript nativi devono implementare quello schema di ereditarietà.

Un programma javascript in esecuzione è costituito almeno dagli oggetti ECMAScript incorporati (Object, Function, Number, ecc.) E probabilmente da alcuni oggetti nativi (ad es. Funzioni). Potrebbe anche avere alcuni oggetti host (come oggetti DOM in un browser o altri oggetti in altri ambienti host).

Mentre gli oggetti incorporati e nativi devono implementare lo schema di ereditarietà definito in ECMA-262, gli oggetti host non lo fanno. Pertanto, non tutti gli oggetti in un ambiente javascript devono ereditare da Object.prototype . Ad esempio, gli oggetti host in IE implementati come oggetti ActiveX genereranno errori se trattati come oggetti nativi (ecco perché try..catch viene utilizzato per inizializzare gli oggetti XMLHttpRequest di MS). Alcuni oggetti DOM (come NodeLists in IE in modalità quirks) se passati ai metodi Array genereranno errori, gli oggetti DOM in IE 8 e versioni precedenti non hanno uno schema di ereditarietà simile a ECMAScript e così via.

Pertanto non si dovrebbe presumere che tutti gli oggetti in un ambiente javascript ereditino da Object.prototype.

il che significa che ogni oggetto in Javascript ha accesso alla funzione hasOwnProperty attraverso la sua catena di prototipi

Il che non è vero per alcuni oggetti host in IE in modalità quirks (e IE 8 e versioni precedenti sempre) almeno.

Considerato quanto sopra, vale la pena riflettere sul motivo per cui un oggetto potrebbe avere il proprio metodo hasOwnProperty e sull'opportunità di chiamare qualche altro hasOwnProperty metodo invece senza prima verificare se questa è una buona idea o meno.

modificare

Sospetto che il motivo per l'utilizzo Object.prototype.hasOwnProperty.callsia che in alcuni browser, gli oggetti host non hanno un metodo hasOwnProperty , utilizzando call e il metodo integrato è un'alternativa. Tuttavia, farlo genericamente non sembra una buona idea per i motivi sopra indicati.

Dove oggetti host concerne l' in operatore può essere utilizzato per prove per proprietà generalmente, ad esempio

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

Un'alternativa (testata in IE6 e altri):

function ownProp(o, prop) {

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

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

In questo modo si chiama in modo specifico solo hasOwnProperty incorporato dove l'oggetto non lo possiede (ereditato o altro).

Tuttavia, se un oggetto non dispone di un hasOwnPropertymetodo, è probabilmente altrettanto adatto per utilizzare il nel dell'operatore come l'oggetto probabilmente non ha un sistema di eredità e tutte le proprietà sono l'oggetto (che è solo una supposizione però), ad esempio, il in operator è un modo comune (e apparentemente efficace) di testare il supporto degli oggetti DOM per le proprietà.


Grazie. Object.prototype.hasOwnProperty.call (o, 'bar') non funziona in FF 18.0 (almeno nel mio caso). Quindi ho deciso di usare ('bar' in o) - e ha aiutato.
Max

@Max innon esegue una hasOwnProperty()ricerca, sospetto che la proprietà che stavi cercando esistesse nella catena del prototipo.
alex

Questo è un esempio interessante tratto da eslint.org/docs/rules/no-prototype-builtins : Ad esempio, non sarebbe sicuro per un server web analizzare l'input JSON da un client e chiamare hasOwnPropertydirettamente sull'oggetto risultante, perché un client dannoso potrebbe invia un valore JSON simile {"hasOwnProperty": 1}e provoca l'arresto anomalo del server.
nought101

Certo, ma sarebbe saggio testare o convalidare qualsiasi JSON fornito dal cliente con uno schema JSON per prevenire tali problemi, anche se la tua preoccupazione era solo la qualità dei dati. E non dovrebbe causare l'arresto anomalo del server. :-)
RobG

8

JavaScript non protegge il nome della proprietà hasOwnProperty

Se esiste la possibilità che un oggetto possa avere una proprietà con questo nome, è necessario utilizzare un hasOwnProperty esterno per ottenere risultati corretti:

Puoi copiare e incollare gli snippet di codice riportati di seguito nella console del browser per ottenere una migliore comprensione

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

Restituisce sempre false

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

Usa hasOwnProperty di un altro oggetto e chiamalo con questo set su foo

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

È anche possibile utilizzare la proprietà hasOwnProperty dal prototipo Object per questo scopo

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

1
Il punto che stai facendo è già stato fatto nella risposta accettata , tranne per il fatto che c'è l'override dei hasOwnPropertyresi true.
Louis

1

Le informazioni fornite in entrambe le risposte esistenti sono esatte. Tuttavia, l'uso di:

('propertyName' in obj)

viene menzionato alcune volte. Va notato che le hasOwnPropertyimplementazioni restituiranno true solo se la proprietà è contenuta direttamente sull'oggetto da testare.

L' inoperatore ispezionerà anche la catena del prototipo.

Ciò significa che le proprietà dell'istanza restituiranno true quando passate a hasOwnPropertydove le proprietà del prototipo restituiranno false.

Utilizzando l' inoperatore, sia le proprietà dell'istanza che quelle del prototipo restituiranno true.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.