JavaScript'teki değişkenlerin kapsamı nedir?
JavaScript'teki değişkenlerin kapsamı nedir? Bir işlevin dışındaki ile aynı kapsama sahipler mi? Ya da önemli mi? Ayrıca, küresel olarak tanımlanmışlarsa değişkenler nerede saklanır?
Yanıtlar
TLDR
JavaScript'in sözcüksel (statik olarak da adlandırılır) kapsam ve kapanışları vardır. Bu, kaynak koda bakarak bir tanımlayıcının kapsamını söyleyebileceğiniz anlamına gelir.
Dört kapsam şunlardır:
- Global - her şey tarafından görülebilir
- İşlev - bir işlev içinde görünür (ve alt işlevleri ve blokları)
- Blok - bir blok (ve alt blokları) içinde görünür
- Modül - bir modül içinde görünür
Global ve modül kapsamının özel durumlarının dışında, değişkenler var
(fonksiyon kapsamı), let
(blok kapsamı) ve const
(blok kapsamı) kullanılarak bildirilir. Diğer tanımlayıcı bildirim biçimlerinin çoğu katı modda blok kapsamına sahiptir.
Genel Bakış
Kapsam, kod tabanının bir tanımlayıcının geçerli olduğu bölgesidir.
Sözcüksel ortam, tanımlayıcı adları ve bunlarla ilişkili değerler arasındaki bir eşlemedir.
Kapsam, sözlü ortamların bağlantılı bir yuvalanmasından oluşur ve yuvadaki her seviye, bir ata yürütme bağlamının sözcüksel ortamına karşılık gelir.
Bu bağlantılı sözcük ortamları bir kapsam "zinciri" oluşturur. Tanımlayıcı çözümlemesi, bu zincir boyunca eşleşen bir tanımlayıcı arama sürecidir.
Tanımlayıcı çözümlemesi yalnızca tek bir yönde gerçekleşir: dışa doğru. Bu şekilde, dış sözcük ortamları, iç sözcük ortamlarını "göremez".
JavaScript'te bir tanımlayıcının kapsamına karar verirken ilgili üç faktör vardır :
- Bir tanımlayıcı nasıl beyan edildi
- Bir tanımlayıcının beyan edildiği yer
- İçinde olsun katı mod veya olmayan katı modda
Tanımlayıcıların bildirilebileceği yollardan bazıları:
var
,let
veconst
- Fonksiyon parametreleri
- Yakalama bloğu parametresi
- İşlev bildirimleri
- Adlandırılmış işlev ifadeleri
- Global nesnede örtük olarak tanımlanmış özellikler (yani
var
katı olmayan modda eksiklik ) import
ifadelereval
Bazı konum tanımlayıcıları beyan edilebilir:
- Küresel bağlam
- İşlev gövdesi
- Sıradan blok
- Bir kontrol yapısının üst kısmı (ör. Loop, if, while, vb.)
- Kontrol yapısı gövdesi
- Modüller
Bildirim Stilleri
var
Kullanılarak bildirilen tanımlayıcılar , doğrudan genel bağlamda bildirilmeleri dışında var
işlev kapsamına sahiptir ; bu durumda bunlar, genel nesneye özellikler olarak eklenir ve genel kapsama sahiptir. eval
İşlevlerde kullanımları için ayrı kurallar vardır .
izin ver ve sabitle
Tanımlayıcıları kullanılarak bildirilmiş let
ve const
blok kapsamına sahip onlar küresel kapsama sahip bu durumda küresel bağlamda, doğrudan beyan edildiği haricinde.
Not: let
, const
ve var
tüm askıya alır . Bu, mantıksal tanım konumlarının, kapsayıcı kapsamlarının (blok veya işlev) en üstünde olduğu anlamına gelir. Ancak, değişkenler kullanılarak beyan let
ve const
okumak veya kontrol kaynak kodunda beyanının noktasını geçene kadar tahsis edilemez. Ara dönem, zamansal ölü bölge olarak bilinir.
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
İşlev parametresi adları
İşlev parametresi adları, işlev gövdesine göre belirlenir. Bunun biraz karmaşık olduğuna dikkat edin. Varsayılan bağımsız değişkenler olarak bildirilen işlevler, işlevin gövdesine değil, parametre listesine kapanır .
İşlev bildirimleri
İşlev bildirimleri katı modda blok kapsamına ve katı olmayan modda işlev kapsamına sahiptir. Not: Katı olmayan mod, farklı tarayıcıların ilginç tarihsel uygulamalarına dayanan karmaşık bir dizi yeni kuraldır.
Adlandırılmış işlev ifadeleri
Adlandırılmış işlev ifadelerinin kapsamı kendi içindedir (örneğin, özyineleme amacıyla).
Global nesnede örtük olarak tanımlanmış özellikler
Katı olmayan modda, genel nesne kapsam zincirinin en üstünde yer aldığından, genel nesne üzerindeki örtük olarak tanımlanmış özelliklerin genel kapsamı vardır. Katı modda bunlara izin verilmez.
değerlendirme
Gelen eval
dizeleri, değişkenler kullanılarak bildirilen var
eğer geçerli kapsamda yer veya edilecek eval
dolaylı olarak kullanılır küresel nesne üzerinde özellikler olarak,.
Örnekler
Aşağıdaki bir ReferenceError atmak isimlerin çünkü edecek x
, y
ve z
fonksiyon hiçbir anlamı dışında var f
.
function f() {
var x = 1
let y = 1
const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)
Aşağıdakiler y
ve için bir ReferenceError atacaktır z
, ancak için değil x
, çünkü x
öğesinin görünürlüğü blok tarafından kısıtlanmamaktadır. Kontrol yapılarının organları tanımlayan Bloklar gibi if
, for
ve while
, benzer şekilde hareket eder.
{
var x = 1
let y = 1
const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope
Aşağıda, işlev kapsamı x
olduğundan döngünün dışında görülebilir var
:
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
... bu davranış nedeniyle, var
döngülerde kullanılarak bildirilen değişkenleri kapatırken dikkatli olmanız gerekir . Burada x
bildirilen tek bir değişken örneği vardır ve mantıksal olarak döngünün dışında yer alır.
Aşağıdaki 5
, beş kez yazdırır ve ardından döngünün dışı 5
için altıncı kez yazdırır console.log
:
for(var x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop
Aşağıdaki baskılar undefined
nedeniyle x
blok-kapsamlı olduğunu. Geri aramalar tek tek eşzamansız olarak çalıştırılır. Yeni davranış let
her anonim işlev adında farklı bir değişkenin üzerinde kapalı olduğu değişkenler yardımıyla x
(o yapmış olabilirler ki aksine var
) ve tamsayılar böylece 0
aracılığıyla 4
yazdırılır .:
for(let x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined
Aşağıdakiler ReferenceError
, görünürlüğü x
blok tarafından kısıtlanmadığından bir A ATMAZ; ancak, undefined
değişken başlatılmadığı için (ifade nedeniyle if
) yazdırılacaktır .
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
Kullanılarak bir for
döngünün en üstünde bildirilen bir değişken let
, döngünün gövdesine göre ayarlanır:
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
Aşağıdakiler ReferenceError
, görünürlüğü x
blok tarafından kısıtlandığı için bir atacaktır :
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
Değişkenler kullanarak ilan var
, let
ya const
tüm modüllere kapsamına eklenir:
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
Aşağıdakiler, global nesnede bir özellik bildirecektir çünkü var
global bağlam içinde kullanılarak bildirilen değişkenler global nesneye özellikler olarak eklenir:
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
ve const
genel bağlamda genel nesneye özellikler ekleme, ancak yine de genel kapsama sahiptir:
let x = 1
console.log(window.hasOwnProperty('x')) // false
İşlev parametrelerinin işlev gövdesinde bildirildiği düşünülebilir:
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
Yakalama bloğu parametreleri, yakalama bloğu gövdesine göre ayarlanır:
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
Adlandırılmış işlev ifadeleri, yalnızca ifadenin kendisine göre belirlenir:
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
Katı olmayan modda, genel nesne üzerinde örtük olarak tanımlanmış özellikler genel kapsamdadır. Katı modda bir hata alırsınız.
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
Katı olmayan modda, işlev bildirimlerinin işlev kapsamı vardır. Katı modda, blok kapsamları vardır.
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
Kaputun altında nasıl çalışır
Kapsam, bir tanımlayıcının geçerli olduğu kodun sözlü bölgesi olarak tanımlanır.
JavaScript'te, her işlev nesnesinin içinde oluşturulduğu yürütme bağlamının (yığın çerçevesi) sözcüksel ortamına[[Environment]]
bir başvuru olan gizli bir referansı vardır .
Bir işlevi çağırdığınızda gizli [[Call]]
yöntem çağrılır. Bu yöntem yeni bir yürütme bağlamı yaratır ve yeni yürütme bağlamı ile işlev nesnesinin sözcüksel ortamı arasında bir bağlantı kurar. Bunu [[Environment]]
, işlev nesnesi üzerindeki değeri yeni yürütme bağlamının sözcüksel ortamındaki bir dış referans alanına kopyalayarak yapar .
Yeni yürütme bağlamı ile işlev nesnesinin sözcüksel ortamı arasındaki bu bağlantının bir kapatma olarak adlandırıldığına dikkat edin .
Bu nedenle, JavaScript'te kapsam, dış referanslarla bir "zincir" içinde birbirine bağlanmış sözcük ortamları aracılığıyla uygulanır. Bu sözcüksel ortamlar zincirine kapsam zinciri adı verilir ve tanımlayıcı çözümlemesi , zincirde eşleşen bir tanımlayıcı aranarak gerçekleşir .
Daha fazlasını öğrenin .
Javascript, belirli bir işlevin kapsamını oluşturmak için kapsam zincirlerini kullanır. Tipik olarak bir genel kapsam vardır ve tanımlanan her işlevin kendi iç içe kapsamı vardır. Başka bir işlev içinde tanımlanan herhangi bir işlev, dış işlevle bağlantılı yerel bir kapsama sahiptir. Kapsamı tanımlayan her zaman kaynaktaki konumdur.
Kapsam zincirindeki bir öğe, temelde üst kapsamına bir işaretçiye sahip bir Haritadır.
Bir değişkeni çözümlerken, javascript en iç kapsamda başlar ve dışarıya doğru arama yapar.
Global olarak beyan edilen değişkenlerin global bir kapsamı vardır. Bir işlev içinde bildirilen değişkenlerin kapsamı bu işlevin kapsamına alınır ve aynı ada sahip global değişkenlerin gölgelenir.
(Eminim gerçek JavaScript programcılarının diğer yanıtlarda gösterebilecekleri pek çok incelik vardır. Özellikle , herhangi bir zamanda tam olarak ne anlama geldiğiyle ilgili bu sayfaya rastladım this
. Umarım bu daha fazla giriş bağlantısı başlamanız için yeterlidir. .)
Eski okul JavaScript
Geleneksel olarak, JavaScript gerçekten yalnızca iki tür kapsama sahiptir:
- Global Kapsam : Değişkenler, uygulamanın başlangıcından itibaren uygulama boyunca bilinir (*)
- İşlevsel Kapsam : Değişkenler , işlevin başlangıcından (*) itibaren bildirildikleri işlev içinde bilinir.
Halihazırda farkı açıklayan birçok başka cevap olduğu için bu konuyu detaylandırmayacağım.
Modern JavaScript
En son JavaScript gözlük şimdi de üçüncü bir kapsam sağlar:
- Blok Kapsamı : Tanımlayıcılar, içinde bildirildikleri kapsamın tepesinden "bilinir" , ancak beyanlarının satırından sonrasına kadar atanamaz veya referans alınamaz (okunamaz). Bu ara döneme "geçici ölü bölge" denir.
Blok kapsamı değişkenlerini nasıl oluşturabilirim?
Geleneksel olarak, değişkenlerinizi şu şekilde oluşturursunuz:
var myVariable = "Some text";
Blok kapsamı değişkenleri şu şekilde oluşturulur:
let myVariable = "Some text";
Öyleyse işlevsel kapsam ile blok kapsamı arasındaki fark nedir?
İşlevsel kapsam ve blok kapsamı arasındaki farkı anlamak için aşağıdaki kodu göz önünde bulundurun:
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
Burada, değişkenimizin j
sadece ilk for döngüsünde bilindiğini, öncesinde ve sonrasında olmadığını görebiliriz. Yine de değişkenimiz i
tüm fonksiyonda bilinir.
Ayrıca, blok kapsamlı değişkenlerin kaldırılmadıkları için bildirilmeden önce bilinmediklerini göz önünde bulundurun. Aynı blok kapsamlı değişkeni aynı blok içinde yeniden açıklamanıza da izin verilmez. Bu, blok kapsamlı değişkenleri, genel veya işlevsel kapsamlı değişkenlerden daha az hataya yatkın hale getirir, bunlar kaldırılır ve birden çok bildirim durumunda herhangi bir hata üretmez.
Bugün blok kapsamı değişkenlerini kullanmak güvenli midir?
Bugün kullanmanın güvenli olup olmadığı ortamınıza bağlıdır:
Sunucu tarafı JavaScript kodu ( Node.js ) yazıyorsanız,
let
ifadeyi güvenle kullanabilirsiniz .İstemci tarafı JavaScript kodu yazıyorsanız ve tarayıcı tabanlı bir aktarıcı ( Traceur veya babel-bağımsız gibi ) kullanıyorsanız,
let
ifadeyi güvenle kullanabilirsiniz , ancak kodunuz muhtemelen performans açısından ideal olmaktan başka her şey olacaktır.İstemci tarafı JavaScript kodu yazıyorsanız ve Düğüm tabanlı bir aktarıcı kullanıyorsanız ( izleyici kabuk komut dosyası veya Babel gibi ),
let
ifadeyi güvenle kullanabilirsiniz . Tarayıcınız yalnızca aktarılan kod hakkında bilgi sahibi olacağından, performans sakıncaları sınırlandırılmalıdır.İstemci tarafı JavaScript kodu yazıyorsanız ve bir aktarıcı kullanmıyorsanız, tarayıcı desteğini dikkate almanız gerekir.
Bunlar hiç desteklemeyen bazı tarayıcılardır
let
:- Internet explorer 10 ve altı
- Firefox 43 ve altı
- Safari 9 ve altı
- Android tarayıcı 4 ve altı
- Opera 27 ve altı
- Chome 40 ve altı
- HERHANGİ BİR Opera Mini & Blackberry Browser sürümü
Tarayıcı desteği nasıl takip edilir
let
Bu cevabı okurken hangi tarayıcıların beyanı desteklediğine dair güncel bir genel bakış için bu Can I Usesayfaya bakın .
(*) Genel ve işlevsel kapsamlı değişkenler, JavaScript değişkenleri kaldırıldığı için bildirilmeden önce başlatılabilir ve kullanılabilir . Bu, bildirimlerin her zaman kapsamın en üstünde olduğu anlamına gelir.
İşte bir örnek:
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
Kapanışları ve bunları özel üye yapmak için nasıl kullanacağınızı araştırmak isteyeceksiniz .
Anahtar, anladığım kadarıyla, Javascript'in daha yaygın C blok kapsamına kıyasla işlev düzeyinde kapsam belirlemesine sahip olmasıdır.
İşte konuyla ilgili güzel bir makale.
"Javascript 1.7" 'de (Mozilla'nın Javascript uzantısı) blok kapsamı değişkenleri aşağıdaki letifadeyle de bildirilebilir :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
Başlangıçta Brendan Eich tarafından tasarlandığında JavaScript'te kapsam belirleme fikri , HyperCard komut dosyası dili HyperTalk'tan geldi .
Bu dilde, ekranlar bir dizi dizin kartına benzer şekilde yapıldı. Arka plan olarak adlandırılan bir ana kart vardı. Şeffaftı ve alt kart olarak görülebilir. Bu temel karttaki herhangi bir içerik, üstüne yerleştirilen kartlarla paylaşıldı. En üste yerleştirilen her kartın, önceki karttan öncelikli olan kendi içeriği vardı, ancak istenirse yine de önceki kartlara erişebiliyordu.
JavaScript kapsam belirleme sistemi tam olarak bu şekilde tasarlanmıştır. Sadece farklı isimleri var. JavaScript'teki kartlar Yürütme Bağlamları ECMA olarak bilinir . Bu bağlamların her biri üç ana bölümden oluşmaktadır. Değişken bir ortam, sözlü bir ortam ve bir bu bağlama. Kart referansına geri dönersek, sözcüksel ortam yığında daha düşük olan önceki kartların tüm içeriğini içerir. Geçerli bağlam, yığının en üstündedir ve orada bildirilen herhangi bir içerik, değişken ortamda depolanacaktır. Adlandırma çakışması durumunda değişken ortam öncelikli olacaktır.
Bu bağlama, içeren nesneyi işaret edecektir. Bazen kapsamlar veya yürütme bağlamları, kapsayıcı nesnenin window
veya bir yapıcı işlevin olabileceği bildirilmiş bir işlevde olduğu gibi, içeren nesne değişmeden değişir .
Bu yürütme bağlamları, kontrolün aktarıldığı her zaman oluşturulur. Kontrol, kod yürütülmeye başladığında aktarılır ve bu, öncelikle işlev yürütmeden yapılır.
Teknik açıklama budur. Pratikte, JavaScript'te bunu hatırlamak önemlidir.
- Kapsamlar teknik olarak "Yürütme Bağlamları" dır
- Bağlamlar, değişkenlerin depolandığı bir ortam yığını oluşturur
- Yığının üst kısmı önceliklidir (alt kısım genel bağlamdır)
- Her işlev bir yürütme bağlamı oluşturur (ancak her zaman bu bağlamda yeni değildir)
Bunu, bu sayfadaki önceki örneklerden birine (5. "Kapanış") uygulayarak, yürütme bağlamları yığınını izlemek mümkündür. Bu örnekte, yığında üç bağlam vardır. Dış bağlamla, var six ile çağrılan hemen çağrılan işlevdeki bağlam ve var six'in hemen çağrılan işlevinin içindeki döndürülen işlevdeki bağlamla tanımlanırlar.
i ) Dış bağlam. A = 1 değişken ortamına sahiptir
ii ) IIFE bağlamı, a = 1 sözlü ortamına sahiptir, ancak yığında öncelikli olan a = 6 değişken ortamına sahiptir
iii ) Döndürülen işlev bağlamı, sözlü bir a = 6 ortamı ve bu, çağrıldığında uyarıda başvurulan değerdir.
1) Global bir kapsam, bir fonksiyon kapsamı ve with ve catch kapsamları vardır. Değişkenler için genel olarak bir 'blok' seviyesi kapsamı yoktur - with ve catch deyimleri bloklarına isimler ekler.
2) Kapsamlar, işlevler tarafından genel kapsamın sonuna kadar yuvalanmıştır.
3) Özellikler prototip zincirinden geçilerek çözülür. With ifadesi, nesne özellik adlarını with bloğu tarafından tanımlanan sözcük kapsamına getirir.
DÜZENLEME: ECMAAScript 6 (Harmony), let'i desteklemek için tasarlanmıştır ve Chrome'un bir "uyum" bayrağına izin verdiğini biliyorum, bu nedenle belki de desteklemektedir ..
Blok düzeyinde kapsam belirleme için bir destek olalım, ancak bunun gerçekleşmesi için anahtar kelimeyi kullanmanız gerekir.
DÜZENLEME: Benjamin'in yorumlardaki with ve catch ifadelerine işaret etmesine dayanarak, gönderiyi düzenledim ve daha fazlasını ekledim. Hem ile ve yakalamak ifadeleri kendi bloklar halinde değişkenleri tanıtmak ve bu ise bir blok kapsamı. Bu değişkenler, kendilerine iletilen nesnelerin özelliklerine göre adlandırılır.
//chrome (v8)
var a = { 'test1':'test1val' }
test1 // error not defined
with (a) { var test1 = 'replaced' }
test1 // undefined
a // a.test1 = 'replaced'
DÜZENLEME: Açıklayıcı örnek:
test1'in kapsamı with bloğuna ayarlanır, ancak a.test1'e diğer ad verilir. 'Var test1', a'nın bir özelliği olmadığı sürece üst sözcük bağlamında (fonksiyon veya genel) yeni bir test1 değişkeni yaratır - ki bu.
Eyvah! 'With' kullanırken dikkatli olun - tıpkı değişken işlevde zaten tanımlanmışsa, var'ın noop olması gibi, nesneden içe aktarılan adlar açısından da noop'dur! Zaten tanımlanmakta olan isme ilişkin küçük bir uyarı, bunu çok daha güvenli hale getirecektir. Şahsen bu yüzden asla kullanmayacağım.
JavaScript'e yeni başlayan birçok kişinin, kalıtımın varsayılan olarak dilde mevcut olduğunu ve şu ana kadar tek kapsamın işlev kapsamının olduğunu anlamakta güçlük çektiğini gördüm. Geçen yılın sonunda yazdığım JSPretty adlı güzelleştiriciye bir uzantı sağladım. Özellik renkleri işlevi, koddaki kapsamı ve her zaman bu kapsamda bildirilen tüm değişkenlerle bir rengi ilişkilendirir. Kapanış, bir kapsamdan bir renge sahip bir değişken farklı bir kapsamda kullanıldığında görsel olarak gösterilir.
Özelliği şurada deneyin:
- http://prettydiff.com/jspretty.xhtml?c=white&jsscope
Şurada bir demo izleyin:
- http://prettydiff.com/jspretty.xhtml?c=white&jsscope&s=http://prettydiff.com/lib/markup_beauty.js
Kodu şu adreste görüntüleyin:
- http://prettydiff.com/lib/jspretty.js
- https://github.com/austincheney/Pretty-Diff/blob/master/lib/jspretty.js
Şu anda bu özellik, 16 iç içe işlev derinliği için destek sunuyor, ancak şu anda genel değişkenleri renklendirmiyor.
JavaScript'in yalnızca iki tür kapsamı vardır:
- Global Kapsam : Global, pencere düzeyinde kapsamdan başka bir şey değildir Burada, değişken uygulama boyunca mevcuttur.
- Fonksiyonel Kapsam :
var
Anahtar kelimeli bir fonksiyon içerisinde bildirilen değişkenin fonksiyonel kapsamı vardır.
Bir işlev çağrıldığında, bir değişken kapsam nesnesi oluşturulur (ve kapsam zincirine dahil edilir) ve ardından JavaScript'teki değişkenler gelir.
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Kapsam zinciri ->
- Pencere düzeyi -
a
veouter
işlev, kapsam zincirinde en üst düzeydedir. - dış işlev yeni olarak çağrıldığında
variable scope object
(ve kapsam zincirine dahil edildiğinde)b
içinde değişken ile eklendi .
Şimdi, bir değişken a
gerekli olduğunda, önce en yakın değişken kapsamını arar ve değişken yoksa, daha sonra değişken kapsam zincirinin bir sonraki nesnesine geçer. Bu durumda pencere seviyesidir.
Diğer yanıtlara ek olarak, kapsam, bildirilen tüm tanımlayıcıların (değişkenlerin) bir arama listesidir ve bunların şu anda çalışan kod tarafından nasıl erişilebilir olduğuna dair katı bir kurallar dizisini zorunlu kılar. Bu arama, bir LHS (sol taraf) referansı olan değişkene atama amaçlı olabilir veya bir RHS (sağ taraf) referansı olan değerini geri alma amacıyla olabilir. Bu aramalar, JavaScript motorunun kodu derlerken ve yürütürken dahili olarak yaptığı şeydir.
Bu açıdan bakıldığında, Kyle Simpson'ın Scopes and Closures e-kitabında bulduğum bir resmin yardımcı olacağını düşünüyorum:
E-kitabından alıntı:
Bina, programımızın iç içe geçmiş kapsam kural kümesini temsil eder. Binanın birinci katı, nerede olursanız olun, şu anda yürütmekte olduğunuz alanı temsil eder. Binanın en üst seviyesi küresel kapsamdır. LHS ve RHS referanslarını mevcut katınıza bakarak ve bulamazsanız, asansörü bir sonraki kata götürerek, oraya, sonra bir sonraki kata bakarak vb. Çözersiniz. En üst kata (küresel kapsam) ulaştığınızda, ya aradığınızı bulursunuz ya da bulamazsınız. Ama ne olursa olsun durmalısın.
Kayda değer bir nokta, "Kapsam araması, ilk eşleşmeyi bulduğunda durur".
Bu "kapsam seviyeleri" fikri, iç içe geçmiş bir işlevde aranıyorsa, yeni oluşturulan bir kapsamla "bunun" neden değiştirilebileceğini açıklar. İşte tüm bu ayrıntılara giren bir bağlantı, JavaScript kapsamı hakkında bilmek istediğiniz her şey
kodu çalıştırın. umarım bu kapsam belirleme hakkında bir fikir verir
Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
Name: 'object data',
f: function(){
alert(this.Name);
}
};
myObj.newFun = function(){
alert(this.Name);
}
function testFun(){
alert("Window Scope : " + window.Name +
"\nLocal Scope : " + Name +
"\nObject Scope : " + this.Name +
"\nCurrent document Scope : " + document.Name
);
}
testFun.call(myObj);
})(window,document);
Global Kapsam:
Küresel değişkenler tam olarak küresel yıldızlar gibidir (Jackie Chan, Nelson Mandela). Bunlara uygulamanızın herhangi bir bölümünden erişebilirsiniz (değeri alabilir veya ayarlayabilirsiniz). Küresel işlevler, küresel olaylar gibidir (Yeni Yıl, Noel). Bunları uygulamanızın herhangi bir bölümünden çalıştırabilirsiniz (çağırabilirsiniz).
//global variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
Yerel Kapsam:
ABD'deyseniz, ünlü ünlü Kim Kardashian'ı tanıyor olabilirsiniz (bir şekilde magazin dergilerini yapmayı başarıyor). Ancak ABD dışındaki insanlar onu tanımayacak. Bölgesine bağlı yerel bir yıldızdır.
Yerel değişkenler yerel yıldızlar gibidir. Bunlara yalnızca kapsam içinde erişebilir (değeri alabilir veya ayarlayabilirsiniz). Yerel bir işlev, yerel olaylar gibidir - yalnızca bu kapsamın içinde gerçekleştirebilirsiniz (kutlayabilirsiniz). Bunlara kapsam dışından erişmek isterseniz, bir referans hatası alırsınız.
function b(){
var d = 21; //local variable
console.log(d);
function dog(){ console.log(a); }
dog(); //execute local function
}
console.log(d); //ReferenceError: dddddd is not defined
Kapsamın derinlemesine anlaşılması için bu makaleye bakın
NEREDEYSE yalnızca iki tür JavaScript kapsamı vardır:
- her bir değişken bildiriminin kapsamı, en kısa sürede kapsayan işlevle ilişkilendirilir
- bir var bildirimi için çevreleyen bir işlev yoksa, bu genel kapsamdır
Bu nedenle, işlevler dışındaki herhangi bir blok yeni bir kapsam oluşturmaz. Bu, for-döngülerinin neden dış kapsamlı değişkenlerin üzerine yazdığını açıklar:
var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5
Bunun yerine işlevleri kullanmak:
var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10
İlk örnekte, blok kapsamı olmadığı için, başlangıçta bildirilen değişkenlerin üzerine yazılmıştır. İkinci örnekte, işlev nedeniyle yeni bir kapsam vardı, bu nedenle başlangıçta bildirilen değişkenler GÖLGELENDİ ve üzerine yazılmadı.
Aşağıdakiler dışında JavaScript kapsamı açısından bilmeniz gereken neredeyse her şey bu kadardır:
- try / catch, SADECE istisna değişkeninin kendisi için yeni kapsam sunar, diğer değişkenlerin yeni kapsamı yoktur.
- with-clause görünüşe göre başka bir istisnadır, ancak with-clause kullanılması kesinlikle önerilmez (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)
Dolayısıyla, JavaScript kapsamının her zaman sezgisel olmasa da aslında son derece basit olduğunu görebilirsiniz. Dikkat edilmesi gereken birkaç nokta:
- var bildirimleri kapsamın en üstüne kaldırılır. Bu, var bildirimi nerede olursa olsun, derleyiciye varlığın kendisi üstte gerçekleşiyormuş gibi olduğu anlamına gelir.
- aynı kapsamdaki çoklu değişken bildirimleri birleştirilir
Yani bu kod:
var i = 1;
function abc() {
i = 2;
var i = 3;
}
console.log(i); // outputs 1
eşdeğerdir:
var i = 1;
function abc() {
var i; // var declaration moved to the top of the scope
i = 2;
i = 3; // the assignment stays where it is
}
console.log(i);
Bu, sezgiye aykırı görünebilir, ancak zorunlu bir dil tasarımcısının bakış açısından anlamlıdır.
Modern Js, ES6 +, ' const
' ve ' let
'
Diğer büyük dillerin çoğunda olduğu gibi, oluşturduğunuz her değişken için blok kapsamını kullanıyor olmalısınız. var
olduğunu eskimiş . Bu, kodunuzu daha güvenli ve bakımı daha kolay hale getirir.
const
vakaların% 95'i için kullanılmalıdır . Değişken referansının değişmemesini sağlar. Dizi, nesne ve DOM düğümü özellikleri değişebilir ve muhtemelen değişmelidir const
.
let
yeniden atanması beklenen herhangi bir değişken için kullanılmalıdır. Buna bir for döngüsü dahildir. Değeri başlatmanın ötesinde değiştirirseniz, kullanın let
.
Blok kapsamı, değişkenin yalnızca bildirildiği parantez içinde kullanılabileceği anlamına gelir. Bu, kapsamınız dahilinde oluşturulan anonim işlevler dahil olmak üzere dahili kapsamları kapsar.
Bu ilginç örneği deneyin. Aşağıdaki örnekte a, 0'da ilklendirilmiş bir sayısal olsaydı, 0'ı ve sonra 1'i görürsünüz. A bir nesnedir ve javascript f1'e bir kopyası yerine a işaretçisi iletir. Sonuç, her iki seferde de aynı uyarıyı almanızdır.
var a = new Date();
function f1(b)
{
b.setDate(b.getDate()+1);
alert(b.getDate());
}
f1(a);
alert(a.getDate());
JS'de yalnızca işlev kapsamları vardır. Kapsamları engellemeyin! Neyin kaldırıldığını da görebilirsiniz.
var global_variable = "global_variable";
var hoisting_variable = "global_hoist";
// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);
if (true) {
// The variable block will be global, on true condition.
var block = "block";
}
console.log("global_scope: - block: " + block);
function local_function() {
var local_variable = "local_variable";
console.log("local_scope: - local_variable: " + local_variable);
console.log("local_scope: - global_variable: " + global_variable);
console.log("local_scope: - block: " + block);
// The hoisting_variable is undefined at the moment.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
var hoisting_variable = "local_hoist";
// The hoisting_variable is now set as a local one.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}
local_function();
// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
Anladığım kadarıyla 3 kapsam var: küresel kapsam, küresel olarak mevcut; yerel kapsam, bloklardan bağımsız olarak tüm işlev için kullanılabilir; ve blok kapsamı, yalnızca üzerinde kullanıldığı blok, ifade veya ifade için kullanılabilir. Global ve yerel kapsam, bir işlev içinde veya dışında 'var' anahtar sözcüğü ile belirtilir ve blok kapsamı 'let' anahtar sözcüğü ile gösterilir.
Yalnızca küresel ve yerel kapsam olduğuna inananlar için, lütfen Mozilla'nın JS'deki blok kapsamının nüanslarını açıklayan tam bir sayfaya sahip olduğunu açıklayın.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
JavaScript'te iki tür kapsam vardır:
- Yerel kapsam
- Global kapsam
Aşağıdaki işlevin yerel bir kapsam değişkeni vardır carName
. Ve bu değişkene işlevin dışından erişilemez.
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
Below Class, Global kapsam değişkenine sahiptir carName
. Ve bu değişkene sınıfın her yerinden erişilebilir.
class {
var carName = " Volvo";
// code here can use carName
function myFunction() {
alert(carName);
// code here can use carName
}
}
Henüz açıklanmayan ve ön uç kodlayıcıların sıklıkla karşılaştığı çok yaygın bir sorun, HTML'deki satır içi bir olay işleyicisi tarafından görülebilen kapsamdır - örneğin,
<button onclick="foo()"></button>
Bir on*
özniteliğin başvurabileceği değişkenlerin kapsamı şunlardan biri olmalıdır :
- global (satır içi işleyicilerle çalışmak neredeyse her zaman global değişkenlere başvurur)
- belgenin bir özelliği (örneğin,
querySelector
bağımsız bir değişken olarakdocument.querySelector
; nadiren) - işleyicinin bağlı olduğu öğenin bir özelliği (yukarıdaki gibi; nadir)
Aksi takdirde, işleyici çağrıldığında bir ReferenceError hatası alırsınız. Bu nedenle, örneğin, satır içi işleyici içinde tanımlanan bir işleve başvurursawindow.onload
veya $(function() {
satır içi işleyici yalnızca genel kapsamdaki değişkenlere başvurabileceği ve işlev genel olmadığı için başvuru başarısız olur:
window.addEventListener('DOMContentLoaded', () => {
function foo() {
console.log('foo running');
}
});
<button onclick="foo()">click</button>
Özellikleri document
ve işleyici içi yükleyiciler çağrılır için de içi işleyicileri içinde bağımsız değişkenler olarak ifade edilebilir bağlı olan elementin özelliklerinin içinde iki withblok için, bir document
elemanın için bir. Bu işleyicilerdeki değişkenlerin kapsam zinciri son derece sezgisel değildir ve çalışan bir olay işleyicisi muhtemelen bir işlevin global olmasını gerektirecektir (ve gereksiz küresel kirlilikten muhtemelen kaçınılmalıdır ).
Satır içi işleyicilerdeki kapsam zinciri çok tuhaf olduğundan ve satır içi işleyicilerin çalışması için küresel kirlilik gerektirdiğinden ve satır içi işleyiciler bazen argümanları iletirken çirkin dizelerden kaçmayı gerektirdiğinden, bunlardan kaçınmak muhtemelen daha kolaydır. Bunun yerine, addEventListener
HTML biçimlendirmesi yerine Javascript kullanarak (ile olduğu gibi ) olay işleyicileri ekleyin .
function foo() {
console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);
<button class="my-button">click</button>
Farklı bir notta, <script>
en üst seviyede çalışan normal etiketlerin aksine , ES6 modülleri içindeki kod kendi özel kapsamında çalışır. Normal bir <script>
etiketin üstünde tanımlanan bir değişken globaldir, bu nedenle ona aşağıdaki gibi diğer <script>
etiketlerde başvurabilirsiniz :
<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>
Ancak bir ES6 modülünün en üst seviyesi küresel değildir . ES6 modülünün tepesinde bildirilen bir değişken, değişken açıkça belirtilmediği export
veya global nesnenin bir özelliğine atanmadığı sürece yalnızca o modülün içinde görünür olacaktır .
<script type="module">
const foo = 'foo';
</script>
<script>
// Can't access foo here, because the other script is a module
console.log(typeof foo);
</script>
Bir ES6 modülünün en üst seviyesi, normalde en üst seviyedeki bir IIFE'nin iç kısmına benzer <script>
. Modül, global olan herhangi bir değişkene başvurabilir ve modül açıkça onun için tasarlanmadıkça hiçbir şey modülün içindeki hiçbir şeye referans veremez.
ES5
ve daha önce:
Javascript'teki değişkenler başlangıçta (ön ES6
) sözcüksel olarak işlev kapsamına alındı . Sözcüksel kapsamlı terimi, koda 'bakarak' değişkenlerin kapsamını görebileceğiniz anlamına gelir.
var
Anahtar kelimeyle bildirilen her değişken , işlevin kapsamı altındadır. Ancak, bu işlev içinde başka işlev bildirilirse, bu işlevler dış işlevlerin değişkenlerine erişebilir. Buna kapsam zinciri denir . Aşağıdaki şekilde çalışır:
- Bir işlev bir değişken değeri çözümlemeye çalıştığında, önce kendi kapsamına bakar. Bu, işlev gövdesidir, yani küme parantezleri {} arasındaki her şey ( bu kapsamdaki diğer işlevlerin içindeki değişkenler hariç ).
- Değişkeni fonksiyon gövdesi içinde bulamazsa zincire tırmanır ve fonksiyonun tanımlandığı fonksiyondaki değişken kapsamına bakar . Sözcük kapsamı ile kastedilen budur, bu işlevin nerede tanımlandığını kodda görebilir ve böylece kapsam zincirini yalnızca koda bakarak belirleyebiliriz.
Misal:
// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';
function outerFunc () {
// outerFunc scope
var foo = 'outerFunc';
var foobar = 'outerFunc';
innerFunc();
function innerFunc(){
// innerFunc scope
var foo = 'innerFunc';
console.log(foo);
console.log(bar);
console.log(foobar);
}
}
outerFunc();
Ne değişkenleri oturum çalışırken olur foo
, bar
ve foobar
konsola şudur:
- Konsola foo kaydetmeye çalışıyoruz, foo işlevin içinde bulunabilir
innerFunc
. Bu nedenle, foo'nun değeri dizeye çözümlenirinnerFunc
. - Konsola bar kaydetmeye çalışıyoruz, bar işlevin içinde bulunamıyor
innerFunc
. Bu nedenle, kapsam zincirine tırmanmamız gerekiyor . Önce fonksiyonuninnerFunc
tanımlandığı dış fonksiyona bakarız . İşlev budurouterFunc
. Kapsamında,outerFunc
'externalFunc' dizesini tutan değişken bar'ı bulabiliriz. - foobar innerFunc içinde bulunamıyor. . Bu nedenle, kapsam zincirini iç Fonksiyon kapsamına tırmanmamız gerekiyor . Burada da bulunamıyor, küresel kapsamda başka bir seviyeye tırmanıyoruz (yani en dış kapsam). Burada 'global' dizesini tutan değişken foobar'ı buluyoruz. Kapsam zincirine tırmandıktan sonra değişkeni bulamazsa, JS motoru bir referenceError atar .
ES6
(ES 2015) ve daha eski:
Aynı sözcüksel kapsam ve kapsam alanı kavramları hala geçerlidir ES6
. Ancak değişkenleri tanımlamanın yeni yolları tanıtıldı. Aşağıdakiler vardır:
let
: blok kapsamlı bir değişken oluştururconst
: başlatılması gereken ve yeniden atanamayan blok kapsamlı bir değişken oluşturur
Arasındaki en büyük fark var
ve let
/ const
olmasıdır var
fonksiyonu ise kapsamlı olan let
/ const
blok kapsamlı bulunmaktadır. İşte bunu açıklamak için bir örnek:
let letVar = 'global';
var varVar = 'global';
function foo () {
if (true) {
// this variable declared with let is scoped to the if block, block scoped
let letVar = 5;
// this variable declared with let is scoped to the function block, function scoped
var varVar = 10;
}
console.log(letVar);
console.log(varVar);
}
foo();
Yukarıdaki örnekte letVar global değeri günlüğe kaydeder çünkü ile bildirilen değişkenler let
blok kapsamlıdır. Kendi bloklarının dışında var olmaya son verirler, bu nedenle değişkene if bloğunun dışından erişilemez.
EcmaScript5'te, esas olarak iki kapsam vardır, yerel kapsam ve genel kapsam, ancak EcmaScript6'da esas olarak üç kapsamımız, yerel kapsam, genel kapsam ve blok kapsamı adı verilen yeni bir kapsam vardır .
Blok kapsamı örneği: -
for ( let i = 0; i < 10; i++)
{
statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
ECMAScript 6, let ve const anahtar sözcüklerini tanıttı. Bu anahtar sözcükler, var anahtar sözcüğü yerine kullanılabilir. Var anahtar kelimesinin tersine, let ve const anahtar sözcükleri, blok ifadeleri içindeki yerel kapsam bildirimini destekler.
var x = 10
let y = 10
const z = 10
{
x = 20
let y = 20
const z = 20
{
x = 30
// x is in the global scope because of the 'var' keyword
let y = 30
// y is in the local scope because of the 'let' keyword
const z = 30
// z is in the local scope because of the 'const' keyword
console.log(x) // 30
console.log(y) // 30
console.log(z) // 30
}
console.log(x) // 30
console.log(y) // 20
console.log(z) // 20
}
console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
Kabul edilen cevabı gerçekten beğendim ama şunu eklemek istiyorum:
Kapsam, bildirilen tüm tanımlayıcıların (değişkenlerin) bir arama listesini toplar ve korur ve bunların şu anda yürütülen kod tarafından nasıl erişilebilir olduğuna dair katı bir kurallar dizisini uygular.
Kapsam, değişkenleri tanımlayıcı adlarına göre aramak için kullanılan bir kurallar kümesidir.
- Bir değişken anlık kapsamda bulunamazsa, Engine, bulunana kadar veya en dıştaki (aka, genel) kapsama ulaşılıncaya kadar devam ederek sonraki dış kapsama alanına danışır.
- Bir değişkenin (tanımlayıcının) nerede ve nasıl aranacağını belirleyen kurallar kümesidir. Bu arama, bir LHS (sol taraf) referansı olan değişkene atama amacıyla olabilir veya bir RHS (sağ taraf) referansı olan değerini geri alma amacıyla olabilir. .
- LHS referansları, atama işlemlerinden kaynaklanır. Kapsamla ilgili atamalar, = operatörüyle veya işlev parametrelerine (atama) bağımsız değişkenler iletilerek gerçekleştirilebilir.
- JavaScript motoru, kodu çalıştırmadan önce derler ve bunu yaparken var a = 2 gibi ifadeleri böler. iki ayrı adımda: 1. İlk olarak, bu kapsamda bildirmek için var a. Bu, kodun çalıştırılmasından önce başlangıçta gerçekleştirilir. 2. Daha sonra, değişkeni (LHS referansı) aramak ve bulunursa ona atamak için a = 2.
- Hem LHS hem de RHS referans aramaları, şu anda yürütülen kapsamda başlar ve gerekirse (yani, orada aradıklarını bulamazlar), iç içe geçmiş kapsamda, bir kapsamda (kat ) bir seferde, global (en üst kat) olana kadar tanımlayıcıyı arar ve durur, bulur veya bulmaz. Doldurulmamış RHS referansları, ReferenceError'ın atılmasına neden olur. Doldurulmamış LHS referansları, otomatik, dolaylı olarak bu adın genel olarak oluşturulmuş (Katı Modda değilse) veya bir Referans Hatası (Katı Modda ise) ile sonuçlanır.
- kapsam, her biri tanımlayıcıların (değişkenler, işlevler) bildirildiği bir kap veya kova görevi gören bir dizi "baloncuk" dan oluşur. Bu kabarcıklar düzgün bir şekilde birbirlerinin içine yuva yapar ve bu yuva, yazar zamanında tanımlanır.