Sözcüksel kapsam nedir?


682

Sözcüksel kapsam belirlemeye kısa bir giriş nedir?


89
Podcast 58'de Joel, başka yerlerde cevaplanmış olsalar bile, SO'nun cevaplar için bir yer olmasını istediğinden, bunları beğenen soruları teşvik ediyor. Bu biraz daha kibar olsa da geçerli bir soru.
Ralph M.Rickenbach

5
@ rahul Anlıyorum eski bir soru. Ama eminim 2009'da bile SO, soranların bunu çözmek için bazı temel çaba sarf etmelerini bekledi . Durduğu için hiç çaba göstermiyor. Belki de bu yüzden birçok kişi tarafından reddedildi?
PP

13
Bu soruyu yazarken
Martin

27
Soru kibar, sadece ne istediğini söylüyor. Cevaplamakta özgürsünüz. Burada abartılı nezakete gerek yok.
Markus Siebeneicher

25
Bence bu tür sorular harika çünkü SO için içerik oluşturuyor. Sorunun çaba sarf etmediğini önemseyen IMO ... cevapların harika içeriği olacak ve bu mesaj panosunda önemli olan bu.
Jwan622

Yanıtlar:


686

Onları örneklerle anlıyorum. :)

İlk olarak, C benzeri sözdiziminde sözcüksel kapsam ( statik kapsam olarak da adlandırılır ):

void fun()
{
    int x = 5;

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

Her iç seviye dış seviyelerine erişebilir.

Yine Lisp'in ilk uygulaması tarafından kullanılan ve C benzeri bir sözdiziminde kullanılan dinamik kapsam olarak adlandırılan başka bir yol daha vardır:

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

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

İşte funerişim Ya xiçinde dummy1ya dummy2veya herhangi xçağrı herhangi işlevinde funile xonun içinde ilan etti.

dummy1();

5 yazdıracak,

dummy2();

yazdıracaktır 10.

Birincisi statik olarak derlenir çünkü derleme zamanında çıkarılabilir, ikincisi dinamik olarak adlandırılır, çünkü dış kapsam dinamiktir ve fonksiyonların zincir çağrısına bağlıdır.

Statik kapsam belirlemeyi göz için daha kolay buluyorum. Çoğu dil sonunda bu şekilde gitti, hatta Lisp (her ikisini de yapabilir, değil mi?). Dinamik kapsam belirleme, tüm değişkenlerin referanslarını çağrılan işleve geçirmeye benzer.

Derleyicinin bir işlevin dış dinamik kapsamını neden bulamadığını gösteren bir örnek olarak son örneğimizi göz önünde bulundurun. Böyle bir şey yazarsak:

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

Çağrı zinciri bir çalışma süresi durumuna bağlıdır. Doğruysa, çağrı zinciri şöyle görünür:

dummy1 --> fun()

Koşul yanlışsa:

dummy2 --> fun()

Her funiki durumda da dış alan arayan ile arayanın arayanıdır .

Sadece C dilinin iç içe fonksiyonlara veya dinamik kapsam belirlemeye izin vermediğinden bahsetmek gerekir.


19
Ayrıca yeni bulduğum eğitimi çok kolay bir şekilde belirtmek istiyorum. Arak'ın örneği güzel, ancak daha fazla örneğe ihtiyacı olan biri için çok kısa olabilir (aslında, diğer dillerle karşılaştırıldığında). Bir göz at. Anlamak önemlidir bu o anahtar kelime sözcük kapsamını anlamak için bize yol gösterecek gibi. howtonode.org/what-is-this
CppLearner

9
Bu iyi bir cevap. Ancak soru ile etiketlenir JavaScript. Bu yüzden bence bu kabul edilen cevap olarak işaretlenmemelidir. Özellikle JS'deki sözcüksel kapsam farklıdır
Boyang

6
Son derece iyi bir cevap. Teşekkür ederim. @Boyang katılmıyorum. Ben Lisp kodlayıcı değilim, ancak Lisp örneğini yararlı buldum, çünkü bu JS'de alamadığınız dinamik kapsam belirleme örneğidir.
dudewad

4
Başlangıçta örnek geçerli C kodu olduğunu düşündüm ve C dinamik kapsamlandırma olup olmadığını karıştı Belki de sonunda feragat kod örneğinden önce kaydırılabilir?
Yangshun Tay

2
Bu hala çok yararlı bir cevap ama bence @Boyang doğru. Bu cevap, daha çok C'nin sahip olduğu blok kapsam çizgileri boyunca olan 'seviye' anlamına gelir. Varsayılan olarak JavaScript'in blok düzeyi kapsamı yoktur, bu nedenle bir fordöngü içinde tipik bir sorundur. JavaScript için sözcüksel kapsam, ES6 letveya constkullanılmadığı sürece yalnızca işlev düzeyindedir .
icc97

275

Mümkün olan en kısa tanımı deneyelim:

Sözcüksel Kapsam Belirleme , değişken adlarının iç içe işlevlerde nasıl çözümlendiğini tanımlar: iç işlevler, üst işlev döndürülse bile üst işlevlerin kapsamını içerir .

Hepsi bu kadar!


21
Son bölüm: "üst işlev dönmüş olsa bile" Kapatma olarak adlandırılır.
Juanma Menendez

1
Lexical Scoping & kapanışını sadece tek bir sentasyonda anladı. Teşekkürler!!
Zindan

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

whatismyscope()()

Yukarıdaki kod "Ben sadece yerel" dönecektir. "Ben bir küreselim" döndürmez. Çünkü func () işlevi, başlangıçta whatismyscope işlevinin kapsamı dahilinde tanımlanmış olan yeri sayar.

Her ne denirse denir (küresel kapsam / başka bir işlev içinde bile), bu yüzden küresel olduğum küresel kapsam değeri yazdırılmayacaktır.

Bu, JavaScript Tanımlama Kılavuzu'na göre " işlevler tanımlandıklarında geçerli olan kapsam zinciri kullanılarak yürütülür " şeklinde sözcüksel kapsam belirleme olarak adlandırılır .

Sözcüksel kapsam çok güçlü bir kavramdır.

Bu yardımcı olur umarım..:)


3
çok güzel bir açıklama i fonksiyon func () {return this.scope;} yazarsanız bir şey daha eklemek istiyorum o zaman "ben küresel" dönecektir sadece bu anahtar kelimeyi kullanın ve kapsamınız değişecek
Rajesh Kumar Bhawsar

41

Sözcüksel (AKA statik) kapsam belirlemesi, bir değişkenin kapsamının yalnızca kodun metinsel yapısı içindeki konumuna dayanarak belirlenmesini ifade eder. Bir değişken her zaman en üst düzey ortamını ifade eder. Dinamik kapsamla ilgili olarak anlamak iyidir .


41

Kapsam, işlevlerin, değişkenlerin ve benzerlerinin kullanılabilir olduğu alanı tanımlar. Örneğin, bir değişkenin kullanılabilirliği bağlamı içinde tanımlanır, diyelim ki bunlar içinde tanımlanmış olan işlev, dosya veya nesne. Genellikle bu yerel değişkenleri çağırırız.

Sözcüksel kısım, kapsamı kaynak kodunu okuyarak türetebileceğiniz anlamına gelir.

Sözcüksel kapsam, statik kapsam olarak da bilinir.

Dinamik kapsam, tanımlandıktan sonra herhangi bir yerden çağrılabilen veya bunlardan referans alınabilecek global değişkenleri tanımlar. Çoğu programmin dilinde küresel değişkenler sözcüksel kapsamda olsalar da, bazen küresel değişkenler olarak adlandırılırlar. Bu, değişkenin bu bağlamda mevcut olduğu kodun okunmasından türetilebilir. Belki de bir kullanımı veya tanımlamayı takip etmek veya tanımlamayı bulmak zorundadır, ancak kod / derleyici buradaki değişkeni bilir.

Buna karşılık, dinamik kapsam belirleme işleminde önce yerel işlevde arama yaparsınız, daha sonra yerel işlev olarak adlandırılan işlevde arama yaparsınız, daha sonra bu işlevi çağıran işlevde arama yaparsınız. "Dinamik", belirli bir işlev her çağrıldığında çağrı yığınının farklı olabilmesi anlamına gelir ve bu nedenle işlev, çağrıldığı yere bağlı olarak farklı değişkenlere vurabilir. ( buraya bakın )

Dinamik kapsam için ilginç bir örnek görmek için buraya bakın .

Daha fazla ayrıntı için buraya ve buraya bakın .

Delphi / Object Pascal'daki bazı örnekler

Delphi'nin sözcüksel kapsamı vardır.

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'nin dinamik kapsama en yakın noktası RegisterClass () / GetClass () fonksiyon çiftidir. Kullanımı için buraya bakınız .

Diyelim ki RegisterClass ([TmyClass]) belirli bir sınıfı kaydetmek için çağrıldığında kod okunarak tahmin edilemez (kullanıcı tarafından çağrılan bir düğme tıklama yönteminde çağrılır), GetClass ('TmyClass') kod çağrısı bir sonuç olsun veya olmasın. RegisterClass () çağrısının GetClass () kullanarak birimin sözcüksel kapsamı içinde olması gerekmez;

Dinamik kapsam için başka bir olasılık , çağrı işlevlerinin değişkenlerini bildikleri için Delphi 2009'daki anonim yöntemlerdir (kapanmalar). Oradan çağıran yolu izlemez ve bu nedenle tam olarak dinamik değildir.


2
Aslında özel, sınıfın tanımlandığı tüm birimde erişilebilir. D2006'da "Katı özel" ifadesinin kullanılmasının nedeni budur.
Marco van de Voort

2
(Çok açıklama olmadan hem komplike dil ve örnekler aksine) sade bir dille için 1
Pops

36

@Arak gibi milletlerin tam özellikli, dil bilincine sahip olmayan cevaplarını seviyorum. Bu soru JavaScript olarak etiketlendiğinden , bu dile çok özel bazı notlar almak istiyorum.

JavaScript'te kapsam belirleme seçeneklerimiz:

  • olduğu gibi (kapsam ayarlaması yok)
  • sözlük var _this = this; function callback(){ console.log(_this); }
  • ciltli callback.bind(this)

Bence JavaScript'in gerçekten dinamik bir kapsamı yok . anahtar kelimeyi .bindayarlar thisve bu yakındır, ancak teknik olarak aynı değildir.

İşte her iki yaklaşımı da gösteren bir örnek. Geri aramaların kapsamını belirleme konusunda her karar verdiğinizde bunu yaparsınız, böylece bu sözler, olay işleyicileri ve daha fazlası için geçerlidir.

sözlük

Lexical ScopingJavaScript'teki geri arama terimleri şöyledir :

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
  }
  //...
};

Ciltli

Kapsamın başka bir yolu da kullanmaktır Function.prototype.bind:

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

Bu yöntemler bildiğim kadarıyla davranışsal olarak eşdeğerdir.


Kullanmak bindkapsamı etkilemez.
Ben Aston

12

Sözcüksel kapsam belirleme: Bir işlev dışında bildirilen değişkenler genel değişkenlerdir ve bir JavaScript programında her yerde görülebilir. Bir işlevin içinde bildirilen değişkenlerin işlev kapsamı vardır ve yalnızca bu işlevin içinde görünen kod tarafından görülebilir.


12

IBM bunu şöyle tanımlar:

Bir programın veya segment biriminin bildirimin geçerli olduğu bölümü. Bir rutinde bildirilen bir tanımlayıcı, bu rutin içinde ve tüm iç içe rutinler içinde bilinir. Yuvalanmış bir yordam aynı ada sahip bir öğe bildirirse, dış öğe yuvalanmış yordamda kullanılamaz.

Örnek 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();

Örnek 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

Sözcüksel kapsam , iç içe geçmiş bir işlev grubunda iç işlevlerin, üst kapsamlarının değişkenlerine ve diğer kaynaklarına erişimi olduğu anlamına gelir . Bu, çocuk işlevlerinin sözcüksel olarak ebeveynlerinin yürütme bağlamına bağlı olduğu anlamına gelir. Sözcüksel kapsam bazen statik kapsam olarak da adlandırılır .

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

Sözcüksel kapsam hakkında fark edeceğiniz şey, ileriye doğru çalışmasıdır, yani isme çocuklarının yürütme bağlamlarından erişilebilir. Ancak ebeveynlerine geriye doğru çalışmaz, yani değişkene likesebeveynleri tarafından erişilemez.

Bu aynı zamanda farklı yürütme bağlamlarında aynı ada sahip değişkenlerin yürütme yığınının yukarıdan aşağısına öncelik kazandığını bildirir. En içteki işlevde (yürütme yığınının en üst bağlamı) başka bir değişkene benzer bir ada sahip bir değişken daha yüksek önceliğe sahip olacaktır.

Bunun buradan alındığını unutmayın .


8

Basit bir dilde, sözcüksel kapsam, kapsamınızın dışında tanımlanan bir değişkendir ya da kapsamınızın içinde üst kapsam otomatik olarak kullanılabilir, yani oradan geçmeniz gerekmez.

Misal:

let str="JavaScript";

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

myFun();

// Çıktı: JavaScript


2
Bir örnekle benim için en kısa ve en iyi cevap. ES6 'ok işlevlerinin sorunu çözdüğü eklenmiş olabilir bind. Onlarla bindartık gerekli değil. Bu değişiklik hakkında daha fazla bilgi için stackoverflow.com/a/34361380/11127383
Daniel Danielecki'yi

4

Çevredeki konuşmanın önemli bir parçası vardır sözcük ve dinamik kapsam belirleme düz bir açıklama: eksik ömür - ya kapsamına sahip değişkenin zaman değişken erişilebilir.

Dinamik kapsam belirleme, geleneksel olarak düşündüğümüz şekilde "küresel" kapsam belirlemeye tekabül eder (ikisi arasındaki karşılaştırmayı ortaya koymamın nedeni, daha önce bahsedilmiş olmasıdır - ve özellikle bağlantılı olanları sevmem makalenin açıklamasını ); muhtemelen küresel ve dinamik arasında bir karşılaştırma yapmamamız en iyisidir - sözde bağlantılı makaleye göre "... [küresel] küresel kapsamdaki değişkenlerin yerine kullanılabilir."

Öyleyse, açık İngilizce'de, iki kapsam belirleme mekanizması arasındaki önemli fark nedir?

Sözcüksel kapsam belirleme, yukarıdaki yanıtlar boyunca çok iyi tanımlanmıştır: sözcüksel kapsamlandırılmış değişkenler, tanımlandığı işlevin yerel düzeyinde kullanılabilir veya erişilebilir.

Ancak - OP'nin odağı olmadığı için - dinamik kapsam çok fazla ilgi görmedi ve aldığı dikkat, muhtemelen biraz daha fazlasına ihtiyaç duyduğu anlamına geliyor (bu, diğer cevapların eleştirisi değil, daha çok "oh, bu cevap biraz daha olmasını diliyoruz "). İşte biraz daha fazla:

Dinamik kapsam belirleme, bir değişkenin fonksiyon çağrısının ömrü boyunca veya fonksiyon yürütülürken daha büyük program tarafından erişilebilir olduğu anlamına gelir. Gerçekten, Wikipedia aslında ikisi arasındaki farkın açıklanması ile iyi bir iş çıkarıyor . Gizlememek için dinamik kapsamı tanımlayan metin şöyledir:

... [I] n dinamik kapsam belirleme (veya dinamik kapsam), değişken adının kapsamı belirli bir işlevse, kapsamı işlevin yürütüldüğü süredir: işlev çalışırken değişken adı vardır , ve değişkenine bağlıdır, ancak işlev döndükten sonra değişken adı yoktur.


3

Sözcüksel kapsam, bir işlevin değişkenleri hemen tanımlandığı kapsamda değil, tanımlandığı bağlamda aradığı anlamına gelir.

Daha fazla ayrıntı istiyorsanız Lisp'de sözcüksel kapsamın nasıl çalıştığına bakın . Common Lisp'de Dinamik ve Sözcüksel Değişkenlerde Kyle Cronin'in Seçtiği Cevap buradaki yanıtlardan çok daha açıktır.

Tesadüfen bunu sadece Lisp sınıfında öğrendim ve JavaScript'te de geçerli.

Bu kodu Chrome'un konsolunda çalıştırdım.

// 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)
})();                     //)

Çıktı:

5
10
5

3

JavaScript'teki sözcüksel kapsam, bir işlev dışında tanımlanan bir değişkenin, değişken bildiriminden sonra tanımlanan başka bir işlev içinde erişilebilir olabileceği anlamına gelir. Ancak bunun tersi doğru değildir; bir işlevin içinde tanımlanan değişkenlere bu işlevin dışında erişilemez.

Bu kavram, JavaScript'teki kapaklarda yoğun olarak kullanılmaktadır.

Diyelim ki aşağıdaki kodumuz var.

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

Şimdi, add () -> öğesini çağırdığınızda bu yazdırılacaktır 3.

Bu nedenle, add () işlevi, xyöntem işlevi eklenmeden önce tanımlanan genel değişkene erişiyor . Bu, JavaScript'teki sözcüksel kapsam belirleme nedeniyle çağrılır.


Kod snippet'inin dinamik olarak kapsamlı bir dil için olduğunu düşünün. Eğer add()verilen kod parçacığını aşağıdaki derhal denilen işlev, aynı zamanda 3. Sözcük kapsam belirleme basacaktır basitçe bir işlev yerel bağlam dışında genel değişkenleri erişebilir anlamına gelmez. Bu nedenle örnek kod, sözcüksel kapsam belirlemenin ne anlama geldiğini göstermeye gerçekten yardımcı olmaz. Sözcüksel kapsam belirleme işleminin gerçekten bir karşı örnek veya en azından kodun diğer olası yorumlarının açıklanması gerekir.
C Perkins

2

Sözcüksel kapsam, yürütme yığınındaki geçerli konumdan görülebilen tanımlayıcıların sözlüğünü (örn. Değişkenler, işlevler, vb.) İfade eder.

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

foove barher zaman kullanılabilir tanımlayıcıların sözlüğü içindedir, çünkü bunlar küreseldir.

Ne zaman function1yürütülür, bu bir sözlüğü erişimi olan foo2, bar2, foove bar.

Ne zaman function2yürütülür, bu bir sözlüğü erişebilir foo3, bar3, foo2, bar2, foo, ve bar.

Global ve / veya dış fonksiyonların bir iç fonksiyon tanımlayıcısına erişememesinin nedeni, o fonksiyonun yürütülmesinin henüz gerçekleşmemesi ve bu nedenle hiçbir tanımlayıcısının belleğe tahsis edilmemiş olmasıdır. Dahası, bu iç bağlam yürütüldüğünde, yürütme yığınından kaldırılır, yani tüm tanımlayıcıları çöp toplanır ve artık kullanılamaz.

Son olarak, bu nedenle iç içe bir yürütme bağlamı HER ZAMAN atalarının yürütme bağlamına erişebilir ve bu nedenle neden daha büyük bir tanımlayıcı sözlüğüne erişebilir.

Görmek:

Yukarıdaki tanımı basitleştirmeye yardımcı olması için @ robr3rd'a özel teşekkürler .


1

Bu soruya geri adım atarak ve daha geniş yorum çerçevesinde (bir program yürütürken) kapsam belirleme rolüne bakarak elde edebileceğimiz farklı bir açı var. Başka bir deyişle, bir dil için bir tercüman (veya derleyici) oluşturduğunuzu ve çıktıyı hesaplamaktan, bir program ve ona bazı girdiler verildiğinden sorumlu olduğunuzu düşünün.

Yorum üç şeyi takip etmeyi içerir:

  1. Durum - yani, yığın ve yığındaki değişkenler ve başvurulan bellek konumları.

  2. Bu durumdaki işlemler - yani programınızdaki her kod satırı

  3. Çevre Belirli olan işlem çalışır - yani, projeksiyon durumuna bir operasyonda.

Bir tercüman bir programın ilk kod satırından başlar, çevresini hesaplar, o ortamdaki satırı çalıştırır ve programın durumu üzerindeki etkisini yakalar. Daha sonra bir sonraki kod satırını yürütmek için programın kontrol akışını izler ve program sona erene kadar işlemi tekrarlar.

Herhangi bir işlem için ortamı hesaplama şekliniz, programlama dili tarafından tanımlanan resmi bir kurallar kümesidir. "Bağlama" terimi, programın genel durumunun ortamdaki bir değere eşlenmesini tanımlamak için sıklıkla kullanılır. "Genel durum" ile küresel devlet anlamına gelmediğini, bunun yerine yürütmenin herhangi bir noktasında ulaşılabilir her tanımın toplamı anlamına geldiğimizi unutmayın).

Kapsam belirleme sorununun tanımlandığı çerçevedir. Şimdi seçeneklerimizin bir sonraki bölümüne.

  • Tercümenin uygulayıcısı olarak, ortamı programın durumuna olabildiğince yakınlaştırarak görevinizi basitleştirebilirsiniz. Buna göre, bir kod satırının ortamı, önceki satırın bir atama mı, bir fonksiyon çağrısı mı, bir fonksiyondan geri dönüp dönmediğine bakılmaksızın, kendisine uygulanan bu işlemin etkisiyle önceki kod satırının ortamı tarafından tanımlanır. veya while döngüsü gibi bir kontrol yapısı.

Bu, dinamik kodlamanın özüdür , burada herhangi bir kodun içinde çalıştığı ortam, yürütme bağlamında tanımlandığı gibi programın durumuna bağlıdır.

  • Veya , kendi dilini kullanarak bir programcı düşünmek ve değişken alabilir değerlerinin takibi için onun işini kolaylaştıracak olabilir. Geçmişteki yürütmenin bütünlüğünün sonucuyla ilgili akıl yürütmede çok fazla yol ve çok fazla karmaşıklık vardır. Sözcüksel Kapsam Belirleme , geçerli ortamı geçerli blok, işlev veya diğer kapsam biriminde tanımlanmış olan kısmı ve üst öğesi (yani geçerli saati çevreleyen blok veya mevcut işlevi çağıran işlev) ile kısıtlayarak bunu yapmanıza yardımcı olur .

Diğer bir deyişle, sözcüksel kapsamla , herhangi bir kodun gördüğü ortam, bir blok veya işlev gibi, dilde açıkça tanımlanan bir kapsamla ilişkili duruma bağlanır.


0

Eski soru, ama işte benim üstlenmem.

Sözcüksel (statik) kapsam, kaynak koddaki bir değişkenin kapsamını ifade eder .

JavaScript gibi bir dilde, işlevlerin aktarılabileceği ve çeşitli nesnelere eklenebileceği ve yeniden eklenebileceği bir dilde, bu kapsamın o anda işlevi kimin çağıracağına bağlı olmasına rağmen olabilir. Kapsamı bu şekilde değiştirmek dinamik kapsam olacaktır ve JavaScript muhtemelen thisnesne başvurusu dışında bunu yapmaz .

Konuyu göstermek için:

var a='apple';

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

var test=doit();
test();

Örnekte, değişken aglobal olarak tanımlanır, ancak doit()işlevde gölgelenir . Bu işlev, gördüğünüz gibi,a kendi kapsamının dışındaki değişkene .

Eğer bu çalıştırırsanız, kullanılan değer olduğunu göreceksiniz aardvark, değil applebunun kapsamına olsa hangi test()işlevi, orijinal fonksiyonun sözcüksel kapsamında değildir. Yani, kullanılan kapsam, işlevin gerçekte kullanıldığı kapsam değil, kaynak kodunda göründüğü kapsamdır.

Bu gerçeğin can sıkıcı sonuçları olabilir. Örneğin, işlevlerinizi ayrı olarak düzenlemenin daha kolay olduğuna karar verebilir ve daha sonra olay geldiğinde, örneğin bir olay işleyicisinde olduğu gibi bunları kullanabilirsiniz:

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>

Bu kod örneği her birini yapar. Sözcüksel kapsam nedeniyle, düğmenin Aiç değişkeni kullandığını,B kullanmadığını görebilirsiniz. Yuvalama işlevlerini istediğinizden daha fazla sonuçlandırabilirsiniz.

Bu arada, her iki örnekte de, içeren işlev işlevi kendi yolunu çalıştırmış olsa bile, içsel olarak kapsamlandırılmış değişkenlerin devam ettiğini göreceksiniz. Buna kapatma denir ve dış işlev bitmiş olsa bile iç içe bir işlevin dış değişkenlere erişimini ifade eder. JavaScript'in bu değişkenlerin artık gerekli olup olmadığını belirleyecek kadar akıllı olması gerekir ve eğer değilse, bunları toplayabilir.


-1

Normalde örnek olarak öğreniyorum ve işte küçük bir şey:

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

Bu konu, yerleşik bindişlevle yakından ilgilidir ve ECMAScript 6 Arrow İşlevleri'nde tanıtılmaktadır . Gerçekten sinir bozucuydu, çünkü kullanmak istediğimiz her yeni "sınıf" (aslında fonksiyon) yöntemi bindiçin, kapsama erişebilmek için buna ihtiyacımız vardı.

JavaScript varsayılan olarak thison işlevlerinin kapsamını ayarlamaz ( içeriği açık değildir this). Varsayılan olarak, hangi içeriğe sahip olmak istediğinizi açıkça belirtmeniz gerekir.

Ok fonksiyonları otomatik olarak adlandırılan alır sözcük kapsamı (taşıyıcı çerçevesine değişkenin tanımı erişimi vardır). Ok işlevlerini kullanırken , otomatik olarak thisok işlevinin tanımlandığı yere bağlanır ve bu ok işlevlerinin içeriği taşıyıcı blok olup.

Aşağıdaki en basit örneklerde pratikte nasıl çalıştığını görün.

Ok İşlevlerinden Önce (varsayılan olarak sözcük kapsamı yoktur):

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"

Ok işlevleriyle (varsayılan olarak sözcük kapsamı):

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

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

arrowFunction(); // Output: "JavaScript"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.