Mengapa menggunakan Object.prototype.hasOwnProperty.call (myObj, prop) daripada myObj.hasOwnProperty (prop)?

104

Jika saya mengerti dengan benar, setiap objek di Javascript mewarisi dari prototipe Objek, yang berarti bahwa setiap objek di Javascript memiliki akses ke fungsi hasOwnProperty melalui rantai prototipe.

Saat membaca kode sumber require.js, saya menemukan fungsi ini:

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

hasOwnadalah referensi ke Object.prototype.hasOwnProperty. Apakah ada perbedaan praktis untuk menulis fungsi ini sebagai

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

Dan karena kita melakukannya, mengapa kita mendefinisikan fungsi ini? Apakah ini hanya masalah pintasan dan cache lokal dari akses properti untuk (sedikit) peningkatan kinerja, atau apakah saya melewatkan kasus di mana hasOwnProperty dapat digunakan pada objek yang tidak memiliki metode ini?

timkg
sumber

Jawaban:

109

Apakah ada perbedaan praktis [antara contoh saya]?

Pengguna mungkin memiliki objek JavaScript yang dibuat dengan Object.create(null), yang akan memiliki null [[Prototype]]rantai, dan karenanya tidak akan memilikinyahasOwnProperty() tersedia di sana. Menggunakan formulir kedua Anda akan gagal karena alasan ini.

Ini juga merupakan referensi yang lebih aman Object.prototype.hasOwnProperty() (dan juga lebih pendek).

Anda bisa membayangkan seseorang mungkin telah melakukan ...

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

Yang akan membuat hasProp(someObject)gagal seandainya itu diimplementasikan seperti contoh kedua Anda (itu akan menemukan metode itu langsung pada objek dan memanggilnya, alih-alih didelegasikan keObject.prototype.hasOwnProperty ).

Namun, kecil kemungkinannya seseorang akan mengganti Object.prototype.hasOwnPropertyreferensi tersebut.

Dan karena kita melakukannya, mengapa kita mendefinisikan fungsi ini?

Lihat di atas.

Apakah ini hanya masalah pintasan dan cache lokal dari akses properti untuk (sedikit) peningkatan kinerja ...

Ini mungkin membuatnya lebih cepat dalam teori, karena [[Prototype]]rantai tidak harus diikuti, tetapi saya curiga ini dapat diabaikan dan bukan alasan penerapannya mengapa demikian.

... atau saya kehilangan kasus di mana hasOwnPropertymungkin digunakan pada objek yang tidak memiliki metode ini?

hasOwnProperty()ada di Object.prototype, tetapi dapat diganti. Setiap objek JavaScript asli (tetapi objek host tidak dijamin untuk mengikuti ini, lihat penjelasan mendalam RobG ) memiliki Object.prototypeobjek terakhir pada rantai sebelumnya null(kecuali tentu saja untuk objek yang dikembalikan oleh Object.create(null)).

alex
sumber
Logika Anda mungkin benar, tapi saya pikir Anda bersikap baik. Jika penulis require.js berpikir hasOwnProperty mungkin telah diganti (yang sangat tidak mungkin), maka mereka harus memanggil semua metode bawaan dengan cara itu (mungkin memang demikian).
RobG
@Perib Benarkah? Saya cukup yakin itu mendukungnya.
alex
Pintasan ES6 jika sering digunakan. const hasProp = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
Richard Ayotte
15

Jika saya mengerti dengan benar, setiap objek di Javascript mewarisi dari prototipe Objek

Ini mungkin tampak seperti membelah rambut, tetapi ada perbedaan antara javascript (istilah umum untuk implementasi ECMAScript) dan ECMAScript (bahasa yang digunakan untuk implementasi javascript). ECMAScript yang menentukan skema pewarisan, bukan javascript, jadi hanya objek ECMAScript asli yang perlu menerapkan skema pewarisan tersebut.

Program javascript yang berjalan terdiri dari setidaknya objek ECMAScript bawaan (Objek, Fungsi, Angka, dll.) Dan mungkin beberapa objek asli (mis. Fungsi). Ini mungkin juga memiliki beberapa objek host (seperti objek DOM di browser, atau objek lain di lingkungan host lain).

Sementara objek bawaan dan bawaan harus menerapkan skema pewarisan yang ditentukan dalam ECMA-262, objek host tidak. Oleh karena itu, tidak semua objek dalam lingkungan javascript harus mewarisi dari Object.prototype . Misalnya, objek host di IE yang diimplementasikan sebagai objek ActiveX akan menampilkan kesalahan jika diperlakukan sebagai objek asli (oleh karena itu, mengapa try..catch digunakan untuk menginisialisasi objek MS XMLHttpRequest). Beberapa objek DOM (seperti NodeLists di IE dalam mode quirks) jika diteruskan ke metode Array akan memunculkan kesalahan, objek DOM di IE 8 dan yang lebih rendah tidak memiliki skema pewarisan seperti ECMAScript, dan seterusnya.

Oleh karena itu, tidak boleh diasumsikan bahwa semua objek dalam lingkungan javascript mewarisi dari Object.prototype.

yang berarti bahwa setiap objek di Javascript memiliki akses ke fungsi hasOwnProperty melalui rantai prototipenya

Yang tidak benar untuk objek host tertentu di IE dalam mode quirks (dan IE 8 dan yang lebih rendah selalu) setidaknya.

Mengingat hal di atas, ada baiknya merenungkan mengapa suatu objek mungkin memiliki metode hasOwnProperty sendiri dan kelayakan untuk memanggil beberapa metode hasOwnProperty lain daripada tanpa pengujian terlebih dahulu apakah itu ide yang bagus atau tidak.

Edit

Saya menduga bahwa alasan penggunaan Object.prototype.hasOwnProperty.calladalah bahwa di beberapa browser, objek host tidak memiliki metode hasOwnProperty , menggunakan panggilan dan metode built-in merupakan alternatif. Namun, melakukannya secara umum sepertinya bukan ide yang baik untuk alasan yang disebutkan di atas.

Jika menyangkut objek host, operator in dapat digunakan untuk menguji properti secara umum, misalnya

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

Alternatif (diuji di IE6 dan lainnya):

function ownProp(o, prop) {

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

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

Dengan cara itu Anda hanya secara khusus memanggil hasOwnProperty built-in di mana objek tidak memilikinya (diwarisi atau sebaliknya).

Namun, jika sebuah objek tidak memiliki hasOwnPropertymetode, itu mungkin sama cocok untuk menggunakan operator in karena objek tersebut kemungkinan besar tidak memiliki skema pewarisan dan semua properti ada pada objek (meskipun itu hanya asumsi), misalnya in operator adalah cara umum (dan tampaknya berhasil) untuk menguji dukungan objek DOM untuk properti.

RobG
sumber
Terima kasih. Object.prototype.hasOwnProperty.call (o, 'bar') tidak berfungsi di FF 18.0 (setidaknya dalam kasus saya). Jadi saya memutuskan untuk menggunakan ('bar' in o) - dan itu membantu.
Maksimal
@ Max intidak melakukan hasOwnProperty()pencarian, saya menduga properti yang Anda cari ada di rantai prototipe.
alex
Ini adalah contoh menarik dari eslint.org/docs/rules/no-prototype-builtins : Misalnya, tidak aman bagi server web untuk mengurai masukan JSON dari klien dan memanggil hasOwnPropertylangsung ke objek yang dihasilkan, karena klien jahat dapat mengirim nilai JSON seperti {"hasOwnProperty": 1}dan menyebabkan server mogok.
naught101
Tentu, tetapi akan bijaksana untuk menguji atau memvalidasi JSON yang disediakan klien dengan skema JSON untuk mencegah masalah tersebut, meskipun kekhawatiran Anda hanya pada kualitas data. Dan itu seharusnya tidak menyebabkan server macet. :-)
RobG
8

JavaScript tidak melindungi nama properti hasOwnProperty

Jika ada kemungkinan bahwa suatu objek mungkin memiliki properti dengan nama ini, maka perlu menggunakan hasOwnProperty eksternal untuk mendapatkan hasil yang benar:

Anda dapat menyalin dan menempel potongan kode di bawah ini ke konsol browser Anda untuk mendapatkan pemahaman yang lebih baik

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

Selalu mengembalikan nilai salah

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

Gunakan hasOwnProperty Objek lain dan panggil dengan set ini ke foo

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

Ini juga memungkinkan untuk menggunakan properti hasOwnProperty dari prototipe Object untuk tujuan ini

Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
Sudharshan
sumber
1
Poin yang Anda buat telah dibuat dalam jawaban yang diterima , kecuali bahwa ada penimpaan hasOwnPropertypengembalian true.
Louis
1

Informasi yang diberikan dalam kedua jawaban yang ada tepat. Namun, penggunaan:

('propertyName' in obj)

disebutkan beberapa kali. Perlu dicatat bahwa hasOwnPropertyimplementasi akan mengembalikan nilai true hanya jika properti secara langsung terdapat pada objek yang diuji.

The inOperator akan memeriksa turun melalui rantai prototipe juga.

Ini berarti properti instance akan mengembalikan nilai true saat diteruskan ke hasOwnPropertytempat properti prototipe akan mengembalikan false.

Menggunakan inoperator, properti instance dan prototipe akan mengembalikan nilai true.

steve
sumber