Apa itu ruang lingkup leksikal?


682

Apa pengantar singkat untuk pelingkupan leksikal?


89
Di podcast 58, Joel mendorong pertanyaan seperti ini karena ia ingin SO menjadi tempat untuk jawaban, bahkan jika mereka telah dijawab di tempat lain. Ini adalah pertanyaan yang valid, meskipun orang bisa mengatakannya dengan sedikit lebih sopan.
Ralph M. Rickenbach

5
@rahul Saya mengerti ini pertanyaan lama. Tetapi saya yakin bahkan pada tahun 2009, SO mengharapkan para penanya untuk melakukan upaya dasar untuk menyelesaikannya. Seperti berdiri tidak menunjukkan setiap usaha sama sekali. Bisa jadi, itu sebabnya banyak downvoted?
PP

13
Mungkin saja si penanya tidak (atau tidak) fasih berbahasa Inggris ketika menulis pertanyaan ini
Martin

27
Pertanyaannya sopan, dia hanya mengatakan apa yang dia inginkan. Anda bebas menjawab. Tidak perlu ada polisi yang bisa dipercaya di sini.
Markus Siebeneicher

25
Saya pikir pertanyaan-pertanyaan seperti ini hebat karena ia membangun konten untuk SO. IMO, siapa yang peduli jika pertanyaannya tidak berhasil ... jawabannya akan memiliki konten yang hebat dan itulah yang penting di papan pesan ini.
Jwan622

Jawaban:


687

Saya memahaminya melalui contoh. :)

Pertama, lingkup leksikal (juga disebut lingkup statis ), dalam sintaksis mirip C:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Setiap level dalam dapat mengakses level luarnya.

Ada cara lain, yang disebut lingkup dinamis yang digunakan oleh implementasi pertama Lisp , lagi-lagi dalam sintaksis mirip-C:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Berikut fundapat baik akses xdi dummy1atau dummy2, atau xdalam fungsi panggilan fundengan xmenyatakan di dalamnya.

dummy1();

akan mencetak 5,

dummy2();

akan mencetak 10.

Yang pertama disebut statis karena dapat disimpulkan pada waktu kompilasi, dan yang kedua disebut dinamis karena lingkup luarnya dinamis dan tergantung pada panggilan rantai fungsi.

Saya menemukan pelingkupan statis lebih mudah untuk mata. Sebagian besar bahasa berjalan seperti ini pada akhirnya, bahkan Lisp (dapat melakukan keduanya, bukan?). Pelingkupan dinamis seperti melewatkan referensi semua variabel ke fungsi yang dipanggil.

Sebagai contoh mengapa kompiler tidak dapat menyimpulkan ruang lingkup dinamis luar fungsi, pertimbangkan contoh terakhir kami. Jika kita menulis sesuatu seperti ini:

if(/* some condition */)
    dummy1();
else
    dummy2();

Rantai panggilan tergantung pada kondisi run time. Jika itu benar, maka rantai panggilan terlihat seperti:

dummy1 --> fun()

Jika kondisinya salah:

dummy2 --> fun()

Lingkup luar fundalam kedua kasus adalah penelepon ditambah penelepon penelepon dan sebagainya .

Hanya untuk menyebutkan bahwa bahasa C tidak memungkinkan fungsi bersarang atau pelingkupan dinamis.


19
Saya juga ingin menunjukkan tutorial yang sangat mudah dipahami yang baru saja saya temukan. Contoh Arak bagus, tetapi mungkin terlalu pendek untuk seseorang yang membutuhkan lebih banyak contoh (sebenarnya, membandingkan dengan bahasa lain ..). Lihatlah. Penting untuk memahami ini , karena kata kunci itu akan mengarahkan kita untuk memahami ruang lingkup leksikal. howtonode.org/what-is-this
CppLearner

9
Ini jawaban yang bagus. Tapi pertanyaannya ditandai JavaScript. Karena itu saya pikir ini tidak boleh ditandai sebagai jawaban yang diterima. Ruang lingkup leksikal khusus di JS berbeda
Boyang

6
Jawaban yang sangat bagus. Terima kasih. @ Boyang saya tidak setuju. Saya bukan pembuat kode Lisp, tetapi menemukan contoh Lisp bermanfaat, karena ini adalah contoh pelingkupan dinamis, yang tidak Anda dapatkan di JS.
dudewad

4
Awalnya saya pikir contohnya adalah kode C yang valid dan bingung apakah ada pelingkupan dinamis dalam C. Mungkin disclaimer pada akhirnya bisa digeser ke sebelum kode contoh?
Yangshun Tay

2
Ini masih jawaban yang sangat membantu tapi saya pikir @Boyang sudah benar. Jawaban ini mengacu pada 'level', yang lebih sesuai dengan cakupan lingkup blok yang dimiliki C. JavaScript secara default tidak memiliki ruang lingkup tingkat blok, jadi di dalam forlingkaran adalah masalah umum. Lingkup leksikal untuk JavaScript hanya pada tingkat fungsi kecuali ES6 letatau constdigunakan.
icc97

275

Mari kita coba definisi sesingkat mungkin:

Lexical Scoping mendefinisikan bagaimana nama variabel dipecahkan dalam fungsi bersarang: fungsi dalam berisi lingkup fungsi induk bahkan jika fungsi induk telah kembali .

Hanya itu yang ada di sana!


21
Bagian terakhir: "bahkan jika fungsi induk telah kembali" disebut Penutupan.
Juanma Menendez

1
Memahami Pelingkupan Leksikal & penutupan hanya dalam satu kalimat. Terima kasih!!
Penjara Bawah Tanah

63
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Kode di atas akan mengembalikan "Saya hanya lokal". Itu tidak akan mengembalikan "Saya seorang global". Karena fungsi func () menghitung di mana awalnya didefinisikan yang berada di bawah lingkup fungsi whatismyscope.

Itu tidak akan mengganggu dari apa pun namanya (lingkup global / dari dalam fungsi lain bahkan), itu sebabnya nilai lingkup global saya global tidak akan dicetak.

Ini disebut pelingkupan leksikal di mana " fungsi dijalankan menggunakan rantai lingkup yang berlaku ketika didefinisikan " - menurut Panduan Definisi JavaScript.

Lingkup leksikal adalah konsep yang sangat kuat.

Semoga ini membantu..:)


3
itu penjelasan yang sangat bagus saya ingin menambahkan satu hal lagi jika Anda menulis fungsi func () {return this.scope;} maka itu akan mengembalikan "Saya global" cukup gunakan kata kunci ini dan ruang lingkup Anda akan berubah
Rajesh Kumar Bhawsar


41

Lingkup mendefinisikan area, di mana fungsi, variabel dan semacamnya tersedia. Ketersediaan variabel misalnya didefinisikan dalam konteksnya, katakanlah fungsi, file, atau objek, mereka didefinisikan. Kami biasanya memanggil variabel lokal ini.

Bagian leksikal berarti bahwa Anda dapat memperoleh ruang lingkup dari membaca kode sumber.

Lingkup leksikal juga dikenal sebagai lingkup statis.

Cakupan dinamis mendefinisikan variabel global yang dapat dipanggil atau dirujuk dari mana saja setelah ditetapkan. Kadang-kadang mereka disebut variabel global, meskipun variabel global dalam sebagian besar bahasa programmin memiliki ruang lingkup leksikal. Ini berarti, dapat diturunkan dari membaca kode bahwa variabel tersedia dalam konteks ini. Mungkin kita harus mengikuti kegunaan atau menyertakan klausa untuk menemukan instatiation atau definisi, tetapi kode / kompiler tahu tentang variabel di tempat ini.

Sebaliknya, dalam pelingkupan dinamis, Anda mencari di fungsi lokal, lalu Anda mencari di fungsi yang disebut fungsi lokal, lalu Anda mencari di fungsi yang memanggil fungsi itu, dan seterusnya, naik tumpukan panggilan. "Dinamis" mengacu pada perubahan, di mana tumpukan panggilan dapat berbeda setiap kali fungsi yang diberikan dipanggil, dan fungsi tersebut mungkin mengenai variabel yang berbeda tergantung dari mana ia dipanggil. (lihat disini )

Untuk melihat contoh menarik untuk ruang lingkup dinamis, lihat di sini .

Untuk perincian lebih lanjut lihat di sini dan di sini .

Beberapa contoh dalam Delphi / Object Pascal

Delphi memiliki ruang lingkup leksikal.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

Delphi terdekat dengan ruang lingkup dinamis adalah pasangan fungsi RegisterClass () / GetClass (). Untuk penggunaannya lihat di sini .

Katakanlah waktu RegisterClass ([TmyClass]) dipanggil untuk mendaftar kelas tertentu tidak dapat diprediksi dengan membaca kode (dipanggil dengan metode klik tombol yang dipanggil oleh pengguna), kode panggilan GetClass ('TmyClass') akan mendapatkan sebuah hasil atau tidak. Panggilan ke RegisterClass () tidak harus dalam lingkup leksikal unit menggunakan GetClass ();

Kemungkinan lain untuk ruang lingkup dinamis adalah metode anonim (penutupan) dalam Delphi 2009, karena mereka tahu variabel dari fungsi panggilan mereka. Itu tidak mengikuti jalur panggilan dari sana secara rekursif dan karena itu tidak sepenuhnya dinamis.


2
Sebenarnya private dapat diakses di seluruh unit di mana kelas didefinisikan. Inilah mengapa "Strict private" diperkenalkan pada D2006.
Marco van de Voort

2
+1 untuk bahasa biasa (sebagai lawan dari bahasa rumit dan contoh tanpa banyak penjelasan)
Pops

36

Saya suka jawaban agnostik bahasa dan fitur lengkap dari orang-orang seperti @rak. Karena pertanyaan ini ditandai dengan JavaScript , saya ingin memasukkan beberapa catatan yang sangat spesifik untuk bahasa ini.

Dalam JavaScript, pilihan kami untuk pelingkupan adalah:

  • apa adanya (tidak ada penyesuaian lingkup)
  • leksikal var _this = this; function callback(){ console.log(_this); }
  • terikat callback.bind(this)

Perlu dicatat, saya pikir, bahwa JavaScript tidak benar-benar memiliki pelingkupan dinamis . .bindmenyesuaikan thiskata kunci, dan itu dekat, tetapi secara teknis tidak sama.

Berikut adalah contoh yang menunjukkan kedua pendekatan. Anda melakukan ini setiap kali Anda membuat keputusan tentang bagaimana melakukan cakupan panggilan balik sehingga ini berlaku untuk janji, penangan acara, dan banyak lagi.

Leksikal

Inilah yang mungkin Anda sebut Lexical Scopingsebagai panggilan balik dalam JavaScript:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // Request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Terikat

Cara lain untuk lingkup adalah menggunakan Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // Create a function object bound to `this`
  }
//...

Metode-metode ini, sejauh yang saya tahu, setara secara perilaku.


Penggunaan bindtidak memengaruhi cakupan.
Ben Aston

12

Ruang lingkup Lexical: Variabel dideklarasikan di luar fungsi adalah variabel global dan terlihat di mana-mana dalam program JavaScript. Variabel yang dideklarasikan di dalam fungsi memiliki cakupan fungsi dan hanya dapat dilihat oleh kode yang muncul di dalam fungsi tersebut.


12

IBM mendefinisikannya sebagai:

Bagian dari unit program atau segmen di mana deklarasi berlaku. Identifier yang dideklarasikan dalam sebuah rutin diketahui dalam rutin itu dan di dalam semua rutinitas bersarang. Jika rutin bersarang mendeklarasikan item dengan nama yang sama, item luar tidak tersedia dalam rutinitas bersarang.

Contoh 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Contoh 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();

8

Lingkup leksikal berarti bahwa dalam kelompok fungsi bersarang, fungsi dalam memiliki akses ke variabel dan sumber daya lain dari lingkup induknya . Ini berarti bahwa fungsi anak terikat secara leksikal dengan konteks eksekusi orang tua mereka. Lingkup leksikal terkadang juga disebut sebagai lingkup statis .

function grandfather() {
    var name = 'Hammad';
    // 'likes' is not accessible here
    function parent() {
        // 'name' is accessible here
        // 'likes' is not accessible here
        function child() {
            // Innermost level of the scope chain
            // 'name' is also accessible here
            var likes = 'Coding';
        }
    }
}

Hal yang akan Anda perhatikan tentang ruang lingkup leksikal adalah bahwa ia bekerja maju, artinya nama dapat diakses oleh konteks eksekusi anak-anaknya. Tapi itu tidak bekerja mundur ke orang tuanya, artinya variabel likestidak dapat diakses oleh orang tuanya.

Ini juga memberi tahu kita bahwa variabel yang memiliki nama yang sama dalam konteks eksekusi yang berbeda mendapatkan prioritas dari atas ke bawah tumpukan eksekusi. Variabel, memiliki nama yang mirip dengan variabel lain, dalam fungsi terdalam (konteks paling atas dari tumpukan eksekusi) akan memiliki prioritas lebih tinggi.

Perhatikan bahwa ini diambil dari sini .


8

Dalam bahasa sederhana, ruang lingkup leksikal adalah variabel yang didefinisikan di luar ruang lingkup Anda atau ruang lingkup atas secara otomatis tersedia di dalam ruang lingkup Anda yang berarti Anda tidak perlu meneruskannya di sana.

Contoh:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// Output: JavaScript


2
Jawaban terpendek dan terbaik bagi saya dengan contoh. Bisa ditambahkan bahwa fungsi panah ES6 menyelesaikan masalah dengan bind. Dengan mereka, bindtidak lagi diperlukan. Untuk informasi lebih lanjut tentang perubahan ini, periksa stackoverflow.com/a/34361380/11127383
Daniel Danielecki

4

Ada bagian penting dari percakapan seputar pelingkupan leksikal dan dinamis yang hilang: penjelasan sederhana tentang masa pakai dari variabel yang dicakup - atau ketika variabel dapat diakses.

Pelingkupan dinamis hanya sangat longgar sesuai dengan pelingkupan "global" dengan cara yang kita pikirkan secara tradisional (alasan saya mengemukakan perbandingan antara keduanya adalah karena hal itu telah disebutkan - dan saya tidak terlalu suka yang terkait penjelasan artikel yang ); mungkin yang terbaik adalah kita tidak membuat perbandingan antara global dan dinamis - meskipun menurut dugaan, menurut artikel yang ditautkan, "... [itu] berguna sebagai pengganti variabel yang dicakup secara global."

Jadi, dalam bahasa Inggris yang sederhana, apa perbedaan penting antara kedua mekanisme pelingkupan?

Pelingkupan leksikal telah didefinisikan dengan sangat baik di seluruh jawaban di atas: variabel yang dicakup secara leksikal tersedia - atau, dapat diakses - di tingkat lokal dari fungsi yang didefinisikan.

Namun - karena ini bukan fokus OP - pelingkupan dinamis belum menerima banyak perhatian dan perhatian yang diterimanya berarti mungkin perlu sedikit lebih banyak (itu bukan kritik atas jawaban lain, tetapi lebih merupakan "oh, jawaban itu membuat kami berharap ada sedikit lagi "). Jadi, ini sedikit lebih banyak:

Pelingkupan dinamis berarti bahwa suatu variabel dapat diakses oleh program yang lebih besar selama masa pemanggilan fungsi - atau, ketika fungsi tersebut dieksekusi. Sungguh, Wikipedia benar-benar melakukan pekerjaan yang baik dengan penjelasan tentang perbedaan antara keduanya. Agar tidak mengaburkannya, berikut adalah teks yang menjelaskan pelingkupan dinamis:

... [I] n pelingkupan dinamis (atau ruang lingkup dinamis), jika ruang lingkup nama variabel adalah fungsi tertentu, maka ruang lingkupnya adalah periode waktu di mana fungsi tersebut dijalankan: ketika fungsi sedang berjalan, nama variabel ada , dan terikat ke variabelnya, tetapi setelah fungsi kembali, nama variabel tidak ada.


3

Ruang lingkup leksikal berarti bahwa suatu fungsi mencari variabel dalam konteks di mana ia didefinisikan, dan tidak dalam lingkup segera di sekitarnya.

Lihatlah bagaimana lingkup leksikal bekerja di Lisp jika Anda ingin lebih detail. Jawaban yang dipilih oleh Kyle Cronin dalam variabel Dynamic dan Lexical dalam Common Lisp jauh lebih jelas daripada jawaban di sini.

Secara kebetulan saya hanya belajar tentang ini di kelas Lisp, dan itu berlaku untuk JavaScript juga.

Saya menjalankan kode ini di konsol Chrome.

// JavaScript               Equivalent Lisp
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x ()
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

Keluaran:

5
10
5

3

Lingkup leksikal dalam JavaScript berarti bahwa variabel yang ditentukan di luar fungsi dapat diakses di dalam fungsi lain yang ditentukan setelah deklarasi variabel. Tetapi yang sebaliknya tidak benar; variabel yang didefinisikan di dalam suatu fungsi tidak akan dapat diakses di luar fungsi itu.

Konsep ini banyak digunakan dalam penutupan dalam JavaScript.

Katakanlah kita memiliki kode di bawah ini.

var x = 2;
var add = function() {
    var y = 1;
    return x + y;
};

Sekarang, ketika Anda memanggil add () -> ini akan mencetak 3.

Jadi, fungsi add () mengakses variabel global xyang didefinisikan sebelum fungsi metode ditambahkan. Ini disebut karena pelingkupan leksikal dalam JavaScript.


Pertimbangkan bahwa cuplikan kode adalah untuk bahasa dengan cakupan dinamis. Jika add()fungsi yang dipanggil segera mengikuti potongan kode yang diberikan, itu juga akan mencetak 3. Ruang lingkup Lexical tidak hanya berarti bahwa suatu fungsi dapat mengakses variabel global di luar konteks lokal. Jadi kode contoh benar-benar tidak membantu menunjukkan apa arti pelingkupan leksikal. Menampilkan pelingkupan leksikal dalam kode benar-benar membutuhkan contoh balasan atau setidaknya penjelasan tentang kemungkinan interpretasi kode lainnya.
C Perkins

2

Lingkup leksikal mengacu pada leksikon pengidentifikasi (misalnya, variabel, fungsi, dll.) Yang terlihat dari posisi saat ini di tumpukan eksekusi.

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

foo dan bar selalu berada dalam leksikon pengidentifikasi yang tersedia karena bersifat global.

Ketika function1dijalankan, ia memiliki akses ke leksikon foo2, bar2, foo, dan bar.

Ketika function2dijalankan, ia memiliki akses ke leksikon foo3, bar3, foo2, bar2, foo, dan bar.

Alasan fungsi global dan / atau luar tidak memiliki akses ke pengidentifikasi fungsi dalam adalah karena pelaksanaan fungsi itu belum terjadi dan oleh karena itu, tidak ada pengidentifikasi yang telah dialokasikan ke memori. Terlebih lagi, begitu konteks dalam dieksekusi, itu dihapus dari tumpukan eksekusi, yang berarti bahwa semua pengidentifikasi itu telah dikumpulkan dan tidak lagi tersedia.

Akhirnya, inilah sebabnya konteks eksekusi bersarang dapat SELALU mengakses konteks eksekusi leluhurnya dan karenanya mengapa ia memiliki akses ke leksikon pengidentifikasi yang lebih besar.

Lihat:

Terima kasih khusus kepada @ robr3rd untuk membantu menyederhanakan definisi di atas.


1

Inilah sudut pandang yang berbeda pada pertanyaan ini yang bisa kita dapatkan dengan mengambil langkah mundur dan melihat peran pelingkupan dalam kerangka interpretasi yang lebih besar (menjalankan program). Dengan kata lain, bayangkan Anda sedang membangun seorang juru bahasa (atau kompiler) untuk suatu bahasa dan bertanggung jawab untuk menghitung hasilnya, diberikan suatu program dan beberapa masukan untuk itu.

Interpretasi melibatkan mencatat tiga hal:

  1. Status - yaitu, variabel dan lokasi memori yang dirujuk pada heap dan stack.

  2. Operasi pada keadaan itu - yaitu, setiap baris kode dalam program Anda

  3. The lingkungan di mana diberikan operasi berjalan - yaitu, proyeksi negara pada operasi.

Seorang penerjemah mulai pada baris pertama kode dalam suatu program, menghitung lingkungannya, menjalankan baris dalam lingkungan itu dan menangkap pengaruhnya pada keadaan program. Kemudian mengikuti alur kontrol program untuk mengeksekusi baris kode berikutnya, dan mengulangi proses sampai program berakhir.

Cara Anda menghitung lingkungan untuk operasi apa pun adalah melalui seperangkat aturan formal yang ditentukan oleh bahasa pemrograman. Istilah "mengikat" sering digunakan untuk menggambarkan pemetaan keadaan keseluruhan program ke nilai di lingkungan. Perhatikan bahwa dengan "keseluruhan negara" kami tidak berarti negara global, tetapi lebih merupakan jumlah total dari setiap definisi yang dapat dicapai, di setiap titik dalam eksekusi).

Ini adalah kerangka kerja di mana masalah pelingkupan didefinisikan. Sekarang ke bagian selanjutnya dari apa pilihan kita.

  • Sebagai pelaksana juru bahasa, Anda dapat menyederhanakan tugas Anda dengan membuat lingkungan sedekat mungkin dengan keadaan program. Dengan demikian, lingkungan dari suatu baris kode hanya akan ditentukan oleh lingkungan dari baris kode sebelumnya dengan efek operasi yang diterapkan padanya, terlepas dari apakah baris sebelumnya adalah penugasan, panggilan fungsi, kembali dari suatu fungsi, atau struktur kontrol seperti loop sementara.

Ini adalah inti dari pelingkupan dinamis , di mana lingkungan tempat kode berjalan terikat dengan keadaan program sebagaimana ditentukan oleh konteks pelaksanaannya.

  • Atau , Anda bisa memikirkan seorang programmer menggunakan bahasa Anda dan menyederhanakan tugasnya untuk melacak nilai-nilai yang bisa diambil oleh variabel. Ada terlalu banyak jalan dan terlalu banyak kerumitan yang terlibat dalam penalaran tentang hasil totalitas dari eksekusi di masa lalu. Lexical Scoping membantu melakukan ini dengan membatasi lingkungan saat ini ke bagian negara yang ditentukan dalam blok saat ini, fungsi atau unit lingkup lainnya, dan induknya (yaitu blok yang melingkupi jam saat ini, atau fungsi yang disebut fungsi sekarang).

Dengan kata lain, dengan lingkup leksikal lingkungan yang dilihat oleh kode apa pun terikat dengan keadaan yang terkait dengan cakupan yang didefinisikan secara eksplisit dalam bahasa, seperti blok atau fungsi.


0

Pertanyaan kuno, tapi ini pendapat saya.

Lingkup leksikal (statis) mengacu pada cakupan variabel dalam kode sumber .

Dalam bahasa seperti JavaScript, di mana fungsi dapat diedarkan dan dilampirkan dan disambungkan kembali ke objek lain-lain, Anda mungkin memiliki cakupan yang tergantung pada siapa yang memanggil fungsi pada saat itu, tetapi tidak. Mengubah ruang lingkup seperti itu akan menjadi ruang lingkup dinamis, dan JavaScript tidak melakukan itu, kecuali mungkin denganthis referensi objek.

Untuk mengilustrasikan poin:

var a='apple';

function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}

var test=doit();
test();

Dalam contoh, variabel adidefinisikan secara global, tetapi dibayangi dalam doit()fungsi. Fungsi ini mengembalikan fungsi lain yang, seperti yang Anda lihat, bergantung padaa variabel di luar cakupannya sendiri.

Jika Anda menjalankan ini, Anda akan menemukan bahwa nilai yang digunakan adalah aardvark, bukan appleyang, meskipun itu berada dalam ruang lingkuptest() fungsi, bukan dalam ruang lingkup leksikal dari fungsi asli. Yaitu, ruang lingkup yang digunakan adalah ruang lingkup seperti yang muncul dalam kode sumber, bukan ruang lingkup di mana fungsi sebenarnya digunakan.

Fakta ini dapat memiliki konsekuensi yang mengganggu. Misalnya, Anda mungkin memutuskan bahwa lebih mudah untuk mengatur fungsi Anda secara terpisah, dan kemudian menggunakannya ketika saatnya tiba, seperti dalam pengendali acara:

var a='apple',b='banana';

function init() {
  var a='aardvark',b='bandicoot';
  document.querySelector('button#a').onclick=function(event) {
    alert(a);
  }
  document.querySelector('button#b').onclick=doB;
}

function doB(event) {
  alert(b);
}

init();
<button id="a">A</button>
<button id="b">B</button>

Contoh kode ini masing-masing. Anda dapat melihat bahwa karena pelingkupan leksikal, tombol Amenggunakan variabel bagian dalam, sedangkan tombolB tidak. Anda mungkin berakhir dengan fungsi bersarang lebih dari yang Anda inginkan.

Ngomong-ngomong, dalam kedua contoh, Anda juga akan melihat bahwa variabel-variabel yang dicakup secara leksikal dalam tetap ada meskipun fungsi fungsi yang berisi telah berjalan dengan sendirinya. Ini disebut closure , dan mengacu pada akses fungsi bersarang ke variabel luar, bahkan jika fungsi luar telah selesai. JavaScript harus cukup pintar untuk menentukan apakah variabel-variabel itu tidak lagi diperlukan, dan jika tidak, dapatkah sampah mengumpulkannya.


-1

Saya biasanya belajar dengan contoh, dan ini ada sesuatu:

const lives = 0;

function catCircus () {
    this.lives = 1;
    const lives = 2;

    const cat1 = {
        lives: 5,
        jumps: () => {
            console.log(this.lives);
        }
    };
    cat1.jumps(); // 1
    console.log(cat1); // { lives: 5, jumps: [Function: jumps] }

    const cat2 = {
        lives: 5,
        jumps: () => {
            console.log(lives);
        }
    };
    cat2.jumps(); // 2
    console.log(cat2); // { lives: 5, jumps: [Function: jumps] }

    const cat3 = {
        lives: 5,
        jumps: () => {
            const lives = 3;
            console.log(lives);
        }
    };
    cat3.jumps(); // 3
    console.log(cat3); // { lives: 5, jumps: [Function: jumps] }

    const cat4 = {
        lives: 5,
        jumps: function () {
            console.log(lives);
        }
    };
    cat4.jumps(); // 2
    console.log(cat4); // { lives: 5, jumps: [Function: jumps] }

    const cat5 = {
        lives: 5,
        jumps: function () {
            var lives = 4;
            console.log(lives);
        }
    };
    cat5.jumps(); // 4
    console.log(cat5); // { lives: 5, jumps: [Function: jumps] }

    const cat6 = {
        lives: 5,
        jumps: function () {
            console.log(this.lives);
        }
    };
    cat6.jumps(); // 5
    console.log(cat6); // { lives: 5, jumps: [Function: jumps] }

    const cat7 = {
        lives: 5,
        jumps: function thrownOutOfWindow () {
            console.log(this.lives);
        }
    };
    cat7.jumps(); // 5
    console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}

catCircus();

-1

Topik ini sangat terkait dengan bindfungsi bawaan dan diperkenalkan di ECMAScript 6 Arrow Functions . Itu benar-benar menjengkelkan, karena untuk setiap metode "kelas" (fungsi sebenarnya) baru yang ingin kami gunakan, kami harus melakukan bindini untuk memiliki akses ke ruang lingkup.

JavaScript secara default tidak mengatur ruang lingkupnya thispada fungsi (tidak mengatur konteks pada this). Secara default, Anda harus secara eksplisit mengatakan konteks mana yang ingin Anda miliki.

Fungsi panah secara otomatis mendapatkan ruang lingkup leksikal yang disebut (memiliki akses ke definisi variabel dalam blok yang mengandungnya). Ketika menggunakan fungsi panah, ia secara otomatis mengikat thiske tempat di mana fungsi panah didefinisikan di tempat pertama, dan konteks fungsi panah ini adalah blok yang berisi.

Lihat cara kerjanya dalam contoh sederhana di bawah ini.

Sebelum Fungsi Panah (tidak ada ruang lingkup leksikal secara default):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined

const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"

Dengan fungsi panah (lingkup leksikal secara default):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const arrowFunction = () => {
    console.log(programming.getLanguage());
}

arrowFunction(); // Output: "JavaScript"
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.