Terkejut bahwa variabel global memiliki nilai yang tidak ditentukan di JavaScript

89

Hari ini, saya sangat terkejut ketika saya melihat variabel global memiliki undefinednilai dalam kasus tertentu.

Contoh:

var value = 10;
function test() {
    //A
    console.log(value);
    var value = 20;

    //B
    console.log(value);
}
test();

Memberikan keluaran sebagai

undefined
20

Di sini, mengapa mesin JavaScript mempertimbangkan nilai global sebagai undefined? Saya tahu bahwa JavaScript adalah bahasa yang ditafsirkan. Bagaimana cara mempertimbangkan variabel dalam fungsi?

Apakah itu jebakan dari mesin JavaScript?

iamjustcoder
sumber

Jawaban:

177

Fenomena ini dikenal sebagai: JavaScript Variable Hoisting .

Anda tidak akan pernah mengakses variabel global dalam fungsi Anda; Anda hanya pernah mengakses valuevariabel lokal .

Kode Anda setara dengan berikut ini:

var value = 10;

function test() {
    var value;
    console.log(value);

    value = 20;
    console.log(value);
}

test();

Masih terkejut Anda mendapatkannya undefined?


Penjelasan:

Ini adalah sesuatu yang setiap programmer JavaScript akan temui cepat atau lambat. Sederhananya, variabel apa pun yang Anda nyatakan selalu diangkat ke atas penutupan lokal Anda. Jadi, meskipun Anda mendeklarasikan variabel Anda setelah console.logpanggilan pertama , itu masih dianggap seolah-olah Anda telah mendeklarasikannya sebelumnya.
Namun, hanya bagian deklarasi yang diangkat; tugas, di sisi lain, tidak.

Jadi, ketika Anda pertama kali memanggil console.log(value), Anda mereferensikan variabel yang dideklarasikan secara lokal, yang belum ada yang ditugaskan padanya; karenanya undefined.

Berikut contoh lainnya :

var test = 'start';

function end() {
    test = 'end';
    var test = 'local';
}

end();
alert(test);

Menurut Anda apa yang akan mengingatkan ini? Tidak, jangan hanya membaca, pikirkanlah. Apa nilainya test?

Jika Anda mengatakan sesuatu selain start, Anda salah. Kode di atas sama dengan ini:

var test = 'start';

function end() {
    var test;
    test = 'end';
    test = 'local';
}

end();
alert(test);

sehingga variabel global tidak pernah terpengaruh.

Seperti yang Anda lihat, di mana pun Anda meletakkan deklarasi variabel, deklarasi itu selalu diangkat ke atas penutupan lokal Anda.


Catatan samping:

Ini juga berlaku untuk fungsi.

Pertimbangkan bagian kode ini :

test("Won't work!");

test = function(text) { alert(text); }

yang akan memberi Anda kesalahan referensi:

ReferenceError Tidak Tertangkap: tes tidak ditentukan

Ini membuang banyak pengembang, karena potongan kode ini berfungsi dengan baik:

test("Works!");

function test(text) { alert(text); }

Alasannya, seperti yang dinyatakan, karena bagian penugasan tidak diangkat. Jadi pada contoh pertama, ketika test("Won't work!")dijalankan, testvariabel telah dideklarasikan, tetapi belum memiliki fungsi yang ditugaskan padanya.

Dalam contoh kedua, kami tidak menggunakan tugas variabel. Sebaliknya, kita menggunakan sintaks deklarasi fungsi yang tepat, yang tidak mendapatkan fungsi benar-benar mengangkat.


Ben Cherry telah menulis artikel yang sangat bagus tentang ini, berjudul Scoping and Hoisting JavaScript .
Membacanya. Ini akan memberi Anda gambaran keseluruhan dengan detail penuh.

Joseph Silber
sumber
27
Ini penjelasan yang bagus. Namun, saya melewatkan solusi di mana Anda dapat mengakses variabel global di dalam suatu fungsi.
DuKes0mE
1
@ DuKes0mE - Anda selalu dapat mengakses variabel global di dalam suatu fungsi.
Joseph Silber
3
ya, dan mengapa kasus A dari posting pembukaan tidak berfungsi dan mengatakan tidak terdefinisi? Saya mengerti, itu akan diartikan sebagai var lokal bukan global, tapi bagaimana itu akan menjadi global?
DuKes0mE
4
untuk mengakses window.value penggunaan varaible global
Venkat Reddy
1
kapan gaya pengangkatan variabel ini diterapkan? apakah ini selalu standar dalam javascript?
Dieskim
55

Saya agak kecewa karena masalah di sini dijelaskan, tetapi tidak ada yang mengusulkan solusi. Jika Anda ingin mengakses variabel global dalam lingkup fungsi tanpa fungsi yang membuat var lokal tidak terdefinisi terlebih dahulu, rujuk var sebagaiwindow.varName

Amalgovinus
sumber
8
Ya, sangat menyebalkan bahwa orang lain tidak mengusulkan solusi karena ini adalah hasil pertama dalam hasil Google. Mereka HAMPIR menjawab pertanyaan sebenarnya yang diajukan tentang itu menjadi jebakan di mesin js .... sigh -> terima kasih atas jawaban Anda
pengguna1567453
2
Itulah perbedaan antara pengetahuan teoritis dan menyelesaikan pekerjaan. Terima kasih atas jawaban itu!
hansTheFranz
Bagi saya, adalah kesalahan jika saya memilih nama global di mana saja dalam fungsi apa pun. Setidaknya membuat kebingungan. Membuat kebutuhan untuk pencarian google, paling buruk. Terima kasih
dcromley
Javascript terus mengejutkan saya setiap hari yang berlalu. Terima kasih kawan, jawabannya sangat membantu.
Raf
Terima kasih atas solusinya, saya menemukan ini setelah var global tidak ditentukan tetapi hanya di Safari. File 'sertakan' lain dan vars global muncul seperti 'google', jadi saya menyalin pendekatan yang digunakan google: window.globalVarFromJS = window.globalVarFromJS || {}; Kemudian saya menemukan solusi Anda jadi saya pikir saya akan menambahkannya.
Ralph Hinkley
10

Variabel dalam JavaScript selalu memiliki cakupan seluruh fungsi. Sekalipun mereka didefinisikan di tengah fungsi, mereka terlihat sebelumnya. Fenomena serupa dapat diamati dengan fungsi mengangkat.

Meskipun demikian , yang pertama console.log(value)melihat valuevariabel (bagian dalam yang membayangi bagian luar value), tetapi belum diinisialisasi. Anda dapat menganggapnya seolah-olah semua deklarasi variabel secara implisit dipindahkan ke awal fungsi ( bukan blok kode paling dalam), sementara definisi ditinggalkan di tempat yang sama.

Lihat juga

Tomasz Nurkiewicz
sumber
Saya selalu menyukai kata-kata sederhana +1 :)
Jashwant
3

Ada variabel global value, tetapi ketika kontrol memasuki testfungsi, valuevariabel lain dideklarasikan, yang membayangi variabel global. Karena deklarasi variabel ( tapi bukan penugasan ) dalam JavaScript diangkat ke atas cakupan di mana deklarasi tersebut dideklarasikan:

//value == undefined (global)
var value = 10;
//value == 10 (global)

function test() {
    //value == undefined (local)
    var value = 20;
    //value == 20 (local)
}
//value == 10 (global)

Perhatikan bahwa hal yang sama berlaku untuk deklarasi fungsi, yang berarti Anda dapat memanggil fungsi sebelum fungsi tersebut tampak didefinisikan dalam kode Anda:

test(); //Call the function before it appears in the source
function test() {
    //Do stuff
}

Perlu juga dicatat bahwa ketika Anda menggabungkan keduanya menjadi ekspresi fungsi, variabel akan tetap undefinedsampai penugasan dilakukan, jadi Anda tidak dapat memanggil fungsi sampai itu terjadi:

var test = function() {
    //Do stuff
};
test(); //Have to call the function after the assignment
James Allardice
sumber
0
  1. Cara termudah untuk menjaga akses ke variabel luar (tidak hanya dalam lingkup global), tentu saja, mencoba untuk tidak mendeklarasikan ulang mereka dengan nama yang sama dalam fungsi; jangan gunakan var di sana. Disarankan untuk menggunakan aturan penamaan deskriptif yang tepat . Dengan itu, akan sulit untuk berakhir dengan variabel bernama seperti nilai (aspek ini tidak selalu terkait dengan contoh dalam pertanyaan karena nama variabel ini mungkin diberikan untuk kesederhanaan).

  2. Jika fungsi tersebut dapat digunakan kembali di tempat lain dan karenanya tidak ada jaminan bahwa variabel luar benar-benar ditentukan dalam konteks baru tersebut, fungsi Evaluasi dapat digunakan. Operasi ini lambat sehingga tidak disarankan untuk fungsi yang menuntut kinerja:

    if (typeof variable === "undefined")
    {
        eval("var variable = 'Some value';");
    }
    
  3. Jika variabel lingkup luar yang ingin Anda akses didefinisikan dalam fungsi bernama, maka variabel itu mungkin dilampirkan ke fungsi itu sendiri di tempat pertama dan kemudian diakses dari mana saja dalam kode - baik itu dari fungsi yang bertingkat atau penangan peristiwa di luar yang lainnya. Perhatikan bahwa mengakses properti jauh lebih lambat dan akan mengharuskan Anda mengubah cara Anda memprogram, jadi ini tidak disarankan kecuali jika benar-benar diperlukan: Variabel sebagai properti fungsi (JSFiddle) :

    // (the wrapper-binder is only necessary for using variables-properties
    // via "this"instead of the function's name)
    var functionAsImplicitObjectBody = function()
    {
        function someNestedFunction()
        {
            var redefinableVariable = "redefinableVariable's value from someNestedFunction";
            console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
            console.log('--> redefinableVariable: ', redefinableVariable);
        }
        var redefinableVariable = "redefinableVariable's value from someFunctionBody";
        console.log('this.variableAsProperty: ', this.variableAsProperty);
        console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
        console.log('redefinableVariable: ', redefinableVariable);
        someNestedFunction();
    },
    functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
    functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
    functionAsImplicitObject();
    
    // (spread-like operator "..." provides passing of any number of arguments to
    // the target internal "func" function in as many steps as necessary)
    var functionAsExplicitObject = function(...arguments)
    {
        var functionAsExplicitObjectBody = {
            variableAsProperty: "variableAsProperty's value",
            func: function(argument1, argument2)
            {
                function someNestedFunction()
                {
                    console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
                        functionAsExplicitObjectBody.variableAsProperty);
                }
                console.log("argument1: ", argument1);
                console.log("argument2: ", argument2);
                console.log("this.variableAsProperty: ", this.variableAsProperty);
                someNestedFunction();
            }    
        };
        return functionAsExplicitObjectBody.func(...arguments);
    };
    functionAsExplicitObject("argument1's value", "argument2's value");
    
DDRRSS
sumber
0

Saya mengalami masalah yang sama bahkan dengan variabel global. Masalah saya, saya menemukan, adalah variabel global tidak bertahan di antara file html.

<script>
    window.myVar = 'foo';
    window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>

Saya mencoba mereferensikan myVar dan myVarTwo di file HTML yang dimuat, tetapi menerima kesalahan yang tidak ditentukan. Singkat cerita / hari, saya menemukan saya bisa mereferensikan variabel menggunakan:

<!DOCTYPE html>
<html lang="en">
    <!! other stuff here !!>
    <script>

        var myHTMLVar = this.parent.myVar

        /* other stuff here */
    </script>
</html>
Nathan Sutherland
sumber