¿Por qué utilizar Object.prototype.hasOwnProperty.call (myObj, prop) en lugar de myObj.hasOwnProperty (prop)?

104

Si lo entiendo correctamente, todos y cada uno de los objetos de Javascript heredan del prototipo de Objeto, lo que significa que todos y cada uno de los objetos de Javascript tienen acceso a la función hasOwnProperty a través de su cadena de prototipos.

Mientras leía el código fuente de require.js, me topé con esta función:

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

hasOwnes una referencia a Object.prototype.hasOwnProperty. ¿Existe alguna diferencia práctica en escribir esta función como

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

Y ya que estamos en eso, ¿por qué definimos esta función? ¿Es solo una cuestión de accesos directos y almacenamiento en caché local del acceso a la propiedad para obtener (leves) ganancias de rendimiento, o me falta algún caso en el que hasOwnProperty podría usarse en objetos que no tienen este método?

timkg
fuente

Respuestas:

109

¿Existe alguna diferencia práctica [entre mis ejemplos]?

El usuario puede tener un objeto JavaScript creado con Object.create(null), que tendrá una null [[Prototype]]cadena y, por lo tanto, no estará hasOwnProperty()disponible en él. El uso de su segundo formulario no funcionaría por este motivo.

También es una referencia más segura Object.prototype.hasOwnProperty()(y también más corta).

Puedes imaginar que alguien pudo haber hecho ...

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

Lo que haría un hasProp(someObject)error si se hubiera implementado como su segundo ejemplo (encontraría ese método directamente en el objeto y lo invocaría, en lugar de delegarlo Object.prototype.hasOwnProperty).

Pero es menos probable que alguien haya anulado la Object.prototype.hasOwnPropertyreferencia.

Y ya que estamos en eso, ¿por qué definimos esta función?

Véase más arriba.

¿Es solo una cuestión de atajos y almacenamiento en caché local del acceso a la propiedad para obtener (leves) ganancias de rendimiento ...

Puede que sea más rápido en teoría, ya [[Prototype]]que no es necesario seguir la cadena, pero sospecho que esto es insignificante y no es la razón por la que lo es.

... ¿O me faltan algunos casos en los que hasOwnPropertypodría usarse en objetos que no tienen este método?

hasOwnProperty()existe Object.prototype, pero se puede anular. Cada objeto JavaScript nativo (pero no se garantiza que los objetos host sigan esto, consulte la explicación detallada de RobG ) tiene Object.prototypecomo último objeto en la cadena antes null(excepto, por supuesto, el objeto devuelto por Object.create(null)).

alex
fuente
Tu lógica probablemente sea correcta, pero creo que estás siendo amable. Si los autores de require.js piensan que hasOwnProperty podría haber sido anulado (lo cual es extremadamente improbable), entonces deberían estar llamando a todos los métodos incorporados de esa manera (tal vez lo hagan).
RobG
@Periback ¿De verdad? Estaba bastante seguro de que lo apoyaba.
alex
Atajo de ES6 si se usa con frecuencia. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Si entiendo correctamente, todos y cada uno de los objetos en Javascript heredan del prototipo de Objeto

Puede parecer que se parten los pelos, pero hay una diferencia entre javascript (el término genérico para las implementaciones de ECMAScript) y ECMAScript (el lenguaje utilizado para las implementaciones de javascript). Es ECMAScript el que define un esquema de herencia, no javascript, por lo que solo los objetos ECMAScript nativos necesitan implementar ese esquema de herencia.

Un programa javascript en ejecución consta de al menos los objetos ECMAScript integrados (Objeto, Función, Número, etc.) y probablemente algunos objetos nativos (por ejemplo, funciones). También puede tener algunos objetos de host (como objetos DOM en un navegador u otros objetos en otros entornos de host).

Si bien los objetos nativos e integrados deben implementar el esquema de herencia definido en ECMA-262, los objetos host no lo hacen. Por lo tanto, no todos los objetos en un entorno javascript deben heredar de Object.prototype . Por ejemplo, los objetos host en IE implementados como objetos ActiveX arrojarán errores si se tratan como objetos nativos (de ahí la razón por la que try..catch se usa para inicializar objetos MS XMLHttpRequest). Algunos objetos DOM (como NodeLists en IE en modo peculiar) si se pasan a métodos Array arrojarán errores, los objetos DOM en IE 8 y versiones inferiores no tienen un esquema de herencia similar a ECMAScript, y así sucesivamente.

Por lo tanto, no se debe asumir que todos los objetos en un entorno javascript heredan de Object.prototype.

lo que significa que todos y cada uno de los objetos en Javascript tienen acceso a la función hasOwnProperty a través de su cadena de prototipos

Lo cual no es cierto para ciertos objetos de host en IE en modo peculiar (e IE 8 e inferior siempre) al menos.

Dado lo anterior, vale la pena reflexionar sobre por qué un objeto podría tener su propio método hasOwnProperty y la conveniencia de llamar a otro método hasOwnProperty en su lugar sin probar primero si es una buena idea o no.

Editar

Sospecho que la razón para usarlo Object.prototype.hasOwnProperty.calles que en algunos navegadores, los objetos de host no tienen un método hasOwnProperty , usar call y el método incorporado es una alternativa. Sin embargo, hacerlo de forma genérica no parece una buena idea por las razones mencionadas anteriormente.

En lo que respecta a los objetos del host, el operador in se puede utilizar para probar las propiedades en general, p. Ej.

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

Una alternativa (probada en IE6 y otros):

function ownProp(o, prop) {

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

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

De esa manera, solo llama específicamente a la propiedad hasOwnProperty incorporada donde el objeto no la tiene (heredada o no).

Sin embargo, si un objeto no tiene un hasOwnPropertymétodo, probablemente sea tan adecuado usar el operador in , ya que el objeto probablemente no tenga un esquema de herencia y todas las propiedades estén en el objeto (eso es solo una suposición), por ejemplo, el El operador in es una forma común (y aparentemente exitosa) de probar el soporte de objetos DOM para propiedades.

RobG
fuente
Gracias. Object.prototype.hasOwnProperty.call (o, 'bar') no funciona en FF 18.0 (al menos en mi caso). Así que decidí usar ('bar' en o) y me ayudó.
Max
@Max inno realiza una hasOwnProperty()búsqueda, sospecho que la propiedad que estaba buscando existía en la cadena de prototipos.
alex
Este es un ejemplo interesante de eslint.org/docs/rules/no-prototype-builtins : por ejemplo, no sería seguro para un servidor web analizar la entrada JSON de un cliente y llamar hasOwnPropertydirectamente al objeto resultante, porque un cliente malintencionado podría envíe un valor JSON como {"hasOwnProperty": 1}y haga que el servidor se bloquee.
naught101
Claro, pero sería prudente probar o validar cualquier JSON proporcionado por el cliente con un esquema JSON para evitar estos problemas, incluso si su preocupación es solo la calidad de los datos. Y no debería hacer que el servidor se bloquee. :-)
RobG
8

JavaScript no protege el nombre de propiedad hasOwnProperty

Si existe la posibilidad de que un objeto tenga una propiedad con este nombre, es necesario utilizar una propiedad hasOwnProperty externa para obtener resultados correctos:

Puede copiar y pegar los siguientes fragmentos de código en la consola de su navegador para comprender mejor

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

Siempre devuelve falso

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

Utilice hasOwnProperty de otro objeto y llámelo con este conjunto para foo

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

También es posible utilizar la propiedad hasOwnProperty del prototipo de objeto para este propósito

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
fuente
1
El punto que está haciendo ya se ha establecido en la respuesta aceptada , excepto que existe la anulación de hasOwnPropertydevoluciones true.
Louis
1

La información proporcionada en ambas respuestas existentes es acertada. Sin embargo, el uso de:

('propertyName' in obj)

se menciona varias veces. Cabe señalar que las hasOwnPropertyimplementaciones devolverán verdadero solo si la propiedad está contenida directamente en el objeto que se está probando.

El inoperador también inspeccionará a lo largo de la cadena del prototipo.

Esto significa que las propiedades de la instancia devolverán verdadero cuando se pasen a hasOwnPropertywhere, ya que las propiedades del prototipo devolverán falso.

Al usar el inoperador, las propiedades de la instancia y del prototipo devolverán verdadero.

steve
fuente