Por que usar Object.prototype.hasOwnProperty.call (myObj, prop) em vez de myObj.hasOwnProperty (prop)?

104

Se bem entendi, todo e qualquer objeto em Javascript herda do protótipo do Objeto, o que significa que todo e qualquer objeto em Javascript tem acesso à função hasOwnProperty por meio de sua cadeia de protótipo.

Ao ler o código-fonte de require.js, me deparei com esta função:

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

hasOwné uma referência a Object.prototype.hasOwnProperty. Existe alguma diferença prática em escrever esta função como

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

E já que estamos nisso, por que definimos essa função? É apenas uma questão de atalhos e cache local de acesso à propriedade para (leves) ganhos de desempenho, ou estou perdendo algum caso em que hasOwnProperty possa ser usado em objetos que não têm esse método?

timkg
fonte

Respostas:

109

Existe alguma diferença prática [entre meus exemplos]?

O usuário pode ter um objeto JavaScript criado com Object.create(null), que terá uma null [[Prototype]]cadeia e, portanto, não terá hasOwnProperty()disponível nela. Usar o segundo formulário não funcionaria por esse motivo.

É também uma referência mais segura para Object.prototype.hasOwnProperty()(e também mais curta).

Você pode imaginar que alguém pode ter feito ...

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

O que faria uma hasProp(someObject)falha se tivesse sido implementado como seu segundo exemplo (ele iria encontrar esse método diretamente no objeto e invocá-lo, em vez de ser delegado Object.prototype.hasOwnProperty).

Mas é menos provável que alguém tenha substituído a Object.prototype.hasOwnPropertyreferência.

E já que estamos nisso, por que definimos essa função?

Veja acima.

É apenas uma questão de atalhos e cache local de acesso à propriedade para (leves) ganhos de desempenho ...

Isso pode torná-lo mais rápido na teoria, já que a [[Prototype]]cadeia não precisa ser seguida, mas eu suspeito que isso seja insignificante e não seja o motivo da implementação.

... ou estou faltando algum caso onde hasOwnPropertypossa ser usado em objetos que não possuem este método?

hasOwnProperty()existe em Object.prototype, mas pode ser substituído. Cada objeto JavaScript nativo (mas os objetos de host não têm garantia de seguir isso, consulte a explicação detalhada de RobG ) tem Object.prototypecomo seu último objeto na cadeia anterior null(exceto, é claro, para o objeto retornado por Object.create(null)).

alex
fonte
Sua lógica provavelmente está correta, mas acho que você está sendo gentil. Se os autores de require.js acharem que hasOwnProperty pode ter sido sobrescrito (o que é extremamente improvável), eles deveriam chamar todos os métodos integrados dessa maneira (talvez eles façam).
RobG de
@Periback Sério? Eu tinha certeza de que era compatível.
alex
Atalho ES6 se usado com freqüência. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Se bem entendi, todo e qualquer objeto em Javascript herda do protótipo do objeto

Pode parecer uma confusão, mas há uma diferença entre javascript (o termo genérico para implementações ECMAScript) e ECMAScript (a linguagem usada para implementações javascript). É o ECMAScript que define um esquema de herança, não o javascript, portanto, apenas os objetos ECMAScript nativos precisam implementar esse esquema de herança.

Um programa javascript em execução consiste em pelo menos os objetos ECMAScript embutidos (objeto, função, número, etc.) e provavelmente alguns objetos nativos (por exemplo, funções). Ele também pode ter alguns objetos de host (como objetos DOM em um navegador ou outros objetos em outros ambientes de host).

Embora os objetos integrados e nativos devam implementar o esquema de herança definido no ECMA-262, os objetos do host não. Portanto, nem todos os objetos em um ambiente javascript devem ser herdados de Object.prototype . Por exemplo, objetos de host no IE implementados como objetos ActiveX irão lançar erros se tratados como objetos nativos (daí porque try..catch é usado para inicializar objetos MS XMLHttpRequest). Alguns objetos DOM (como NodeLists no IE em modo quirks) se passados ​​para métodos Array irão lançar erros, objetos DOM no IE 8 e inferior não têm um esquema de herança ECMAScript, e assim por diante.

Portanto, não deve ser assumido que todos os objetos em um ambiente javascript são herdados de Object.prototype.

o que significa que todo e qualquer objeto em Javascript tem acesso à função hasOwnProperty por meio de sua cadeia de protótipo

O que não é verdade para certos objetos de host no IE no modo quirks (e no IE 8 e inferior sempre), pelo menos.

Diante do exposto, vale a pena ponderar por que um objeto pode ter seu próprio método hasOwnProperty e a conveniência de chamar algum outro método hasOwnProperty , sem antes testar se isso é uma boa ideia ou não.

Editar

Suspeito que o motivo do uso Object.prototype.hasOwnProperty.callé que, em alguns navegadores, os objetos de host não têm um método hasOwnProperty , usando call e o método integrado é uma alternativa. No entanto, fazer isso genericamente não parece uma boa ideia pelas razões mencionadas acima.

No que diz respeito a objetos hospedeiros, o operador in pode ser usado para testar propriedades em geral, por exemplo

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

Uma alternativa (testado no IE6 e outros):

function ownProp(o, prop) {

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

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

Dessa forma, você apenas chama especificamente a hasOwnProperty incorporada onde o objeto não a possui (herdada ou não).

No entanto, se um objeto não tiver um hasOwnPropertymétodo, é provavelmente tão adequado usar o operador in , pois o objeto provavelmente não tem um esquema de herança e todas as propriedades estão no objeto (embora isso seja apenas uma suposição), por exemplo, o O operador in é uma maneira comum (e aparentemente bem-sucedida) de testar o suporte a objetos DOM para propriedades.

RobG
fonte
Obrigado. Object.prototype.hasOwnProperty.call (o, 'bar') não está funcionando no FF 18.0 (pelo menos no meu caso). Então decidi usar ('bar' em o) - e ajudou.
Máx.
@Max innão realiza uma hasOwnProperty()pesquisa, suspeito que a propriedade que você estava procurando existia na cadeia de protótipos.
alex
Este é um exemplo interessante de eslint.org/docs/rules/no-prototype-builtins : por exemplo, não seria seguro para um servidor da web analisar a entrada JSON de um cliente e chamar hasOwnPropertydiretamente no objeto resultante, porque um cliente malicioso poderia enviar um valor JSON como {"hasOwnProperty": 1}e fazer com que o servidor trave.
naught101
Claro, mas seria sensato testar ou validar qualquer JSON fornecido pelo cliente com um esquema JSON para evitar esses problemas, mesmo se sua preocupação fosse apenas a qualidade dos dados. E não deve causar o travamento do servidor. :-)
RobG
8

JavaScript não protege o nome da propriedade hasOwnProperty

Se houver a possibilidade de um objeto ter uma propriedade com este nome, é necessário usar uma hasOwnProperty externa para obter os resultados corretos:

Você pode copiar e colar os trechos de código abaixo no console do seu navegador para compreender melhor

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

Sempre retorna falso

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

Use o hasOwnProperty de outro objeto e chame-o com este conjunto para foo

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

Também é possível usar a propriedade hasOwnProperty do protótipo do objeto para esta finalidade

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
fonte
1
O ponto que você está fazendo já foi abordado na resposta aceita , exceto que há a anulação das hasOwnPropertydevoluções true.
Louis
1

A informação dada em ambas as respostas existentes é local. No entanto, o uso de:

('propertyName' in obj)

é mencionado algumas vezes. Deve-se observar que as hasOwnPropertyimplementações retornarão true somente se a propriedade estiver contida diretamente no objeto sendo testado.

O inoperador também fará a inspeção ao longo da cadeia do protótipo.

Isso significa que as propriedades da instância retornarão true quando passadas para hasOwnPropertyonde as propriedades do protótipo retornarão false.

Usando o inoperador, as propriedades de instância e protótipo retornarão true.

Steve
fonte