Javascript'teki değişkenlerin kapsamı nedir? Bir fonksiyonun dışında içeride aynı kapsama sahipler mi? Yoksa önemi var mı? Ayrıca, değişkenler global olarak tanımlanırsa nerede saklanır?
Javascript'teki değişkenlerin kapsamı nedir? Bir fonksiyonun dışında içeride aynı kapsama sahipler mi? Yoksa önemi var mı? Ayrıca, değişkenler global olarak tanımlanırsa nerede saklanır?
Yanıtlar:
JavaScript sözcüksel (statik olarak da adlandırılır) kapsamlandırma ve kapanışa sahiptir. Bu, kaynak koduna bakarak bir tanımlayıcının kapsamını anlatabileceğiniz anlamına gelir.
Dört kapsam:
Global ve modül kapsamındaki özel durumlar 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.
Kapsam, kod tabanının üzerinde bir tanımlayıcının geçerli olduğu bölgedir.
Sözcüksel ortam, tanımlayıcı adları ile bunlarla ilişkili değerler arasındaki eşlemedir.
Kapsam, sözcüksel ortamların bağlantılı bir yuvalamasından oluşur; yuvalamadaki 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üksel ortamlar bir kapsam "zinciri" oluştururlar. Tanımlayıcı çözünürlüğü, bu zincir boyunca eşleşen bir tanımlayıcı arama sürecidir.
Tanımlayıcı çözünürlüğü yalnızca bir yönde gerçekleşir: dışa doğru. Bu şekilde, dış sözcüksel ortamlar iç sözcüksel ortamları "göremez".
JavaScript'te bir tanımlayıcının kapsamına karar vermede üç ilgili faktör vardır :
Tanımlayıcıların bildirilebilme yollarından bazıları:
var
, let
veconst
var
Katı olmayan modda eksik )import
ifadelerieval
Konum tanımlayıcılarından bazıları bildirilebilir:
Kullanılarak bildirilen tanımlayıcıların , doğrudan genel bağlamda bildirilmeleri dışında var
işlev kapsamı vardır; bu durumda, genel nesneye özellikler olarak eklenir ve genel kapsamı vardır. eval
Fonksiyonlarda kullanımları için ayrı kurallar vardır .
Doğrudan küresel bağlamda beyan edildikleri zaman dışında blok kapsamı kullanılarak let
ve const
blok kapsamı olarak tanımlanmış tanımlayıcılar , bu durumda genel kapsamı vardır.
Not: let
, const
ve var
tüm askıya alır . Bu, mantıksal tanım konumlarının çevreleyen kapsamlarının (blok veya işlev) en üstünde olduğu anlamına gelir. Ancak değişkenler kullanıldığını beyan etti let
ve const
denetim kaynak kodundaki bildirim noktasını geçene kadar okunamaz veya atanamaz. Ara dönem geçici ö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 parametre adları, işlev gövdesine dahil edilir. Bunun hafif bir karmaşıklığı olduğuna dikkat edin. Varsayılan bağımsız değişken olarak bildirilen işlevler, işlev gövdesini değil, parametre listesini kapatır .
İşlev bildirimlerinin katı modda blok kapsamı ve katı olmayan modda fonksiyon kapsamı vardır. Not: katı olmayan mod, farklı tarayıcıların ilginç tarihsel uygulamalarına dayanan karmaşık bir acil kurallar kümesidir.
Adlandırılmış işlev ifadeleri kendilerine dahil edilir (örneğin özyineleme amacıyla).
Katı olmayan modda, genel nesne kapsam zincirinin en üstünde yer aldığından, genel nesne üzerindeki örtülü olarak tanımlanan özellikler genel kapsama sahiptir. Katı modda bunlara izin verilmez.
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,.
Aşağıdaki isimlerin çünkü ReferenceError atacağım x
, y
ve z
fonksiyonun 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ğıdaki için ReferenceError atacağım y
ve z
ancak için x
görünürlüğü nedeniyle, x
bloğun ile sınırlı değildir. 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ü dışında görünür 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 bildirilen değişkenlerin kapatılması konusunda dikkatli olmanız gerekir . Burada x
bildirilen değişkenin yalnızca bir örneği vardır ve bu, mantıksal olarak döngünün dışında yer alır.
Aşağıdakiler 5
beş kez yazdırılır ve daha sonra döngü dışına 5
altıncı kez yazdırılı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ğıdakiler yazdırılır undefined
çünkü x
blok kapsamlıdır. Geri aramalar eşzamansız olarak tek tek ç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 alamaz: ReferenceError
çünkü görünürlüğü x
blok tarafından kısıtlanmaz; ancak undefined
değişken ( if
ifadeden dolayı ) başlatılmadığı için yazdırılacaktır .
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
for
Kullanarak bir döngünün üstünde bildirilen bir değişken let
, döngünün gövdesine dahil edilir:
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
Aşağıdakiler atar, ReferenceError
çünkü görünürlüğü x
blok tarafından kısıtlanı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
var
Global bağlamda bildirilen değişkenler , global nesneye özellikler olarak eklendiğinden, aşağıdakiler genel nesne üzerinde bir özellik bildirir :
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
ve const
global bağlamda global nesneye özellikler eklemez, ancak yine de genel kapsamı vardır:
let x = 1
console.log(window.hasOwnProperty('x')) // false
İşlev parametrelerinin işlev gövdesinde bildirildiği kabul edilebilir:
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
Yakalama bloğu parametreleri catch-block gövdesine dahil edilir:
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 dahil edilir:
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
Katı olmayan modda, genel nesne üzerindeki dolaylı olarak tanımlanan özellikler genel olarak kapsamlandırılı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 kapsamı vardır.
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
Kapsam, bir tanımlayıcının geçerli olduğu kodun sözlüksel 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]]
başvuru olan gizli bir başvurusu 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ı oluşturur 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 nesnesindeki değeri yeni yürütme bağlamının sözcüksel ortamındaki bir dış başvuru alanına kopyalayarak yapar .
Yeni yürütme bağlamıyla işlev nesnesinin sözcüksel ortamı arasındaki bu bağlantıya kapatma adı verildiğini unutmayın .
Böylece, JavaScript'te kapsam, bir "zincir" içinde dış referanslarla birbirine bağlanan sözcüksel ortamlar aracılığıyla uygulanır. Sözcüksel ortamlar zincirine kapsam zinciri denir ve tanımlayıcı çözünürlüğü , 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. Genellikle bir global kapsam vardır ve tanımlanan her fonksiyonun kendi iç içe kapsamı vardır. Başka bir işlevde tanımlanan herhangi bir işlev, dış işleve bağlı yerel bir kapsama sahiptir. Her zaman kaynaktaki kapsamı tanımlayan konumdur.
Kapsam zincirindeki bir eleman temel olarak ana kapsamını gösteren bir Haritadır.
Bir değişkeni çözerken, javascript en iç kapsamda başlar ve dışa doğru arama yapar.
Küresel olarak beyan edilen değişkenlerin küresel bir kapsamı vardır. Bir işlev içinde bildirilen değişkenler o işleve dahil edilir ve aynı addaki global değişkenleri gölgelendirir.
(Eminim gerçek JavaScript programcılar diğer yanıtlar işaret etmek mümkün olacak birçok incelikler vardır ediyorum. Özellikle ben rastladım olarak bu sayfayı tam olarak ne hakkında this
herhangi bir zamanda aracı. Umarım bu daha tanıtıcı bağlantı Gerçi başlamak için yeterli olduğunu .)
Geleneksel olarak, JavaScript gerçekten sadece iki tür kapsama sahiptir:
Farkı açıklayan başka birçok cevap olduğu için bunun üzerinde ayrıntılı bir şekilde durmayacağım.
En son JavaScript gözlük şimdi de üçüncü bir kapsam sağlar:
Geleneksel olarak, değişkenlerinizi şu şekilde yaratırsınız:
var myVariable = "Some text";
Blok kapsam değişkenleri şu şekilde oluşturulur:
let myVariable = "Some text";
Fonksiyonel 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 döngü için ilkinde bilindiğini görüyoruz , ancak önce ve sonra değil. Ancak değişkenimiz i
tüm fonksiyonda bilinir.
Ayrıca, blok kapsamındaki değişkenlerin, kaldırılmadıkları için bildirilmeden önce bilinmediğini düşünün. Aynı blok kapsamındaki değişkeni aynı blok içinde yeniden bildirmenize de izin verilmez. Bu, blok kapsamındaki değişkenleri, genel veya işlevsel kapsamdaki değişkenlerden daha az hataya eğilimli hale getirir, bu da kaldırılır ve birden fazla bildirim olması durumunda hata üretmez.
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 transpiler ( Traceur veya babel-bağımsız gibi ) kullanıyorsanız, let
ifadeyi güvenle kullanabilirsiniz , ancak kodunuz performans açısından en uygun şey olabilir.
İstemci tarafı JavaScript kodu yazıyorsanız ve Düğüm tabanlı bir transpiler ( traceur shell betiği veya Babel gibi ) kullanıyorsanız, let
ifadeyi güvenle kullanabilirsiniz . Ve tarayıcınız sadece aktarılan kodu bileceğinden, performans dezavantajları sınırlı olmalıdır.
İstemci tarafı JavaScript kodu yazıyorsanız ve bir aktarıcı kullanmıyorsanız tarayıcı desteğini göz önünde bulundurmanız gerekir.
Bunlar hiç desteklemeyen bazı tarayıcılardır let
:
let
Bu yanıtı okurken hangi tarayıcıların ifadeyi desteklediğine dair güncel bir genel bakış için bu Can I Use
sayfaya bakın .
(*) Genel ve işlevsel kapsamdaki değişkenler, JavaScript değişkenleri kaldırıldığı için bildirilmeden önce başlatılabilir ve kullanılabilir . Bu, beyanların 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>
Kapakları ve bunları özel üyeler 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 karşı işlev düzeyi kapsamına sahip olmasıdır.
"Javascript 1.7" de (Mozilla'nın Javascript'e eklenmesi) blok-kapsam değişkenlerini şu let
ifadeyle bildirebilir :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
let
.
Brendan Eich tarafından orijinal olarak tasarlandığında JavaScript'te kapsam oluşturma fikri HyperCard kodlama dili HyperTalk'tan geldi .
Bu dilde, ekranlar bir dizin kartı yığınına benzer şekilde yapıldı. Arka plan olarak adlandırılan bir ana kart vardı. Şeffaftı ve alt kart olarak görülebilir. Bu ana karttaki herhangi bir içerik, üzerine yerleştirilmiş kartlarla paylaşıldı. En üste yerleştirilen her kartın, önceki karttan önce gelen, ancak istenirse yine de önceki kartlara erişimi olan kendi içeriği vardı.
Tam olarak JavaScript kapsam belirleme sistemi 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üm içerir. Değişken bir ortam, sözcüksel bir ortam ve bu bağlanma. Kart referansına geri dönersek, sözlü ortam, yığının altında bulunan önceki kartların tüm içeriğini içerir. Geçerli bağlam yığının en üstünde yer alır ve orada bildirilen herhangi bir içerik değişken ortamda depolanır. Çarpışmaların adlandırılması durumunda değişken ortam öncelikli olacaktır.
Bu bağlanma, içeren nesneyi gösterecektir. Bazen kapsamlar veya yürütme bağlamları, içerilen nesnenin olabileceği bildirilen bir işlev window
veya bir yapıcı işlevi gibi, içerilen nesne değişmeden değişir .
Bu yürütme bağlamları, denetimin aktarıldığı her seferde oluşturulur. Kod yürütülmeye başladığında kontrol aktarılır ve bu öncelikle işlev yürütmesinden yapılır.
Yani teknik açıklama budur. Uygulamada, JavaScript'te bunu hatırlamak önemlidir.
Bunu bu sayfadaki önceki örneklerden birine (5. "Kapatma") uygulayarak yürütme bağlamları yığınını takip etmek mümkündür. Bu örnekte, yığınta üç bağlam vardır. Dış bağlam, var altı tarafından çağrılan hemen çağrılan fonksiyondaki bağlam ve var altı'nın hemen çağrılan fonksiyonunun içindeki döndürülen fonksiyondaki bağlam ile tanımlanırlar.
i ) Dış bağlam. A = 1 değişken ortamına sahiptir
ii ) IIFE bağlamı, a = 1 lexik ortamına sahiptir, ancak yığında öncelikli olan a = 6 değişken ortamına sahiptir
iii ) Döndürülen fonksiyon bağlamı a = 6 ortamına sahiptir ve çağrıda uyarıda belirtilen değerdir.
1) Küresel bir kapsam, bir fonksiyon kapsamı ve with ve catch kapsamları vardır. Değişkenler için genel olarak 'blok' seviye kapsamı yoktur - with ve catch deyimleri bloklarına isim ekler.
2) Kapsamlar, global kapsamın sonuna kadar işlevler tarafından iç içe yerleştirilir.
3) Özellikler, prototip zinciri üzerinden çözülür. With ifadesi, nesne özelliği adlarını with bloğu tarafından tanımlanan sözcük kapsamına getirir.
DÜZENLEME: ECMAAScript 6 (Harmony) izin desteklemek için spesifikasyon olduğunu ve krom 'uyum' bayrağı izin verir biliyorum, bu yüzden belki de destekliyor ..
Izin vermek blok düzeyinde kapsam belirleme için bir destek, ama bunu yapmak için anahtar kelime kullanmak zorunda.
EDIT: Benjamin'in yorumlarda ve catch ifadeleri işaret dayalı, yazı düzenledim ve daha 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 takma olarak yerleştirilir.
//chrome (v8)
var a = { 'test1':'test1val' }
test1 // error not defined
with (a) { var test1 = 'replaced' }
test1 // undefined
a // a.test1 = 'replaced'
EDIT: Açıklayıcı örnek:
test1, tümce bloğuna dahil edilir, ancak a.test1'e takma olarak adlandırılır. 'Var test1', üstteki sözcüksel bağlamda (işlev veya global) yeni bir değişken1 oluşturur;
Olmadı! 'With' komutunu kullanırken dikkatli olun - değişkenin işlevde zaten tanımlanmış olması durumunda var'ın bir noop olması gibi, nesneden içe aktarılan isimler açısından da bir nooptur! Zaten tanımlanmış olan isme biraz kafa vurmak bunu daha güvenli hale getirecektir. Ben şahsen bu yüzden asla kullanmayacağım.
with
deyim olan blok kapsam belirleme şeklidir ama catch
cümleleri çok daha sık görülen (İşin eğlenceli yanı, v8 uygular olan catch
bir ile with
) - JavaScript kendisi (Yani, fonksiyon, küresel, deneme / yakalama hemen hemen blok Kapsam belirlemenin tek formlarını var Bununla birlikte, ana bilgisayar ortamları farklı kapsamlandırma kavramlarına sahiptir - örneğin tarayıcıdaki satır içi olaylar ve NodeJS'nin vm modülü.
JavaScript'te yeni olan birçok kişinin, dilde kalıtımın varsayılan olarak kullanılabildiğini ve işlev kapsamının şimdiye kadar tek kapsam olduğunu anlamada sorun yaşadığını gördüm. Geçen yılın sonunda yazdığım bir güzelliğe JSPretty adlı bir uzantı sağladım. Özellik renkleri koddaki kapsamı işlev görür ve her zaman bir rengi o kapsamda bildirilen tüm değişkenlerle ilişkilendirir. Bir kapsamdan bir renge sahip bir değişken farklı bir kapsamda kullanıldığında kapatma görsel olarak gösterilir.
Özelliği şurada deneyin:
Bir demoya bakın:
Kodu şurada görüntüleyin:
Şu anda bu özellik 16 iç içe işlev derinliği için destek sunuyor, ancak şu anda global değişkenleri renklendirmiyor.
JavaScript'in yalnızca iki tür kapsamı vardır:
var
Anahtar sözcük içeren bir işlev içinde bildirilen değişkenin işlevsel kapsamı vardır.Bir işlev çağrıldığında, bir değişken kapsam nesnesi oluşturulur (ve kapsam zincirine dahil edilir), bunu JavaScript'teki değişkenler izler.
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Kapsam zinciri ->
a
ve outer
fonksiyon kapsam zincirinde en üst seviyededir.variable scope object
değişken b
bulunan yeni (ve kapsam zincirine dahil) olarak adlandırıldığında.Şimdi bir değişken a
gerektiğinde ilk önce en yakın değişken kapsamını arar ve eğer değişken orada değilse, değişken kapsam zincirinin bir sonraki nesnesine geçer. Bu durumda pencere seviyesi.
Diğer yanıtlara eklemek için kapsam, bildirilen tüm tanımlayıcıların (değişkenler) bir arama listesidir ve bunların şu anda kod yürütme için nasıl erişilebilir olduğuna ilişkin katı bir kurallar dizisi uygular. Bu arama, bir LHS (sol taraf) referansı olan değişkene atama veya bir RHS (sağ taraf) referansı olan değerinin alınması amacıyla olabilir. Bu aramalar, JavaScript motorunun kodu derlerken ve yürütürken dahili olarak yaptığı şeydir.
Bu açıdan bakıldığında, bir resmin Kyle Simpson'ın Scopes and Closures e-kitabında bulduğumda yardımcı olacağını düşünüyorum:
E-kitabından alıntı:
Bina, programımızın iç içe kapsam kural kümesini temsil eder. Binanın birinci katı, nerede olursanız olun şu anda yürütmekte olduğunuz kapsamı temsil eder. Binanın en üst seviyesi küresel kapsamdır. Mevcut katınıza bakarak LHS ve RHS referanslarını çözersiniz ve bulamazsanız asansörü bir sonraki kata götürür, oraya bakar, sonra bir sonrakine vb. En üst kata (global kapsam) ulaştığınızda, aradığınızı bulursunuz veya bulmazsınız. Ama ne olursa olsun durmalısın.
Bahsetmeye değer bir nokta, "İlk aramayı bulduktan sonra Kapsam araması durur".
Bu "kapsam düzeyleri" fikri, iç içe geçmiş bir işleve bakılıyorsa "bu" nun neden yeni oluşturulmuş bir kapsamla 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);
Küresel değişkenler aynen küresel yıldızlar gibidir (Jackie Chan, Nelson Mandela). Bunlara, uygulamanızın herhangi bir bölümünden erişebilir (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 yürütebilir (arayabilirsiniz).
//global variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
ABD'deyseniz, ünlü olan Kim Kardashian'ı tanıyabilirsiniz (bir şekilde tabloidleri yapmayı başarıyor). Fakat ABD dışındaki insanlar onu tanımayacaklar. Bölgesine bağlı yerel bir yıldızdır.
Yerel değişkenler yerel yıldızlar gibidir. Bunlara yalnızca kapsam içinden erişebilir (değeri alabilir veya ayarlayabilirsiniz). Yerel bir işlev yerel etkinlikler gibidir - bu kapsamda yalnızca yürütme (kutlama) yapabilirsiniz. Bunlara kapsamın dışından erişmek istiyorsanız, 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 göz atın.
ALMOST yalnızca iki tür JavaScript kapsamı içerir:
Bu nedenle, işlevler dışındaki bloklar yeni bir kapsam oluşturmaz. Bu, for-loop'ların neden dış kapsamdaki değişkenlerin üzerine yazıldığı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 kullanma:
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, hiçbir blok kapsamı bulunmadığından, 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ÖSTERİLDİ ve üzerine yazılmadı.
JavaScript kapsamı açısından bilmeniz gereken hemen hemen hepsi bu, ancak:
Böylece JavaScript kapsamının her zaman sezgisel olmasa da aslında son derece basit olduğunu görebilirsiniz. Dikkat edilmesi gereken birkaç nokta:
Yani bu kod:
var i = 1;
function abc() {
i = 2;
var i = 3;
}
console.log(i); // outputs 1
şuna eşittir:
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 karşı sezgisel görünebilir, ancak zorunlu bir dil tasarımcısı açısından mantıklıdır.
const
' ve ' let
'Diğer büyük diller gibi, oluşturduğunuz her değişken için blok kapsamayı kullanmanız gerekir. var
olduğunu eskimiş . Bu, kodunuzu daha güvenli ve daha bakımlı hale getirir.
const
vakaların% 95'inde kullanılmalıdır . Değişken başvurusunun değişmemesi için bunu yapar . Dizi, nesne ve DOM düğümü özellikleri değişebilir ve olması gerekir const
.
let
yeniden atanması beklenen herhangi bir değişken için kullanılmalıdır. Buna bir for döngüsü dahil. Değeri başlatmanın ötesinde değiştirirseniz, kullanın let
.
Blok kapsamı, değişkenin yalnızca bildirildiği köşeli parantez içinde kullanılabileceği anlamına gelir. Bu, kapsamınızda oluşturulan anonim işlevler dahil olmak üzere dahili kapsamlara kadar uzanır.
Bu meraklı örneği deneyin. Aşağıdaki örnekte, a 0 değerinde başlatılmış bir sayısal olsaydı, 0 ve sonra 1 görürsünüz. A dışında bir nesnedir ve javascript f1 öğesinin bir kopyasından ziyade bir işaretçisini geçirir. Sonuç, aynı uyarıyı her iki seferde de 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; bloklar ne olursa olsun tüm fonksiyonlar için yerel kapsam; ve blok kapsamı, yalnızca üzerinde kullanıldığı blok, ifade veya ifade için kullanılabilir. Genel ve yerel kapsam, bir işlev içinde veya dışında 'var' anahtar sözcüğüyle ve blok kapsamı 'let' anahtar sözcüğüyle belirtilir.
Yalnızca küresel ve yerel kapsam olduğuna inananlar için, lütfen Mozilla'nın neden JS'deki blok kapsamının nüanslarını açıklayan tüm bir sayfaya sahip olacağını açıklayın.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Henüz açık uç kodlayıcıların sık sık karşılaştığı çok yaygın bir sorun, HTML'deki bir satır içi 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 :
querySelector
bağımsız bir değişken olarak document.querySelector
; nadir)Aksi takdirde, işleyici çağrıldığında ReferenceError alırsınız. Bu nedenle, örneğin, satır içi işleyici içinde tanımlanan bir işleve başvuruyorsa window.onload
veya $(function() {
başvuru başarısız olursa, çünkü satır içi işleyici yalnızca genel kapsamdaki değişkenlere başvurabilir ve işlev genel değildir:
Ö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 with
blok için, bir document
elemanın için bir. Bu işleyicilerin içindeki değişkenler kapsam zinciri son derece sezgisel değildir ve çalışan bir olay işleyicisi muhtemelen küresel olmak için bir işlev gerektirecektir (ve gereksiz küresel kirlilikten kaçınılmalıdır ).
Satır içi işleyicilerin içindeki kapsam zinciri çok garip olduğu ve satır içi işleyicilerin çalışması için küresel kirliliğe ihtiyaç duyduklarından ve satır içi işleyiciler bazen bağımsız değişkenleri geçerken çirkin dizelerden kaçmayı gerektirdiğinden, bunlardan kaçınmak daha kolaydır. Bunun yerine, addEventListener
HTML işaretlemesinden ziyade Javascript (gibi ) kullanarak olay işleyicileri ekleyin .
Farklı bir notta, <script>
en üst düzeyde çalışan normal etiketlerin aksine , ES6 modüllerinin içindeki kod kendi özel kapsamında çalışır. Normal bir <script>
etiketin üstünde tanımlanan bir değişken globaldir, bu nedenle aşağıdaki <script>
gibi diğer etiketlerde referans verebilirsiniz :
Ancak ES6 modülünün en üst seviyesi global değildir . ES6 modülünün üstünde bildirilen bir değişken, değişken açıkça export
düzenlenmedikçe veya global nesnenin bir özelliğine atanmadıkça yalnızca bu modülün içinde görünür .
Bir ES6 modülünün en üst seviyesi, normalde en üst seviyedeki IIFE'nin iç seviyesine benzer <script>
. Modül, global olan herhangi bir değişkene başvurabilir ve modül açıkça bunun için tasarlanmadığı sürece hiçbir şey modülün içindeki herhangi bir şeye başvuramaz.
JavaScript'te iki tür kapsam vardır:
Below fonksiyonunun yerel bir kapsam değişkeni vardır carName
. Ve bu değişkene fonksiyonun dışından erişilemez.
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
Aşağıdaki Sınıf bir 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
}
}
ES5
ve öncesi:Javascript'teki değişkenler başlangıçta (pre ES6
) lexically fonksiyon kapsamı kapsamına alınmıştır. Sözcüksel olarak kapsamlandırılmış terimi, koda 'bakarak' değişkenlerin kapsamını görebileceğiniz anlamına gelir.
var
Anahtar kelimeyle bildirilen her değişken işleve dahil edilir. Ancak, bu işlev içinde başka işlev bildirilirse, bu işlevlerin dış işlevlerin değişkenlerine erişimi olur. Buna kapsam zinciri denir . Aşağıdaki şekilde çalışır:
// 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:
innerFunc
. Bu nedenle, foo değeri dizeye çözümlenir innerFunc
.innerFunc
. Bu nedenle, kapsam zincirine tırmanmamız gerekiyor . İlk olarak, işlevin innerFunc
tanımlandığı dış işleve bakarız . Bu işlev outerFunc
. Bu kapsamda outerFunc
'outerFunc' dizesini tutan değişken çubuğu bulabiliriz.ES6
(ES 2015) ve daha eski sürümler:Sözcüksel kapsam ve skopekinin aynı kavramları hala geçerlidir ES6
. Bununla birlikte, değişkenleri bildirmenin yeni yolları tanıtıldı. Aşağıdakiler var:
let
: blok kapsamı değişkeni oluştururconst
: yeniden başlatılması gereken ve yeniden atanamayan bir blok kapsamındaki değişken oluştururArası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 gösteren 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 değeri, global olarak günlüğe kaydeder çünkü bildirilen değişkenler let
blok kapsamlıdır. İlgili bloklarının dışında kalmayı bırakırlar, bu nedenle değişkene if bloğunun dışında erişilemez.
EcmaScript5'te esas olarak yerel kapsam ve küresel kapsam olmak üzere iki kapsam vardır, ancak EcmaScript6'da esas olarak üç kapsamımız vardır, yerel kapsam, global kapsam ve blok kapsamı adı verilen yeni bir kapsam .
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 kelimelerini tanıttı. Bu anahtar kelimeler var anahtar sözcüğü yerine kullanılabilir. Var anahtar sözcüğünün aksine, let ve const anahtar sözcükleri, blok deyimleri 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 seviyorum ama bunu eklemek istiyorum:
Scope, bildirilen tüm tanımlayıcıların (değişkenler) bir arama listesini toplar ve saklar ve bunların şu anda yürütülen kod için nasıl erişilebilir olduğuna ilişkin katı kurallar uygular.
Kapsam, değişkenleri tanımlayıcı adlarına göre aramak için bir dizi kuraldır.
JavaScript'te iki tür kapsam vardır.
Global kapsam : global kapsamda ilan edilen değişken, programın herhangi bir yerinde sorunsuz bir şekilde kullanılabilir. Örneğin:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
İşlevsel kapsam veya Yerel kapsam : bu kapsamda bildirilen değişken yalnızca kendi işlevinde kullanılabilir. Örneğin:
// code here can not use carName
function myFunction() {
var carName = "BMW";
// code here can use carName
}