let
في JavaScript
تعبير let
يُصرِّح عن متغير محلي للقسم الكتلي، ويمكن تهيئة قيمته الابتدائية اختياريًا.
البنية العامة
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
varnameN
اسم المتغير، ويمكن أن يكون أيّ معرِّف صالح في JavaScript.
valueN
القيمة الابتدائية للمتغير، ويمكن استخدام أيّ تعبير (expression) صالح في JavaScript.
الوصف
التعبير let
يسمح بالتصريح عن متغيرات يكون مجالها (scope) محدودًا إلى القسم الكتلي (block statement)، أو إلى التعبير (expression) الذي اُستخدِمَ فيه؛ وهو على النقيض من الكلمة المحجوزة var
التي تُعرِّف متغيرًا عامًا، أو محليًا إلى الدالة الحالة بغض النظر عن مجال القسم الكتلي (block scope).
قواعد المجالات
المتغيرات المُصرَّح عنها عبر let
سيكون مجالها هو القسم الكتلي الذي عُرِّفَت فيه إضافةً إلى أيّة أقسام فرعية محتواة داخله؛ وهذا يعني أنَّ المتغيرات المُصرَّح عنها عبر let
تشبه كثيرًا var
؛ لكن الاختلاف الرئيسي بينهما هو أنَّ مجال متغيرات var
سيكون كامل الدالة التي تحتويها:
function varTest() {
var x = 1;
if (true) {
var x = 2; // المتغير نفسه
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // متغير مختلف
console.log(x); // 2
}
console.log(x); // 1
}
شيفرة أوضح في الدوال الداخلية
تسمح المتغيرات المُعرَّفة عبر let
بإنشاء شيفرة أكثر وضوحًا عند استخدام الدوال الداخلية (inner functions):
var list = document.getElementById('list');
for (let i = 1; i <= 5; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
list.appendChild(item);
}
// لإنشاء نفس التأثير لكن مع استخدام var
// فعلينا إنشاء سياق مختلف
// باستخدام تعبير مغلق للحفاظ على القيمة closure
for (var i = 1; i <= 5; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
(function(i){
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
})(i);
list.appendChild(item);
}
عمِلَ المثال السابق كما ينبغي لأن النسخ الخمس من الدالة الداخلية المجهولة (anonymous inner function) تُشير إلى خمس نسخ مختلفة من المتغير i
، لاحظ أنَّ المثال السابق لن يعمل إذا وضعت var
بدلًا من let
لأنَّ جميع الدوال الداخلية ستُعيد آخر قيمة للمتغير i
وهي 6
؛ لاحظ أننا أبقينا المجال حول حلقة التكرار واضحًا بنقلنا لجميع الشيفرات التي تُنشِئ العناصر الجديدة إلى مجال كل تكرار.
يجدر بالذكر أنَّ let
(على النقيض من var
) لن تُنشِئ خاصيةً في الكائن العام (global object)، مثلًا:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
محاكاة المتغيرات الخاصة
عند التعامل مع الدوال البانية، فمن الممكن استخدام let
لمشاركة عنصر أو أكثر دون استخدام التعابير المغلقة (closures):
var Thing;
{
let privateScope = new WeakMap();
let counter = 0;
Thing = function() {
this.someProperty = 'foo';
privateScope.set(this, {
hidden: ++counter,
});
};
Thing.prototype.showPublic = function() {
return this.someProperty;
};
Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}
console.log(typeof privateScope);
// "undefined"
var thing = new Thing();
console.log(thing);
// Thing {someProperty: "foo"}
thing.showPublic();
// "foo"
thing.showPrivate();
// 1
المناطق الميتة زمنيًا والأخطاء مع let
إعادة التصريح عن نفس المتغير ضمن الدالة أو القسم نفسه سيؤدي إلى SyntaxError
:
if (x) {
let foo;
let foo; // SyntaxError
}
في ECMAScript 2015 لن تنتقل التصريحات عن المتغيرات عبر let
إلى أعلى سياق التنفيذ الحالي (current execution context)، والإشارة إلى المتغيرات في القسم الكتلي قبل تهيئتها سيؤدي إلى حدوث ReferenceError
(على النقيض من المتغيرات المُصرَّح عنها عبر var
، والتي ستملك القيمة undefined
). أي أنَّ المتغير سيكون في «منطقة ميتة زمنيًا» (temporal dead zone) بدءًا من بداية القسم الكتلي حتى تتم تهيئة المتغير.
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
}
قد تواجه أخطاء في تعابير switch
لأنَّها تُشكِّل قسمًا كتليًا وحيدًا:
let x = 1;
switch(x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError بسبب إعادة التصريح
break;
}
لكن من المهم الإشارة إلى أنَّ الأقسام الكتلية المتشعبة ضمن قسم case
ستؤدي إلى إنشاء مجال كتلي جديد، ولن تظهر هنا أخطاء تتعلق بإعادة التصريح عن المتغيرات:
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
المجالات الكتلية
عند استخدام الكلمة المحجوزة let
داخل كتلة (block) فسيكون مجال المتغير محدودًا إلى تلك الكتلة، لاحظ الاختلافات بينها وبين var
التي يكون مجالها هو كامل الدالة التي عُرِّفَت فيها:
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // المجال عام
let b = 22; // المجال هو داخل التعبير الشرطي فقط
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
دعم المتصفحات
الميزة | Chrome | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
الدعم الأساسي | 41 | 44 | 11 | 17 | 10 |
مصادر ومواصفات
- مسودة المعيار ECMAScript Latest Draft.
- معيار ECMAScript 2015 (6th Edition).