Saya juga tidak akan tahu ini, pulang dan bertani.
Salinan kode adalah sebagai berikut:
Hapus ThisisObject [Key]
atau
hapus thisisobject.key
Ngomong -ngomong, mari kita bicara tentang penggunaan Delete
Beberapa minggu yang lalu, saya memiliki kesempatan untuk membaca buku JavaScript yang berorientasi objek Stoyan Stoyan. Buku ini sangat dinilai di Amazon (12 ulasan, 5 bintang), jadi saya ingin tahu apakah itu buku yang direkomendasikan, jadi saya mulai membaca bab fungsi. Saya sangat menghargai cara buku ini menjelaskan banyak hal, dan contoh -contohnya diatur dengan cara yang sangat indah, bertahap, dan tampaknya bahkan pemula dapat dengan mudah menguasai pengetahuan ini. Namun, segera, saya menemukan kesalahpahaman yang menarik di seluruh bab ini - menghapus fungsi fungsional. Ada juga beberapa kesalahan lain (seperti perbedaan antara deklarasi fungsi dan ekspresi fungsi), tetapi kami tidak akan membahasnya saat ini.
Buku itu mengklaim:
"Fungsi diperlakukan seperti variabel normal - dapat disalin ke variabel yang berbeda, atau bahkan dihapus". Contoh dilampirkan pada penjelasan ini:
Salinan kode adalah sebagai berikut:
var sum = fungsi (a, b) {return a + b;}
var add = sum;
Hapus jumlah
BENAR
tipe jumlah;
"belum diartikan"
Abaikan beberapa titik koma yang hilang, dapatkah Anda melihat di mana kesalahan dalam kode ini? Jelas, kesalahannya adalah bahwa pengoperasian menghapus variabel jumlah tidak akan berhasil. Ekspresi hapus seharusnya tidak mengembalikan true, dan tipe jumlah tidak boleh mengembalikan "tidak terdefinisi". Semua ini karena tidak mungkin untuk menghapus variabel dalam JavaScript. Setidaknya, tidak mungkin dalam cara deklarasi ini.
Jadi, apa sebenarnya yang terjadi dalam contoh ini? Apakah ini bug? Atau penggunaan khusus? Mungkin tidak. Kode ini sebenarnya adalah output nyata di konsol Firebug, dan Stoyan harus menggunakannya sebagai alat untuk pengujian cepat. Ini hampir seperti Firebug mengikuti beberapa aturan penghapusan lainnya. Itu adalah Firebug yang menyebabkan Stoyan tersesat! Jadi, apa sebenarnya yang terjadi di sini?
Sebelum menjawab pertanyaan ini, pertama -tama kita perlu memahami cara kerja operator hapus dalam JavaScript: apa sebenarnya yang bisa dihapus dan apa yang tidak dapat dihapus? Hari ini, saya akan mencoba menjelaskan ini secara rinci. Kami akan melihat perilaku "aneh" dari Firebug dan menyadari bahwa itu tidak aneh. Kami akan menggali lebih dalam apa yang tersembunyi di balik layar di mana variabel, fungsi, menetapkan nilai untuk atribut dan menghapusnya. Kami akan melihat kompatibilitas browser dan beberapa bug yang paling terkenal. Kami juga akan membahas pola ketat ES5, dan bagaimana ia mengubah perilaku operator hapus.
Saya akan bertukar JavaScript dan Ecmascript, keduanya berarti ecmascript (kecuali jika jelas bahwa implementasi JavaScript Mozilla)
Seperti yang diharapkan, di internet, penjelasan tentang penghapusan cukup langka. Artikel MDC mungkin merupakan sumber daya terbaik untuk dipahami, tetapi, sayangnya, tidak memiliki beberapa detail menarik dari topik tersebut. Anehnya, salah satu hal yang terlupakan adalah alasan manifestasi aneh Firebug. Dan referensi MSDN hampir tidak berguna dalam aspek -aspek ini.
Teori
Jadi, mengapa kita dapat menghapus sifat suatu objek:
Salinan kode adalah sebagai berikut:
var o = {x: 1};
hapus sapi; // BENAR
sapi; // belum diartikan
Tetapi objek yang dinyatakan seperti ini tidak dapat dihapus:
Salinan kode adalah sebagai berikut:
var x = 1;
hapus x; // PALSU
X; // 1
Atau fungsinya:
Salinan kode adalah sebagai berikut:
fungsi x () {}
hapus x; // PALSU
tipe x; // "fungsi"
Catatan: Ketika suatu properti tidak dapat dihapus, operator hapus hanya akan mengembalikan false.
Untuk memahami hal ini, pertama -tama kita perlu menguasai konsep -konsep ini tentang instance variabel dan properti atribut - konsep -konsep ini sayangnya jarang disebutkan dalam buku -buku JavaScript. Saya akan mencoba meninjau secara singkat konsep -konsep ini di beberapa paragraf berikutnya. Konsep -konsep ini sulit dimengerti! Jika Anda tidak peduli "mengapa hal -hal ini bekerja seperti ini", lewati saja bab ini.
Jenis Kode:
Dalam ecmascript, ada 3 jenis kode yang dapat dieksekusi: kode global, kode fungsi dan kode evaluasi. Jenis-jenis ini kurang lebih jelas dalam hal nama, berikut adalah gambaran singkat:
Ketika sepotong kode sumber dianggap sebagai program, itu akan dieksekusi dalam lingkungan global dan dianggap sebagai kode global. Dalam lingkungan browser, isi elemen skrip biasanya ditafsirkan sebagai program dan karenanya dieksekusi sebagai kode global.
Kode apa pun yang dieksekusi secara langsung dalam suatu fungsi jelas dianggap sebagai kode fungsi. Di browser, konten properti acara (seperti <p onclick = "...">) biasanya ditafsirkan sebagai kode fungsi.
Akhirnya, teks kode yang diterapkan pada evaluasi fungsi bawaan ditafsirkan sebagai kode eval. Segera kami akan mencari tahu mengapa jenis ini istimewa.
Konteks eksekusi:
Ketika kode ecmascript dieksekusi, biasanya terjadi dalam konteks eksekusi tertentu. Konteks eksekusi adalah konsep entitas yang agak abstrak, yang dapat membantu memahami bagaimana ruang lingkup dan instance variabel bekerja. Untuk masing -masing dari tiga kode yang dapat dieksekusi, ada konteks eksekusi yang sesuai dengannya. Ketika suatu fungsi dieksekusi, kami mengatakan "kontrol program memasuki konteks eksekusi kode fungsi"; Ketika sepotong kode global dijalankan, kontrol program memasuki konteks eksekusi kode global, dll.
Seperti yang Anda lihat, konteks eksekusi secara logis dapat membentuk tumpukan. Pertama, mungkin ada sepotong kode global dan konteks eksekusi sendiri, dan kemudian potongan kode ini dapat memanggil fungsi dengan konteks eksekusi (fungsi). Fungsi ini dapat memanggil fungsi lain, dll. Bahkan jika fungsi disebut rekursif, itu akan dimasukkan ke dalam konteks eksekusi baru setiap kali disebut.
Objek Aktif (Objek Aktivasi) / Objek Variabel:
Setiap konteks eksekusi memiliki apa yang disebut objek variabel yang terkait dengannya. Mirip dengan konteks eksekusi, objek variabel adalah entitas abstrak, suatu mekanisme yang digunakan untuk menggambarkan instance variabel. Menariknya, variabel dan fungsi yang dinyatakan dalam kode sumber biasanya ditambahkan ke objek variabel ini sebagai properti.
Ketika kontrol program memasuki konteks eksekusi kode global, objek global digunakan sebagai objek variabel. Inilah mengapa variabel fungsi yang dinyatakan global menjadi properti objek global.
Salinan kode adalah sebagai berikut:
/ * Ingatlah bahwa `this` mengacu pada objek global saat berada di ruang lingkup global */
var global_object = ini;
var foo = 1;
Global_object.foo; // 1
foo === global_object.foo; // BENAR
function bar () {}
typeOf global_object.bar; // "fungsi"
Global_object.bar === Bar; // BENAR
OK, jadi variabel global akan menjadi properti objek global, tetapi apa yang terjadi pada variabel lokal (yang didefinisikan dalam kode fungsi)? Bahkan, mereka berperilaku sangat mirip: mereka akan menjadi properti objek variabel (objek variabel). Satu-satunya perbedaan adalah bahwa ketika dalam kode fungsi, objek variabel bukan objek global, tetapi objek aktivasi yang disebut. Objek aktif dibuat setiap kali memasuki konteks eksekusi kode fungsi.
Tidak hanya variabel dan fungsi yang dinyatakan dalam kode fungsi akan menjadi properti dari objek aktif; Ini juga akan terjadi pada setiap parameter fungsi (nama yang sesuai dengan parameter formal yang sesuai) dan objek argumen khusus (nama argumen). Perhatikan bahwa objek aktif adalah mekanisme deskripsi internal dan tidak dapat diakses dalam kode program.
Salinan kode adalah sebagai berikut:
(function (foo) {
var bar = 2;
fungsi baz () {}
/*
Dalam istilah abstrak,
Objek `argumen` khusus menjadi properti yang mengandung objek aktivasi fungsi:
Activation_object.arguments; // Objek Argumen
... serta argumen `foo`:
Activation_object.foo; // 1
... serta variabel `bar`:
Activation_object.bar; // 2
... serta fungsi yang dinyatakan secara lokal:
typeof Activation_object.baz; // "fungsi"
*/
}) (1);
Akhirnya, variabel yang dideklarasikan dalam kode eval menjadi properti objek variabel dalam konteks penelepon. Eval Code hanya menggunakan objek variabel dalam konteks eksekusi kode yang menyebutnya.
Salinan kode adalah sebagai berikut:
var global_object = ini;
/* `foo` dibuat sebagai properti objek variabel konteks panggilan,
yang dalam hal ini merupakan objek global */
eval ('var foo = 1;');
Global_object.foo; // 1
(fungsi(){
/* `bar` dibuat sebagai properti objek variabel konteks panggilan,
yang dalam hal ini merupakan objek aktivasi yang mengandung fungsi */
eval ('var bar = 1;');
/*
Dalam istilah abstrak,
Activation_object.bar; // 1
*/
}) ();
Atribut Properti
Kami hampir di sini. Sekarang kita sangat menyadari apa yang terjadi dengan variabel (mereka menjadi properti), satu -satunya konsep yang tersisa yang harus dipahami adalah atribut properti. Setiap atribut dapat memiliki 0 atau lebih properti, yang dipilih dari set berikut: Readonly, Dontenum, DontDelete dan Internal. Anda dapat menganggapnya sebagai bendera - fitur yang dapat ada atau tidak ada di properti. Untuk diskusi kami hari ini, kami hanya tertarik pada DontDelete.
Ketika variabel dan fungsi yang dinyatakan menjadi atribut objek variabel (atau objek aktif kode fungsi, atau objek global kode global), atribut ini dibuat dengan atribut atribut DontDelete. Namun, atribut eksplisit (atau implisit) tidak akan disertakan dengan atribut DontDelete. Inilah sebabnya kami dapat menghapus beberapa atribut tetapi tidak dapat menghapus yang lain.
Salinan kode adalah sebagai berikut:
var global_object = ini;
/* `foo` adalah properti dari objek global.
Ini dibuat melalui deklarasi variabel dan begitu juga atribut DontDelete.
Inilah sebabnya mengapa tidak dapat dihapus. */
var foo = 1;
hapus foo; // PALSU
tipe foo; // "nomor"
/* `Bar` adalah properti dari objek global.
Ini dibuat melalui deklarasi fungsi dan begitu juga atribut DontDelete.
Inilah sebabnya mengapa juga tidak dapat dihapus. */
function bar () {}
hapus bar; // PALSU
tipe bar; // "fungsi"
/* `Baz` juga merupakan properti dari objek global.
Namun, ini dibuat melalui penugasan properti dan demikian pula tidak ada atribut DontDelete.
Inilah sebabnya mengapa dapat dihapus. */
Global_object.baz = 'bla';
hapus global_object.baz; // BENAR
typeOf global_object.baz; // "belum diartikan"
Objek bawaan dan dontdelete
Jadi, ini semua tentang itu (DontDelete): Properti khusus dari properti yang mengontrol apakah properti ini dapat dihapus. Perhatikan bahwa beberapa objek bawaan ditentukan untuk berisi DontDelete, sehingga tidak dapat dihapus. Misalnya, variabel argumen khusus (atau, seperti yang kita ketahui sekarang, properti objek aktif) memiliki DontDelete. Properti panjang dari sebuah instance fungsi juga memiliki properti DontDelete.
Salinan kode adalah sebagai berikut:
(fungsi(){
/ * tidak dapat menghapus `argumen`, karena ia memiliki dontdelete */
hapus argumen; // PALSU
tipe argumen; // "objek"
/* tidak dapat menghapus fungsi `length`; itu juga memiliki DontDelete */
fungsi f () {}
hapus f.length; // PALSU
tipe f.length; // "nomor"
}) ();
Atribut yang sesuai dengan parameter fungsi juga memiliki fitur DontDelete sejak pendiriannya, jadi kami tidak dapat menghapusnya.
Salinan kode adalah sebagai berikut:
(function (foo, bar) {
hapus foo; // PALSU
foo; // 1
hapus bar; // PALSU
batang; // 'bla'
}) (1, 'bla');
Tugas yang tidak diumumkan:
Anda juga dapat ingat bahwa penugasan yang tidak diumumkan menciptakan properti pada objek global kecuali properti telah ditemukan di tempat lain dalam rantai lingkup ini sebelum objek global. Dan sekarang kita tahu perbedaan antara penugasan properti dan deklarasi variabel - yang terakhir menetapkan properti DontDelete, tetapi yang pertama tidak. Kita harus jelas mengapa penugasan yang tidak diumumkan menciptakan properti yang dihapus.
Salinan kode adalah sebagai berikut:
var global_object = ini;
/* Buat properti global melalui deklarasi variabel; Properti memiliki DontDelete */
var foo = 1;
/* Buat properti global melalui penugasan yang tidak diumumkan; Properti tidak memiliki dontdelete */
bar = 2;
hapus foo; // PALSU
tipe foo; // "nomor"
hapus bar; // BENAR
tipe bar; // "belum diartikan"
Harap dicatat: Properti ditentukan saat atribut dibuat, dan penugasan selanjutnya tidak memodifikasi properti atribut yang ada. Sangat penting untuk memahami perbedaan ini.
Salinan kode adalah sebagai berikut:
/ * `foo` dibuat sebagai properti dengan DontDelete */
function foo () {}
/* Tugas selanjutnya tidak memodifikasi atribut. Dontdelete masih ada! */
foo = 1;
hapus foo; // PALSU
tipe foo; // "nomor"
/* Tetapi menugaskan properti yang tidak ada,
membuat properti itu dengan atribut kosong (dan tanpa dontdelete) */
this.bar = 1;
hapus bar; // BENAR
tipe bar; // "belum diartikan"
Kebingungan Firebug:
Apa yang terjadi di Firebug? Mengapa variabel yang dinyatakan dalam konsol dapat dihapus? Bukankah ini bertentangan dengan apa yang telah kita pelajari sebelumnya? Nah, seperti yang saya katakan sebelumnya, kode eval memiliki kinerja khusus saat menghadapi deklarasi variabel. Variabel yang dideklarasikan dalam eval sebenarnya dibuat sebagai atribut tanpa atribut DontDelete.
Salinan kode adalah sebagai berikut:
eval ('var foo = 1;');
foo; // 1
hapus foo; // BENAR
tipe foo; // "belum diartikan"
Demikian pula, juga, ketika dipanggil dalam kode fungsi:
Salinan kode adalah sebagai berikut:
(fungsi(){
eval ('var foo = 1;');
foo; // 1
hapus foo; // BENAR
tipe foo; // "belum diartikan"
}) ();
Ini adalah dasar untuk perilaku abnormal firebug. Semua teks di konsol akan diuraikan dan dieksekusi sebagai kode eval, bukan kode global atau fungsi. Jelas, semua variabel yang dinyatakan di sini pada akhirnya akan menjadi properti tanpa atribut DontDelete, sehingga semuanya dapat dengan mudah dihapus. Kita perlu memahami perbedaan antara kode global dan konsol Firebug.
Hapus variabel melalui eval:
Perilaku eval yang menarik ini, ditambah dengan aspek lain dari ecmascript, secara teknis dapat memungkinkan kita untuk menghapus sifat "tidak dapat dihapus". Satu hal tentang deklarasi fungsi adalah bahwa mereka dapat mengganti variabel dengan nama yang sama dalam konteks eksekusi yang sama.
Salinan kode adalah sebagai berikut:
fungsi x () {}
var x;
tipe x; // "fungsi"
Perhatikan bagaimana deklarasi fungsi memperoleh variabel prioritas dan menimpa dengan nama yang sama (atau, dengan kata lain, properti yang sama dalam objek variabel). Ini karena deklarasi fungsi dipakai setelah deklarasi variabel dan diizinkan untuk menimpa mereka (deklarasi variabel). Deklarasi fungsi tidak hanya menggantikan nilai properti, tetapi juga mengganti properti properti itu. Jika kami mendeklarasikan fungsi melalui eval, fungsi itu harus menggantikan properti dari properti asli (diganti) dengan propertinya sendiri. Dan, karena variabel yang dinyatakan melalui eval membuat properti tanpa atribut DontDelete, instantiating fungsi baru ini sebenarnya akan menghapus atribut DontDelete yang ada dari properti, sehingga properti dapat dihapus (dan, jelas, menunjukkan nilainya ke fungsi yang baru dibuat).
Salinan kode adalah sebagai berikut:
var x = 1;
/ * Tidak dapat menghapus, `x` memiliki dontDelete */
hapus x; // PALSU
tipe x; // "nomor"
eval ('function x () {}');
/ * `x` properti sekarang referensi fungsi, dan seharusnya tidak memiliki dontdelete */
tipe x; // "fungsi"
hapus x; // harus `benar`
tipe x; // harus "tidak ditentukan"
Sayangnya, "penipuan" ini tidak berfungsi dalam implementasi apa pun saat ini. Mungkin saya kehilangan sesuatu di sini, atau mungkin perilakunya terlalu tidak jelas bahwa pelaksana tidak menyadarinya.
Kompatibilitas browser:
Berguna dalam teori untuk memahami bagaimana segala sesuatu bekerja, tetapi praktik adalah hal yang paling penting. Apakah browser mengikuti standar dalam hal membuat/menghapus variabel/properti? Jawabannya adalah: Dalam kebanyakan kasus, ya.
Saya menulis set tes sederhana untuk menguji kompatibilitas browser dengan operator hapus, termasuk tes di bawah kode global, kode fungsi dan kode evaluasi. Set tes memeriksa apakah nilai pengembalian dan nilai atribut dari operator hapus (seperti yang seharusnya mereka perilaku) benar -benar dihapus. Nilai pengembalian hapus tidak sepenting hasil sebenarnya. Jika menghapus pengembalian benar, bukan salah, ini tidak penting, dan yang penting adalah atribut dengan atribut DontDelete tidak dihapus, dan sebaliknya.
Browser modern umumnya cukup kompatibel. Terlepas dari fitur eval yang saya sebutkan sebelumnya, browser berikut telah lulus semua set tes: Opera 7.54+, Firefox 1.0+, Safari 3.1.2+, Chrome 4+.
Safari 2.x dan 3.0.4 memiliki masalah saat menghapus parameter fungsi; Properti ini tampaknya dibuat tanpa DontDelete, sehingga dapat dihapus. Safari 2.x memiliki lebih banyak masalah - menghapus variabel yang tidak direferensikan (seperti Delete 1) akan melempar pengecualian; Deklarasi fungsi membuat properti yang dapat dihapus (tetapi, anehnya, deklarasi variabel tidak); Deklarasi variabel dalam eval akan menjadi non-evelete (tetapi deklarasi fungsi dapat dihapus).
Mirip dengan Safari, Konqueror (3.5, bukan 4.3) melempar pengecualian saat menghapus tipe yang tidak direferensikan (seperti: Hapus 1), dan salah membuat variabel fungsi dapat dihapus.
Catatan Penerjemah:
Saya menguji versi terbaru Chrome, Firefox dan IE, dan pada dasarnya mempertahankan situasi di mana semua lewat lainnya kecuali 23 dan 24 akan gagal. Pada saat yang sama, saya menguji UC dan beberapa browser seluler. Kecuali untuk browser bawaan Nokia E72, yang juga gagal 15 dan 16, browser bawaan lainnya sebagian besar sama dengan browser desktop. Tetapi perlu disebutkan bahwa browser bawaan BlackBerry Curve 8310/8900 dapat melewati 23, yang mengejutkan saya.
Tokek dontdelete bug:
Gecko 1.8.x Browser - Firefox 2.x, Camino 1.x, Seamonkey 1.x, dll. - Menampilkan bug yang sangat menarik, penugasan eksplisit suatu properti akan menghapus properti DontDelete -nya, bahkan jika properti ini dibuat melalui deklarasi variabel atau deklarasi fungsi.
Salinan kode adalah sebagai berikut:
function foo () {}
hapus foo; // false (seperti yang diharapkan)
tipe foo; // "fungsi" (seperti yang diharapkan)
/ * sekarang ditugaskan ke properti secara eksplisit */
this.foo = 1; // keliru menghapus atribut DontDelete
hapus foo; // BENAR
tipe foo; // "belum diartikan"
/ * Perhatikan bahwa ini tidak terjadi saat menetapkan properti secara implisit */
function bar () {}
bar = 1;
hapus bar; // PALSU
tipe bar; // "nomor" (meskipun penugasan menggantikan properti)
Anehnya, Internet Explorer 5.5 - 8 melewati set tes lengkap, kecuali bahwa menghapus tipe yang tidak dirujuk (seperti Delete 1) akan melempar pengecualian (seperti safari lama). Namun, ada bug yang lebih serius di bawah IE, yang tidak begitu jelas. Bug ini terkait dengan objek global.
Yaitu bug:
Seluruh bab ini berbicara tentang bug Internet Explorer? Wow! Luar biasa!
Dalam IE (setidaknya IE 6-8), ekspresi berikut melempar pengecualian (ketika dieksekusi dalam kode global):
this.x = 1;
hapus x; // TypeError: Objek tidak mendukung tindakan ini
Yang ini juga akan, tetapi akan melempar pengecualian yang berbeda, yang membuat segalanya menjadi lebih menarik:
var x = 1;
hapus this.x; // typeError: tidak dapat menghapus 'this.x'
Ini terlihat seperti di IE, deklarasi variabel dalam kode global tidak membuat atribut pada objek global. Membuat atribut dengan penugasan (this.x = 1) dan kemudian menghapusnya dengan menghapus x sesudahnya melempar kesalahan. Membuat atribut dengan deklarasi (var x = 1) dan menghapusnya sesudahnya melempar kesalahan lain.
Tapi bukan itu saja. Membuat properti dengan penugasan eksplisit sebenarnya akan selalu menyebabkan pengecualian saat dihapus. Tidak hanya ada kesalahan di sini, tetapi properti yang dibuat tampaknya memiliki atribut DontDelete, yang tentu saja seharusnya tidak.
this.x = 1;
hapus this.x; // TypeError: Objek tidak mendukung tindakan ini
tipe x; // "nomor" (masih ada, tidak dihapus sebagaimana mestinya!)
hapus x; // TypeError: Objek tidak mendukung tindakan ini
tipe x; // "nomor" (tidak dihapus lagi)
Sekarang, kami akan berpikir bahwa di bawah IE, penugasan yang tidak dideklarasikan (properti harus dibuat pada objek global) melakukan pembuatan properti yang dapat dihapus.
x = 1;
hapus x; // BENAR
tipe x; // "belum diartikan"
Namun, jika Anda menghapus properti ini melalui referensi ini dalam kode global (hapus this.x), kesalahan serupa akan muncul.
x = 1;
hapus this.x; // typeError: tidak dapat menghapus 'this.x'
Jika kita ingin merangkum perilaku ini, tampaknya menggunakan hapus this.x untuk menghapus variabel dari kode global tidak akan pernah berhasil. Ketika properti dalam pertanyaan dibuat dengan penugasan eksplisit (this.x = 1), hapus melempar kesalahan; Ketika properti dibuat oleh penugasan yang tidak diumumkan (x = 1) atau dengan deklarasi (var x = 1), hapus melempar kesalahan lain.
Hapus x, di sisi lain, kesalahan harus dilemparkan hanya ketika properti dibuat oleh penugasan eksplisit - this.x = 1. Jika suatu properti dibuat oleh deklarasi (var x = 1), operasi penghapusan tidak pernah terjadi, dan operasi penghapusan dengan benar mengembalikan false. Jika suatu properti dibuat oleh penugasan yang tidak diumumkan (x = 1), operasi penghapusan berfungsi seperti yang diharapkan.
Saya memikirkan masalah ini lagi di bulan September. Garrett Smith menyarankan itu di bawah IE,
"Objek variabel global diimplementasikan sebagai objek JScript, dan objek global diimplementasikan oleh host."
Garrett menggunakan entri blog Eric Lippert sebagai referensi.
Kami dapat mengkonfirmasi teori ini dengan menerapkan beberapa tes. Perhatikan bahwa ini dan jendela tampaknya menunjuk ke objek yang sama (jika kita dapat mempercayai operator ===), tetapi objek variabel (objek di mana deklarasi fungsi berada) berbeda dari apa yang menunjuk ke.
Salinan kode adalah sebagai berikut:
/ * dalam kode global */
function getBase () {return ini; }
getBase () === this.getBase (); // PALSU
this.getBase () === this.getBase (); // BENAR
window.getBase () === this.getBase (); // BENAR
window.getBase () === getBase (); // PALSU
salah paham:
Keindahan pemahaman mengapa hal -hal bekerja seperti itu tidak bisa diremehkan. Saya telah melihat beberapa kesalahpahaman tentang operator hapus di internet. Misalnya, jawaban di StackOverflow (dengan peringkat yang sangat tinggi), jelas dengan percaya diri
"Ketika operan target bukan properti objek, hapus harus beroperasi."
Sekarang kita telah memahami inti dari perilaku operasi delete, kesalahan dalam jawaban ini menjadi jelas. Hapus tidak membedakan antara variabel dan atribut (pada kenyataannya, untuk dihapus, keduanya adalah tipe referensi) dan pada kenyataannya hanya peduli dengan atribut DontDelete (dan apakah atribut itu sendiri ada).
Juga sangat menarik untuk melihat berbagai kesalahpahaman saling menyangkal. Dalam topik yang sama, satu orang pertama kali menyarankan bahwa hanya menghapus variabel (ini tidak akan berfungsi kecuali dinyatakan dalam eval), sementara orang lain memberikan koreksi bug untuk bagaimana menghapus digunakan untuk menghapus variabel dalam kode global, tetapi tidak dalam kode fungsi.
Berhati -hatilah dengan penjelasan JavaScript di internet. Metode yang ideal adalah untuk selalu memahami esensi masalahnya. ;)
Hapus dan Objek Host (objek host):
Algoritma Delete kira -kira seperti ini:
Kembalikan true jika operan bukan jenis referensi
Jika objek tidak memiliki atribut langsung dari nama ini, kembalikan true (seperti yang kita ketahui, objek dapat berupa objek aktif atau objek global)
Jika properti ada tetapi memiliki atribut DontDelete, kembalikan false
Dalam kasus lain, hapus atribut dan kembalikan true
Namun, perilaku operator penghapusan pada objek host tidak dapat diprediksi. Dan perilaku ini sebenarnya tidak salah: (menurut standar), objek host diizinkan untuk mengimplementasikan perilaku apa pun untuk beberapa operator seperti baca (metode internal [[get]]), tulis (metode internal [[put]] dan hapus (metode internal [[hapus]]). Perilaku Grace for Custom [[Delete]] inilah yang membuat objek host begitu membingungkan.
Kami telah melihat beberapa kebiasaan IE, di mana menghapus objek tertentu (yang jelas diimplementasikan sebagai objek host) akan melempar kesalahan. Beberapa versi Firefox akan melempar saat menghapus window.location. Ketika operan adalah objek host, Anda tidak dapat mempercayai nilai pengembalian hapus. Mari kita lihat apa yang terjadi di Firefox:
Salinan kode adalah sebagai berikut:
/ * "Peringatan" adalah properti langsung dari `window` (jika kami percaya` hasownproperty`) */
window.hasownproperty ('waspada'); // BENAR
hapus window.alert; // BENAR
typeof window.alert; // "fungsi"
Hapus jendela. Ini akan diselesaikan untuk referensi (jadi itu tidak akan mengembalikan true di langkah pertama). Ini adalah properti langsung dari objek jendela (jadi tidak akan mengembalikan true di langkah kedua). Jadi satu -satunya kasus di mana Delete dapat mengembalikan true adalah mencapai langkah keempat dan benar -benar menghapus properti itu. Namun, properti ini tidak pernah dihapus.
Moral dari cerita ini adalah: tidak pernah mempercayai objek tuan rumah.
ES5 Mode ketat:
Jadi, apa yang dibawa ECMascript5 Mode Ketat kepada kami? Itu memperkenalkan beberapa batasan. Ketika ekspresi operator hapus adalah referensi langsung ke variabel, parameter fungsi atau pengidentifikasi fungsi, kesalahan sintaks akan dilemparkan. Selain itu, jika properti memiliki properti internal [[dapat dikonfigurasi]] == Salah, kesalahan jenis akan dilemparkan.
Salinan kode adalah sebagai berikut:
(function (foo) {
"Gunakan ketat"; // Aktifkan mode ketat dalam fungsi ini
var bar;
fungsi baz () {}
hapus foo; // sintakser (saat menghapus argumen)
hapus bar; // sintakser (saat menghapus variabel)
Hapus Baz; // SyntaxError (saat menghapus variabel dibuat dengan deklarasi fungsi)
/ * `length` instance fungsi memiliki {[[dapat dikonfigurasi]]: false} */
delete (function () {}). length; // typeError
}) ();
Selain itu, menghapus variabel yang tidak diumumkan (atau referensi yang belum terselesaikan) juga akan melempar kesalahan sintaks:
"Gunakan ketat";
hapus i_dont_exist; // Sintakserror
Penugasan yang tidak diumumkan berperilaku serupa dengan variabel yang tidak dideklarasikan dalam mode ketat (kecuali kali ini menimbulkan kesalahan penawaran alih -alih kesalahan sintaksis):
"Gunakan ketat";
i_dont_exist = 1; // ReferenceError
Seperti yang Anda pahami sekarang, semua pembatasan lebih atau kurang masuk akal, karena menghapus variabel, deklarasi fungsi, dan parameter dapat menyebabkan begitu banyak kebingungan. Alih -alih secara diam -diam mengabaikan operasi penghapusan, pola ketat mengambil ukuran yang lebih radikal dan deskriptif.
Meringkaskan:
Posting blog ini akhirnya cukup panjang, jadi saya tidak akan membicarakan sesuatu seperti menggunakan Delete untuk menghapus objek array atau apa artinya. Anda dapat merujuk pada penjelasan khusus artikel MDC (atau membaca standar dan melakukan eksperimen Anda sendiri).
Berikut adalah ringkasan singkat tentang cara kerja operasi penghapusan di JavaScript:
Variabel dan deklarasi fungsi adalah sifat objek aktif atau objek global
Atribut memiliki beberapa karakteristik, dan DontDelete adalah atribut yang menentukan apakah atribut ini dapat dihapus.
Variabel dan deklarasi fungsi dalam kode global atau fungsi selalu membuat atribut dengan atribut DontDelete.
Parameter fungsi selalu merupakan atribut dari objek aktif dan disertai dengan DontDelete.
Variabel dan fungsi yang dideklarasikan dalam kode eval selalu membuat properti tanpa dontdelete.
Properti baru tidak memiliki atribut saat dibuat (tentu saja tidak ada dontdelete juga).
Objek host diizinkan untuk memutuskan sendiri bagaimana bereaksi terhadap operasi penghapusan.
Jika Anda ingin lebih terbiasa dengan apa yang dijelaskan di sini, lihat spesifikasi edisi ke-3 ECMA-262.
Saya harap Anda dapat menikmati artikel ini dan mempelajari sesuatu yang baru. Setiap pertanyaan, saran, atau koreksi dipersilakan.