Bagaimana cara menghapus variabel JavaScript?

592

Saya memiliki variabel global dalam JavaScript (sebenarnya windowproperti, tapi saya rasa itu tidak penting) yang sudah diisi oleh skrip sebelumnya, tetapi saya tidak ingin skrip lain yang akan berjalan nanti untuk melihat nilainya atau bahkan didefinisikan.

Saya telah memasukkan some_var = undefineddan berfungsi untuk tujuan pengujian typeof some_var == "undefined"tetapi saya benar-benar tidak berpikir itu cara yang tepat untuk melakukannya.

Bagaimana menurut anda?

Guss
sumber

Jawaban:

454

The deleteOperator menghilangkan properti dari suatu objek. Itu tidak bisa menghapus variabel. Jadi jawaban untuk pertanyaan tergantung pada bagaimana variabel global atau properti didefinisikan.

(1) Jika itu dibuat dengan var, itu tidak dapat dihapus.

Sebagai contoh:

var g_a = 1; //create with var, g_a is a variable 
delete g_a; //return false
console.log(g_a); //g_a is still 1

(2) Jika itu dibuat tanpa var, itu bisa dihapus.

g_b = 1; //create without var, g_b is a property 
delete g_b; //return true
console.log(g_b); //error, g_b is not defined

Penjelasan Teknis

1. Menggunakan var

Dalam hal ini referensi g_adibuat dalam apa yang disebut spesifikasi ECMAScript " VariableEnvironment " yang dilampirkan ke lingkup saat ini - ini mungkin konteks pelaksanaan fungsi dalam kasus menggunakan vardi dalam fungsi (meskipun mungkin sedikit lebih rumit ketika Anda mempertimbangkan let) atau dalam kasus kode "global", VariableEnvironment dilampirkan ke objek global (sering window).

Referensi dalam VariableEnvironment biasanya tidak dapat dihapus - proses yang dirinci dalam ECMAScript 10.5 menjelaskan ini secara rinci, tetapi cukup untuk mengatakan bahwa kecuali kode Anda dieksekusi dalam evalkonteks (yang menggunakan sebagian besar konsol pengembangan berbasis browser), maka variabel yang dideklarasikan dengan vartidak dapat dihapus.

2. Tanpa Menggunakan var

Ketika mencoba untuk menetapkan nilai ke nama tanpa menggunakan varkata kunci, Javascript mencoba untuk menemukan referensi bernama dalam apa yang disebut spesifikasi ECMAScript " LexicalEnvironment ", dan perbedaan utama adalah bahwa LexicalEvironment s bersarang - yaitu LexicalEnvironment memiliki induk ( apa yang disebut spesifikasi ECMAScript sebagai "referensi lingkungan luar") dan ketika Javscript gagal menemukan referensi dalam LexicalEenvironment , itu terlihat di induknya LexicalEnvironment (sebagaimana dirinci dalam 10.3.1 dan 10.2.2.1 ). LexicalEnvironment tingkat atas adalah " lingkungan global", dan yang terikat ke objek global di mana rujukannya adalah properti objek global. Jadi, jika Anda mencoba mengakses nama yang tidak dideklarasikan menggunakan varkata kunci dalam lingkup saat ini atau cakupan luar apa pun, Javascript pada akhirnya akan mengambil sebuah properti dari windowobjek untuk dijadikan referensi itu. Seperti yang telah kita pelajari sebelumnya, properti pada objek dapat dihapus.

Catatan

  1. Penting untuk diingat bahwa vardeklarasi "diangkat" - yaitu deklarasi selalu dianggap terjadi di awal ruang lingkup di mana mereka berada - meskipun bukan inisialisasi nilai yang dapat dilakukan dalam varpernyataan - yang dibiarkan berada di tempat pernyataan . Jadi dalam kode berikut, aadalah referensi dari VariableEnvironment dan bukan windowproperti dan nilainya akan berada 10di akhir kode:

    function test() { a = 5; var a = 10; }

  2. Diskusi di atas adalah ketika "mode ketat" tidak diaktifkan. Aturan pencarian sedikit berbeda ketika menggunakan "mode ketat" dan referensi leksikal yang akan diselesaikan ke properti jendela tanpa "mode ketat" akan menimbulkan "kesalahan variabel" yang tidak dideklarasikan di bawah "mode ketat". Saya tidak benar-benar mengerti di mana ini ditentukan, tetapi bagaimana browser berperilaku.

Dayong
sumber
8
Apa yang Anda katakan adalah kesalahpahaman umum tetapi sebenarnya salah - dalam Javascript tidak ada "variabel global". Variabel yang didefinisikan tanpa lingkup eksplisit (seperti menggunakan varfungsi di luar) adalah properti dari "objek global", yang ada di browser web window. Jadi - var a = 1; delete window.a; console.log(a);akan berhasil menghapus variabel dan menyebabkan baris terakhir mengeluarkan kesalahan referensi.
Guss
7
@Guss, kode Anda var a = 1; delete window.a; console.log(a);menampilkan 1.
Dayong
5
Saya menggunakan Google Chrome v36. Saya menguji pada browser lain. Sepertinya itu bukan browser silang yang konsisten. Chrome dan Opera ditampilkan 1, sementara Firefox, Safari dan IE 11 di komputer saya memberikan kesalahan.
Dayong
3
Ok, kesalahanku. Lihat ecma-international.org/ecma-262/5.1/#sec-10.5 (sub-poin 2 dan 8.c.ii): Saat menjalankan pengujian saya di konsol pengembang, umumnya dianggap "konteks yang jauh" (walaupun mungkin tidak di Chrome), jadi itu akan menimbulkan kesalahan. Kode yang sama dalam konteks global dokumen nyata akan menampilkan 1dengan benar di semua browser. Berjalan di dokumen nyata, contoh kode Anda sudah benar. Saya memilih jawaban Anda sebagai benar, tetapi saya akan menghargai jika Anda dapat mengeditnya untuk menyertakan penjelasan window.a = 1; delete window.a;dan mungkin mekanismenya. Saya bisa melakukannya juga jika Anda tidak keberatan.
Guss
2
@KlaiderKlai ya. Variabel fungsi cakupan dibuat dan dimusnahkan setiap kali ketika fungsi dieksekusi. Mungkin penutupan adalah pengecualian.
Dayong
278

Jawaban @ scunlife akan bekerja, tetapi secara teknis seharusnya

delete window.some_var; 

delete seharusnya menjadi no-op ketika target bukan properti objek. misalnya,

(function() {
   var foo = 123;
   delete foo; // wont do anything, foo is still 123
   var bar = { foo: 123 };
   delete bar.foo; // foo is gone
}());

Tetapi karena variabel global sebenarnya adalah anggota objek jendela, ia berfungsi.

Ketika rantai prototipe terlibat, menghapus menggunakan menjadi lebih kompleks karena hanya menghilangkan properti dari objek target, dan bukan prototipe. misalnya,

function Foo() {}
Foo.prototype = { bar: 123 };
var foo = new Foo();
// foo.bar is 123
foo.bar = 456;
// foo.bar is now 456
delete foo.bar;
// foo.bar is 123 again.

Jadi berhati-hatilah.

EDIT: Jawaban saya agak tidak akurat (lihat "Kesalahpahaman" di bagian akhir). Tautan menjelaskan semua detail berdarah, tetapi ringkasannya adalah bahwa mungkin ada perbedaan besar antara browser dan tergantung pada objek yang Anda hapus. delete object.somePropumumnya harus aman selama object !== window. Saya masih tidak akan menggunakannya untuk menghapus variabel yang dideklarasikan dengan varmeskipun Anda bisa dalam keadaan yang tepat.

Nuh
sumber
14
terima kasih @jedierikb untuk tautan ke artikel menarik itu. lebih khusus untuk bagian ini < perfectionkills.com/understanding-delete/#misconceptions > dari artikel di mana penulis menyatakan bahwa pernyataan noah "delete seharusnya no-op" agak tidak akurat bersama dengan penjelasan yang sangat baik mengapa penjelasan ini tidak akurat . (Jangan tembak kurirnya!)
Rob Wells
2
Sehubungan dengan kalimat terakhir dari jawaban yang direvisi, satu-satunya keadaan di mana Anda dapat menghapus variabel yang dideklarasikan varadalah ketika variabel tersebut dideklarasikan dengan eval.
Stephen Booher
1
Dalam hal ini , pernyataan hapus tampaknya tidak melakukan apa-apa sama sekali. Apa yang terjadi di sini?
Anderson Green
@ AndersonGreen — variabel global yang telah di-unduh dibuat dengan flag DontDelete sehingga tidak dapat dihapus. Kode itu berperilaku tepat seperti yang diharapkan.
RobG
35

Jika Anda secara implisit mendeklarasikan variabel tanpa var, cara yang tepat adalah menggunakan delete foo.

Namun setelah Anda menghapusnya, jika Anda mencoba menggunakan ini dalam operasi seperti penambahan ReferenceErrorakan dibuang karena Anda tidak dapat menambahkan string ke pengidentifikasi yang tidak dideklarasikan, tidak terdefinisi. Contoh:

x = 5;
delete x
alert('foo' + x )
// ReferenceError: x is not defined

Dalam beberapa situasi mungkin lebih aman untuk menetapkannya sebagai false, null, atau undefined sehingga dideklarasikan dan tidak akan membuang jenis kesalahan ini.

foo = false

Perhatikan bahwa di ECMAScript null, false, undefined, 0, NaN, atau ''semua akan mengevaluasi false. Pastikan Anda tidak menggunakan !==operator tetapi !=saat mengetik memeriksa boolean dan Anda tidak ingin memeriksa identitas (begitu nulljuga == falsedan false == undefined).

Perhatikan juga bahwa deletetidak "menghapus" referensi tetapi hanya properti secara langsung pada objek, misalnya:

bah = {}, foo = {}; bah.ref = foo;

delete bah.ref;
alert( [bah.ref, foo ] )
// ,[object Object] (it deleted the property but not the reference to the other object)

Jika Anda telah mendeklarasikan variabel dengan varAnda tidak dapat menghapusnya:

(function() {
    var x = 5;
    alert(delete x)
    // false
})();

Dalam Badak:

js> var x
js> delete x
false

Anda juga tidak dapat menghapus beberapa properti yang telah ditentukan seperti Math.PI:

js> delete Math.PI
false

Ada beberapa pengecualian aneh untuk deletebahasa apa pun, jika Anda cukup peduli, Anda harus membaca:

meder omuraliev
sumber
Terima kasih atas jawaban lengkapnya dengan semua detailnya. Saya menandai ini untuk ini, tetapi saya telah menerima jawaban Nuh karena saya percaya bahwa untuk pertanyaan singkat, keringkasan lebih penting daripada penyelesaian. Sekali lagi - terima kasih atas kerja keras yang Anda lakukan pada jawaban ini.
Guss
30
some_var = null;

//or remove it..
delete some_var;
scunliffe
sumber
11
Ini tidak berfungsi jika ruang lingkup kode ini adalah fungsi. Lihat jawaban @ noah untuk solusi yang benar.
Roatin Marth
1
Terima kasih atas jawabannya, tetapi saya telah menerima jawaban Nuh karena lebih baik menjelaskan perangkap delete.
Guss
3
jangan khawatir ... Saya memberikan jawaban sederhana "cepat dan kotor" - @noah menambahkan semua detail untuk kasus "lain" sehingga dia juga layak mendapatkan kredit. ;-)
scunliffe
7
Ini tidak benar. deletehanya berfungsi untuk properti. Pengaturan itu nullvariabel masih ada.
Derek 朕 會 功夫
1
Jawaban ini cukup baik untuk kasus yang paling mungkin di mana Anda memeriksa dengan "if (some_var) {..}"
BearCode
16

TLDR: sederhana didefinisikan variabel (tanpa var, let, const) dapat dihapus dengan delete. Jika Anda menggunakan var, let, const- mereka tidak dapat dihapus tidak dengan deleteatau dengan Reflect.deleteProperty.

Chrome 55:

simpleVar = "1";
"1"
delete simpleVar;
true
simpleVar;
VM439:1 Uncaught ReferenceError: simpleVar is not defined
    at <anonymous>:1:1
(anonymous) @ VM439:1
var varVar = "1";
undefined
delete varVar;
false
varVar;
"1"
let letVar = "1";
undefined
delete letVar;
true
letVar;
"1"
const constVar="1";
undefined
delete constVar;
true
constVar;
"1"
Reflect.deleteProperty (window, "constVar");
true
constVar;
"1"
Reflect.deleteProperty (window, "varVar");
false
varVar;
"1"
Reflect.deleteProperty (window, "letVar");
true
letVar;
"1"

FF Nightly 53.0a1 menunjukkan perilaku yang sama.

Serj.by
sumber
Jawaban Anda secara teknis benar, sehingga Anda mendapatkan poin, tetapi semua yang Anda tulis dicakup oleh jawaban yang dipilih dengan lebih banyak detail dan referensi untuk spesifikasi ECMAScript - di masa depan akan berguna untuk meninjau jawaban yang ada sebelum memposting.
Guss
5
Sepakat. Tapi hanya ada satu varkasus. Bagi saya itu menarik untuk diuji dan dibagikan letserta constkasing juga. Namun, terima kasih atas perhatiannya. Akan mencoba lebih spesifik waktu berikutnya.
Serj.by
4

ECMAScript 2015 menawarkan Reflect API. Dimungkinkan untuk menghapus properti objek dengan Reflect.deleteProperty () :

Reflect.deleteProperty(myObject, 'myProp');
// it is equivalent to:
delete myObject.myProp;
delete myObject['myProp'];

Untuk menghapus properti windowobjek global :

Reflect.deleteProperty(window, 'some_var');

Dalam beberapa kasus, properti tidak dapat dihapus (ketika properti tidak dapat dikonfigurasi) dan kemudian fungsi ini kembali false(serta menghapus operator ). Dalam kasus lain pengembalian true:

Object.defineProperty(window, 'some_var', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: 'some_val'
});

var frozen = Object.freeze({ myProperty: 'myValue' });
var regular = { myProperty: 'myValue' };
var blank = {};

console.log(Reflect.deleteProperty(window, 'some_var')); // false
console.log(window.some_var); // some_var

console.log(Reflect.deleteProperty(frozen, 'myProperty')); // false
console.log(frozen.myProperty); // myValue

console.log(Reflect.deleteProperty(regular, 'myProperty')); // true
console.log(regular.myProperty); // undefined

console.log(Reflect.deleteProperty(blank, 'notExistingProperty')); // true
console.log(blank.notExistingProperty); // undefined

Ada perbedaan antara deletePropertyfungsi dan deleteoperator saat dijalankan dalam mode ketat:

'use strict'

var frozen = Object.freeze({ myProperty: 'myValue' });

Reflect.deleteProperty(frozen, 'myProperty'); // false
delete frozen.myProperty;
// TypeError: property "myProperty" is non-configurable and can't be deleted
madox2
sumber
4

Variabel, berbeda dengan properti sederhana, memiliki atribut [[Dapat Dikonfigurasi]] , yang berarti ketidakmungkinan untuk menghapus variabel melalui operator hapus . Namun ada satu konteks eksekusi yang tidak memengaruhi aturan ini. Ini adalah konteks eval : ada [[Dapat Dikonfigurasi]] atribut tidak diatur untuk variabel.

Eldiyar Talantbek
sumber
3

Selain apa yang ditulis semua orang, catat juga bahwa deletemengembalikan boolean. Ini dapat memberi tahu Anda apakah penghapusan berhasil atau tidak.

Pengujian di Chrome, semuanya kecuali letdapat dihapus. ketika deletedikembalikan trueitu benar-benar menghapusnya:

implicit_global = 1;
window.explicit_global = 1;
function_set = function() {};
function function_dec() { };
var declared_variable = 1;
let let_variable = 1;

delete delete implicit_global; // true, tested on Chrome 52
delete window.explicit_global; // true, tested on Chrome 52
delete function_set; // true, tested on Chrome 52
delete function_dec; // true, tested on Chrome 52
delete declared_variable; // true, tested on Chrome 52
delete let_variable; // false, tested on Chrome 78
oriadam
sumber
Itu tidak selalu benar. Terutama di Chrome. Firefox mengembalikan semuanya dengan benar. Tidak menguji di peramban lain. Adapun letvars dan constvars itu mengembalikan benar apa yang seharusnya berarti bahwa variabel dihapus tetapi tidak. Anda bisa memeriksanya di Chrome dan FF. FF tampaknya mengembalikan nilai yang benar sementara Chrome tidak. Jadi jangan yakin Anda benar-benar bisa mengandalkannya. Coba lihat:let letVar = "1"; undefined delete letVar; true letVar "1" typeof letVar; "string" const constVar="1"; undefined delete constVar; true constVar; "1" typeof constVar; "string"
Serj.by
1
Seperti yang disebutkan jedierikb di bawah ini, ada artikel sempurna oleh kangax perfectionkills.com/understanding-delete yang kebanyakan menjelaskan mengapa dan bagaimana deleteoperator bekerja. Tapi itu tidak menggambarkan mengapa situasi benar-benar berlawanan dengan fungsi. Sayang sekali. Namun, mengenai variabel, hal-hal mulai tampak jauh lebih jelas.
Serj.by
2

Anda tidak dapat menghapus variabel jika Anda mendeklarasikannya (dengan var x;) pada saat pertama kali digunakan. Namun, jika variabel x Anda pertama kali muncul di skrip tanpa deklarasi, maka Anda dapat menggunakan operator hapus (delete x;) dan variabel Anda akan dihapus, sangat mirip dengan menghapus elemen array atau menghapus properti objek. .

Satyapriya Mishra
sumber
1

Saya agak bingung. Jika semua yang Anda inginkan adalah agar nilai variabel tidak diteruskan ke skrip lain, maka tidak perlu menghapus variabel dari ruang lingkup. Cukup batalkan variabelnya, kemudian periksa secara eksplisit apakah variabel itu null atau bukan. Mengapa harus melalui masalah menghapus variabel dari ruang lingkup? Apa tujuan server ini yang membatalkan tidak bisa?

foo = null;
if(foo === null) or if(foo !== null)
designrumus
sumber
Syaratnya adalah skrip pesanan, yang tidak di bawah kendali saya, tidak akan melihat bahwa variabel ada - khusus untuk kasus OP, skrip target memiliki perilaku untuk nullnilai yang tidak ingin saya picu.
Guss
Tidak ada "backend" yang disalahgunakan selama pembuatan pertanyaan ini. Ini hanya beberapa skrip di situs web di mana saya tidak memiliki kendali apa pun kecuali skrip yang satu ini.
Guss
Apakah kedua skrip dalam dokumen yang sama atau dalam dokumen terpisah yang satu memanggil yang lain untuk dimuat? Anda menyebutkan skrip pesanan dan skrip target. Jika itu adalah masalah variabel yang diteruskan ke skrip lain melalui variabel get / post, maka saya akan menghapusnya di backend sebelum ada javascript yang mendapatkannya. Contoh dari ini di php akan menjadi sesuatu seperti. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm
Saya melihat. Nah jika ada checks and balances untuk null maka set ke nilai script target tidak akan melakukan apa-apa dengan tampaknya lebih logis kemudian menghapus variabel dari lingkup, tetapi Anda tampaknya memiliki jawaban Anda, jadi saya akan membiarkan kuda berbaring. Terima kasih atas tanggapan Anda.
designdrumm
Satu pertanyaan singkat. Akankah ada skrip yang dipanggil setelah skrip Anda yang tidak akan berada dalam kendali Anda tetapi masih akan memerlukan variabel ini? Jika demikian, maka menghapus variabel dari ruang lingkup adalah ide yang buruk.
designdrumm