ما الفرق بين استخدام "دع" و "فار"؟
-
11-09-2019 - |
سؤال
ecmascript 6 قدمت ال let
بيان.
لقد سمعت أنه تم وصفه بأنه متغير "محلي"، لكنني ما زلت غير متأكد من كيفية تخصيصه بشكل مختلف عن var
الكلمة الرئيسية.
ما هي الاختلافات؟ عندما يجب let
أن تستخدم أكثر var
?
المحلول
قواعد النطاق
الفرق الرئيسي هو قواعد النطاق. المتغيرات المعلنة var
يتم استبدال الكلمة الأساسية إلى هيئة الوظيفة الفورية (وبالتالي نطاق الوظيفة) أثناء let
يتم استبدال المتغيرات إلى الفوري أرفق كتلة تشير إليها { }
(وبالتالي نطاق كتلة).
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
السبب let
تم تقديم الكلمة الأساسية إلى اللغة كانت نطاق الوظيفة مربكة وكانت واحدة من المصدر الرئيسي للعلة في جافا سكريبت.
نلقي نظرة على هذا المثال من سؤال آخر Stackoverflow:
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3
كان الإخراج إلى وحدة التحكم في كل مرة funcs[j]();
تم الاحتجاج منذ أن كانت الوظائف المجهولة مرتبطة بنفس المتغير نفسه.
كان على الناس إنشاء مهام إنشاء على الفور لالتقاط القيمة الصحيحة من الحلقات، لكن ذلك كان أيضا شعر.
الرفع
بينما أعلن المتغيرات مع var
الكلمة الرئيسية هي "رفع" إلى الجزء العلوي من الكتلة مما يعني أنهم يمكن الوصول إليهم في نطاقهم المحيط حتى قبل إعلانهم:
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
لن يتم تهيئة المتغيرات حتى يتم تقييم تعريفها. الوصول إليها قبل أن يؤدي التهيئة إلى ReferenceError
. وبعد وقال المتغير ليكون في "المنطقة الميتة الزمنية" من بداية الكتلة حتى تتم معالجة التهيئة.
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
إنشاء خاصية كائن عالمي
في المستوى الأعلى، let
, ، على عكس var
, ، لا ينشئ خاصية على الكائن العالمي:
var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
reteclaration.
في وضع صارم، var
سوف تتيح لك إعادة إعلان نفس المتغير في نفس النطاق أثناء let
يرفع SyntaxError.
'use strict';
var foo = "foo1';
var foo = "foo2'; // No problem, 'foo' is replaced.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
نصائح أخرى
let
يمكن أن تستخدم أيضا لتجنب مشاكل الإغلاق. إنها تربط القيمة الطازجة بدلا من الاحتفاظ بمرجع قديم كما هو موضح في الأمثلة أدناه.
for(var i=1; i<6; i++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
يدل الرمز أعلاه مشكلة إغلاق JavaScript الكلاسيكية. إشارة إلى i
يتم تخزين المتغير في إغلاق معالج النقر، بدلا من القيمة الفعلية لل i
.
سيشير كل معالج نقرة واحدة إلى نفس الكائن لأنه يوجد كائن عداد واحد فقط يحمل 6 حتى تحصل على ستة في كل نقرة.
الحلجوم العام هو التفاف هذا في وظيفة مجهولة وتمرير i
كحجة. يمكن أيضا تجنب هذه القضايا الآن باستخدام let
في حين أن var
كما هو موضح في التعليمات البرمجية أدناه.
(تم اختباره في Chrome و Firefox 50)
for(let i=1; i<6; i++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
ما الفرق بين let
و var
?
- متغير محدد باستخدام
var
بيان معروف في جميع أنحاء الوظيفة يتم تعريفه، من بداية الوظيفة.* - متغير محدد باستخدام
let
بيان معروف فقط في الكتلة يتم تعريفه، من اللحظة التي يتم تعريفها فصاعدا.**
لفهم الفرق، فكر في التعليمات البرمجية التالية:
// 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
هنا، يمكننا أن نرى أن المتغير لدينا j
يعرف فقط في أول حلقة، ولكن ليس قبل وبعد. ومع ذلك، متغيرنا i
هو معروف في الوظيفة بأكملها.
أيضا، فكر في هذه المتغيرات المبكعة غير معروفة قبل إعلانها لأنها لا يتم رفعها. أنت أيضا غير مسموح لك بإعادة إظهار نفس متغير الكتلة المذكورة داخل الكتلة نفسها. هذا يجعل Block Scoped Playbles أقل عرضة للخطأ من المتغيرات على مستوى العالم أو عجزيا، والتي يتم رفعها والتي لا تنتج أي أخطاء في حالة تصريحات متعددة.
هل هو آمن للاستخدام let
اليوم؟
من شأن بعض الأشخاص يجادلون أنه في المستقبل سوف نستخدم فقط البيانات والبيانات الفاورة ستصبح عفا عليها الزمن. جافا سكريبت جرو كايل سيمبسون كتب مقال متقن للغاية حول سبب قيامه بأنه لن يكون هذا هو الحال.
اليوم، ومع ذلك، هذا بالتأكيد ليس هو الحال. في الواقع، نحن بحاجة في الواقع أن نسأل أنفسنا ما إذا كانت آمنة لاستخدام let
بيان. الإجابة على هذا السؤال يعتمد على بيئتك:
إذا كنت تكتب رمز JavaScript جانب الخادم (node.js.)، يمكنك استخدام بأمان
let
بيان.إذا كنت تكتب رمز JavaScript جانب العميل واستخدم مقصور مستيقظ (مثل تريسور أو Babel-Startalone.)، يمكنك استخدام بأمان
let
العبارة، ولكن من المحتمل أن يكون رمزك أي شيء ولكن الأمثل فيما يتعلق بالأداء.إذا كنت تكتب رمز JavaScript جانب العميل واستخدم مقصور عقدة مقرها (مثل traceur shell script. أو بابل)، يمكنك استخدام بأمان
let
بيان. ولأن متصفحك سوف يعرف فقط عن التعليمات البرمجية التي تم التردد فيها، يجب أن تكون عيوب الأداء محدودة.إذا كنت تكتب رمز JavaScript جانب العميل ولا تستخدم Transpiler، فأنت بحاجة إلى النظر في دعم المتصفح.
لا تزال هناك بعض المتصفحات التي لا تدعم
let
على الاطلاق:
كيفية تتبع دعم المتصفح
للحصول على نظرة عامة حديثة على المتصفحات التي تدعم let
بيان في وقت قراءتك هذه الإجابة، انظر هذه Can I Use
صفحة.
* يمكن تهيئة المتغيرات المبكرة على مستوى العالم واستخدامها وتعديلها قبل إعلانها لأن متغيرات JavaScript هي رافعة. هذا يعني أن الإعلانات هي دائما الكثير على الجزء العلوي من النطاق.
** لا يتم رفع متغيرات كتلة النحافة
هنا شرح let
الكلمة الرئيسية مع بعض الأمثلة.
let
يعمل كثيرا مثلvar
. وبعد الفرق الرئيسي هو أن نطاقvar
المتغير هو وظيفة مرفق بأكملها
هذه الطاولة على ويكيبيديا يظهر المتصفحات التي تدعم جافا سكريبت 1.7.
لاحظ أن متصفحات Mozilla و Chrome فقط تدعمها. أي سفاري، ويحتمل أن يكون الآخرون لا.
الجواب المقبول يفتقد نقطة:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
let
كتلة النطاق
أعلنت المتغيرات باستخدام let
الكلمة الرئيسية هي كذا مجلة، مما يعني أنها متوفرة فقط في منع التي أعلن فيها.
على المستوى الأعلى (خارج وظيفة)
في المستوى الأعلى، أعلنت المتغيرات باستخدام let
لا تنشئ خصائص على الكائن العالمي.
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined
داخل وظيفة
داخل وظيفة (ولكن خارج كتلة)، let
لديه نفس النطاق var
.
(() => {
var functionScopedVariable = 42;
let blockScopedVariable = 43;
console.log(functionScopedVariable); // 42
console.log(blockScopedVariable); // 43
})();
console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
داخل كتلة
أعلن المتغيرات باستخدام let
داخل كتلة لا يمكن الوصول إليها خارج تلك الكتلة.
{
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
}
console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
داخل حلقة
أعلن المتغيرات مع let
في حلقات يمكن الإشارة إليها فقط داخل هذه الحلقة.
for (var i = 0; i < 3; i++) {
var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4
for (let k = 0; k < 3; k++) {
let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.
حلقات مع الإغلاق
كما ترى let
بدلا من var
في حلقة، مع كل التكرار تحصل على متغير جديد. هذا يعني أنه يمكنك استخدام إغلاق بأمان داخل حلقة.
// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
المنطقة الميتة الزمنية
بسبب المنطقة الميتة الزمنية, ، والمتغيرات المعلنة باستخدام let
لا يمكن الوصول إليها قبل إعلانها. محاولة القيام بذلك يلقي خطأ.
console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;
لا إعادة الإعلان
لا يمكنك إعلان نفس المتغير عدة مرات باستخدام let
. وبعد لا يمكنك أيضا إعلان متغير باستخدام let
مع نفس المعرف كمتغير آخر تم إعلانه باستخدام var
.
var a;
var a; // Works fine.
let b;
let b; // SyntaxError: Identifier 'b' has already been declared
var c;
let c; // SyntaxError: Identifier 'c' has already been declared
const
const
يشبه تماما let
- من كتلة البكوب ولديها TDZ. ومع ذلك، هناك شيئان مختلفان.
لا إعادة تعيين
المعلن عن المتغير const
لا يمكن إعادة تعيينها.
const a = 42;
a = 43; // TypeError: Assignment to constant variable.
لاحظ أنه لا يعني أن القيمة ثابتة. خصائصها لا تزال يمكن تغييرها.
const obj = {};
obj.a = 42;
console.log(obj.a); // 42
إذا كنت ترغب في الحصول على كائن ثابت، فيجب عليك استخدام Object.freeze()
.
التهيئة مطلوب
يجب عليك دائما تحديد قيمة عند الإعلان عن متغير باستخدام const
.
const a; // SyntaxError: Missing initializer in const declaration
هنا مثال على الفرق بين الاثنين (الدعم بدأ للتو في Chrome):
كما ترون var j
لا يزال المتغير وجود قيمة خارج نطاق حلقة (نطاق كتلة)، ولكن let i
المتغير غير محدد خارج نطاق حلقة.
"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
console.log(j);
}
console.log(j);
console.log("let:");
for (let i = 0; i < 2; i++) {
console.log(i);
}
console.log(i);
هناك بعض الاختلافات الدقيقة - let
التركيب يتصرف أكثر مثل التركيب المتغير يحدث في أي لغات أخرى أو أقل.
على سبيل المثال، فإنها نطاقات إلى كتلة مرفقة، فلا تكون موجودة قبل إعلانها، إلخ.
ومع ذلك فإن الأمر يستحق لاحظ ذلك let
ليست سوى جزء من تطبيقات جافا سكريبت الأحدث ودرجات مختلفة من دعم المتصفح.
متغير لا يرفعlet
إرادة لا يرفع إلى النطاق بالكامل من الكتلة التي تظهر فيها. على النقيض من ذلك،var
يمكن أن يرفع على النحو التالي.{ console.log(cc); // undefined. Caused by hoisting var cc = 23; } { console.log(bb); // ReferenceError: bb is not defined let bb = 23; }
في الواقع، لكلbgi، كلاهما
var
وlet
هراء.جمع القمامة
كتلة نطاق
let
مفيد يتعلق بالإغلاق ومجموعة القمامة لاستعادة الذاكرة. انصح،function process(data) { //... } var hugeData = { .. }; process(hugeData); var btn = document.getElementById("mybutton"); btn.addEventListener( "click", function click(evt){ //.... });
ال
click
اتصال معالج لا يحتاجhugeData
متغير على الإطلاق. من الناحية النظرية، بعد ذلكprocess(..)
يعمل، هيكل البيانات الضخمhugeData
يمكن أن يكون القمامة التي تم جمعها. ومع ذلك، من الممكن أن لا يزال يتعين على بعض محركات JS الحفاظ على هذا الهيكل الضخم، منذclick
وظيفة لديها إغلاق على النطاق بأكمله.ومع ذلك، فإن نطاق الكتلة يمكن أن يجعل هذا الهيكل البيانات الضخمة إلى القمامة التي تم جمعها.
function process(data) { //... } { // anything declared inside this block can be garbage collected let hugeData = { .. }; process(hugeData); } var btn = document.getElementById("mybutton"); btn.addEventListener( "click", function click(evt){ //.... });
let
حلقاتlet
في حلقة يمكن إعادة ربطها إلى كل تكرار للحلقة، تأكد من إعادة تعيينها القيمة من نهاية التكرار الحلقة السابقة. انصح،// print '5' 5 times for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); }, 1000); }
ومع ذلك، استبدل
var
معlet
// print 1, 2, 3, 4, 5. now for (let i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); }, 1000); }
لأن
let
قم بإنشاء بيئة معجمية جديدة مع تلك الأسماء من أجل أ) تعبير "تهيئة" ب) كل تكرار (يؤدي إلى تقييم تعبير الزيادة)، مزيد من التفاصيل هنا.
الفرق الرئيسي هو مجال الفرق، في حين يترك يمكن أن تكون متاحة فقط داخل مجال أعلن، كما هو الحال في حلقة، فار يمكن الوصول إليها خارج الحلقة على سبيل المثال. من الوثائق في MDN. (أمثلة أيضا من MDN):
يترك يتيح لك الإعلان عن المتغيرات التي تقتصر على نطاق الكتلة أو العبري أو التعبير الذي يتم استخدامه. هذا على عكس فار الكلمة الأساسية، التي تحدد المتغير عالميا، أو محليا إلى وظيفة كاملة بغض النظر عن نطاق الحظر.
المتغيرات المعلنة يترك لدينا كفرط لها الكتلة التي يتم تعريفها، وكذلك في أي كتل فرعية مضمنة. في هذا الطريق، يترك يعمل كثيرا مثل فار. وبعد الفرق الرئيسي هو أن نطاق فار المتغير هو وظيفة مرفق بأكملها:
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}`
على المستوى العلوي من البرامج والوظائف، يترك, ، على عكس فار, ، لا يخلق خاصية على الكائن العالمي. علي سبيل المثال:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
عند استخدامها داخل كتلة، دعونا نحد نطاق المتغير إلى تلك الكتلة. لاحظ الفرق بين فار الذي النطاق داخل الوظيفة حيث يتم الإعلان عنه.
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
أيضا لا تنسى ميزة ECMA6، لذلك لا يتم دعمها بالكامل بعد، لذلك من الأفضل أن تراجعها دائما إلى ECMA5 باستخدام Babel ETC ... لمزيد من المعلومات حول زيارة بابل الموقع
إليك مثال إضافته إلى ما كتبه الآخرون بالفعل. لنفترض أنك تريد تقديم صفيف من الوظائف، adderFunctions
, ، حيث تأخذ كل وظيفة وسيطة رقم واحد وإرجاع مجموع الوسيطة وفهرس الوظيفة في الصفيف. محاولة توليد adderFunctions
مع حلقة باستخدام var
لن تعمل الكلمات الرئيسية على الطريقة التي قد يتوقعها شخص ما:
// An array of adder functions.
var adderFunctions = [];
for (var i = 0; i < 1000; i++) {
// We want the function at index i to add the index to its argument.
adderFunctions[i] = function(x) {
// What is i bound to here?
return x + i;
};
}
var add12 = adderFunctions[12];
// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000
// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true
العملية أعلاه لا تولد الصفيف المطلوب من الوظائف لأن i
نطاق يمتد إلى ما وراء تكرار for
كتلة تم إنشاء كل وظيفة. بدلا من ذلك، في نهاية الحلقة، i
في كل وظيفة في كل وظيفة يشير إلى i
قيمة في نهاية الحلقة (1000) لكل وظيفة مجهولة في adderFunctions
. وبعد هذا ليس ما أردناه على الإطلاق: لدينا الآن مجموعة من 1000 وظيفة مختلفة في الذاكرة مع نفس السلوك بالضبط. وإذا حدثنا لاحقا قيمة i
, ، سوف تؤثر الطفرة على كل adderFunctions
.
ومع ذلك، يمكننا أن نحاول مرة أخرى باستخدام let
الكلمة الرئيسية:
// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];
for (let i = 0; i < 1000; i++) {
// NOTE: We're using the newer arrow function syntax this time, but
// using the "function(x) { ..." syntax from the previous example
// here would not change the behavior shown.
adderFunctions[i] = x => x + i;
}
const add12 = adderFunctions[12];
// Yay! The behavior is as expected.
console.log(add12(8) === 20); // => true
// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined
هذا الوقت، i
انتعاش على كل تكرار for
حلقه. كل وظيفة تحافظ الآن على قيمة i
في وقت إنشاء الوظيفة، و adderFunctions
يتصرف كما هو متوقع.
الآن، صورة خلط السلوكيات، وربما ترى لماذا لا ينصح بمزج الأحدث let
و const
مع الأكبر سنا var
في نفس البرنامج النصي. القيام بذلك يمكن أن يؤدي إلى أن بعض رمز مربك مذهل.
const doubleAdderFunctions = [];
for (var i = 0; i < 1000; i++) {
const j = i;
doubleAdderFunctions[i] = x => x + i + j;
}
const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];
// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true
لا تدع هذا يحصل لك. استخدام linter.
ملاحظة: هذا هو مثال تعليمي يهدف إلى إظهار
var
/let
السلوك في الحلقات وإغلاق الوظائف من شأنها أن تكون سهلة أيضا. ستكون هذه طريقة رهيبة لإضافة أرقام. لكن التقنية العامة لالتقاط البيانات في إغلاق الوظائف المجهولة قد تصادف في العالم الحقيقي في سياقات أخرى. ymmv.
الفرق في مجال من المتغيرات المعلنة مع كل منها.
في الممارسة العملية، هناك عدد من العواقب المفيدة للفرق في النطاق:
let
المتغيرات مرئية فقط في أقرب مرفق منع ({ ... }
).let
المتغيرات غير صالحة للاستعمال فقط في خطوط التعليمات البرمجية التي تحدث بعد يتم الإعلان عن المتغير (على الرغم من يتم رفعها!).let
قد لا تكون المتغيرات reteclared من قبلvar
أوlet
.- عالمي
let
لا تتم إضافة المتغيرات إلى العالميةwindow
هدف. let
المتغيرات هي سهل الاستخدام مع إغلاق (أنها لا تسبب شروط السباق).
القيود التي تفرضها let
قلل من رؤية المتغيرات وزيادة احتمال أن يتم العثور على تصادمات اسم غير متوقعة مبكرا. هذا يجعل من الأسهل تتبع وسبب المتغيرات، بما في ذلك الروائي(المساعدة في استعادة الذاكرة غير المستخدمة).
بالتالي، let
من المرجح أن تسبب المتغيرات في حدوث مشاكل عند استخدامها في برامج كبيرة أو عند الجمع بين الأطر المتقدمة بشكل مستقل بطرق جديدة وغير متوقعة.
var
قد لا تزال مفيدة إذا كنت متأكدا من أنك تريد تأثير ربط واحد عند استخدام إغلاق في حلقة (# 5) أو للإعلان عن المتغيرات العالمية المرئية من الخارج في التعليمات البرمجية (# 4). استخدام var
بالنسبة للصادرات قد يتم حل محلها إذا export
يهاجر خارج المساحة عبر المنتقل وفي اللغة الأساسية.
أمثلة
1. لا فائدة خارج أقرب كتلة أرفق:سيقوم كتلة الرمز بإلقاء خطأ مرجعي لأن الاستخدام الثاني لل x
يحدث خارج الكتلة حيث يتم الإعلان عنها let
:
{
let x = 1;
}
console.log(`x is ${x}`); // ReferenceError during parsing: "x is not defined".
في المقابل، نفس المثال مع var
يعمل.
2. لا فائدة قبل الإعلان:
هذه الكتلة من الرمز سوف رمي ReferenceError
قبل أن يتم تشغيل الرمز ل x
يستخدم قبل إعلانه:
{
x = x + 1; // ReferenceError during parsing: "x is not defined".
let x;
console.log(`x is ${x}`); // Never runs.
}
في المقابل، نفس المثال مع var
يوزع ويدير دون إلقاء أي استثناءات.
3. لا reteclaration:يظهر التعليمة البرمجية التالية أن المتغير المعلن let
قد لا يتم reedeclared لاحقا:
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
4. العالم لا تعلق window
:
var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link); // OK
console.log(window.link); // undefined (GOOD!)
console.log(window.button); // OK
5. سهل الاستخدام مع الإغلاق:أعلن المتغيرات مع var
لا تعمل بشكل جيد مع الإغلاق داخل الحلقات. إليك حلقة بسيطة تنتج تسلسل القيم التي يتغيرها i
لديه في نقاط مختلفة في الوقت المناسب:
for (let i = 0; i < 5; i++) {
console.log(`i is ${i}`), 125/*ms*/);
}
على وجه التحديد، هذه النواتج:
i is 0
i is 1
i is 2
i is 3
i is 4
في جافا سكريبت، غالبا ما نستخدم المتغيرات في وقت لاحق بكثير من الوقت الذي يتم إنشاؤه. عندما نثبت ذلك عن طريق تأخير الإخراج بإغلاق مرت إليه setTimeout
:
for (let i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... الإخراج يظل دون تغيير طالما نحن عصا let
. وبعد على النقيض من ذلك، إذا كنا قد استخدمنا var i
في حين أن:
for (var i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... حلقة مخرجات بشكل غير متوقع "أنا 5" خمس مرات:
i is 5
i is 5
i is 5
i is 5
i is 5
قد تظهر الوظائف التالية الفرق:
function varTest() {
var x = 31;
if (true) {
var x = 71; // Same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // Different variable
console.log(x); // 71
}
console.log(x); // 31
}
let
مثيرة للاهتمام، لأنه يسمح لنا بفعل شيء مثل هذا:
(() => {
var count = 0;
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
مما يؤدي إلى العد [0، 7].
بينما
(() => {
var count = 0;
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
فقط التهم [0، 1].
وظيفة VS كتلة نطاق:
الفرق الرئيسي بين var
و let
هو أن المتغيرات المعلنة مع var
نكون وظيفة تحرير. وبعد في حين أن الوظائف المعلنة مع let
نكون كتلة النحافة. وبعد علي سبيل المثال:
function testVar () {
if(true) {
var foo = 'foo';
}
console.log(foo);
}
testVar();
// logs 'foo'
function testLet () {
if(true) {
let bar = 'bar';
}
console.log(bar);
}
testLet();
// reference error
// bar is scoped to the block of the if statement
المتغيرات مع var
:
عندما الوظيفة الأولى testVar
يحصل يسمى فو المتغير، معلن مع var
, ، لا يزال يمكن الوصول إليه خارج if
بيان. هذا المتغير foo
ستكون متاحة في كل مكان في نطاق testVar
وظيفة.
المتغيرات مع let
:
عندما الوظيفة الثانية testLet
يحصل يسمى شريط المتغير، معلن مع let
, ، يمكن الوصول إليها فقط داخل if
بيان. لأن المتغيرات المعلنة مع let
نكون كتلة النحافة (حيث كتلة هي الكود بين الأقواس المجعد على سبيل المثال if{}
, for{}
, function{}
).
let
المتغيرات لا تحصل على رفع:
فرق آخر بين var
و let
هو المتغيرات مع إعلان مع let
لا تحصل على رفع. وبعد مثال هو أفضل طريقة لتوضيح هذا السلوك:
المتغيرات مع let
لا الحصول على رفع
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
المتغيرات مع var
فعل الحصول على رفع
console.log(varVar);
var varVar = 10;
// logs undefined, the variable gets hoisted
عالمي let
لا تعلق window
:
متغير معلن مع let
في النطاق العالمي (الذي يعد رمز ليس في وظيفة) لا يضاف كخاصية على العموم window
هدف. على سبيل المثال (هذا الرمز موجود في نطاق عالمي):
var bar = 5;
let foo = 10;
console.log(bar); // logs 5
console.log(foo); // logs 10
console.log(window.bar);
// logs 5, variable added to window object
console.log(window.foo);
// logs undefined, variable not added to window object
عندما يجب
let
أن تستخدم أكثرvar
?
يستخدم let
على var
كلما استطعت لأنه ببساطة تحرير أكثر تحديدا. هذا يقلل من تعارضات التسمية المحتملة التي يمكن أن تحدث عند التعامل مع عدد كبير من المتغيرات. var
يمكن استخدامها عندما تريد أن يكون متغير عالمي صراحة على window
كائن (دائما النظر بعناية إذا كان هذا ضروريا حقا).
كما يبدو أنه، على الأقل في Visual Studio 2015، يتيح Duxcript 1.5، "VAR" تصريحات متعددة لنفس الاسم المتغير في كتلة، و "دع" لا.
لن يؤدي هذا إلى إنشاء خطأ في ترجمة:
var x = 1;
var x = 2;
هذا سوف:
let x = 1;
let x = 2;
var
هو النطاق العالمي (متغير الرافعة القادرة).
let
و const
هو حظر نطاق.
test.js.
{
let l = 'let';
const c = 'const';
var v = 'var';
v2 = 'var 2';
}
console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined
عند استخدام let
ال let
الكلمة الأساسية تعلق الإعلان المتغير لنطاق أي كتلة (عادة { .. }
زوج) موجود في. وبعبارة أخرى،let
اختطف ضمنيا أي نطاق كتلة لإعلانها المتغير.
let
لا يمكن الوصول إلى المتغيرات في window
كائن لأنه لا يمكن الوصول إليه عالميا.
function a(){
{ // this is the Max Scope for let variable
let x = 12;
}
console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined
عند استخدام var
var
والمتغيرات في ES5 لديها نطاقات في وظائف مما يعني أن المتغيرات صالحة داخل الوظيفة وليس خارج الوظيفة نفسها.
var
يمكن الوصول إلى المتغيرات في window
كائن لأنه لا يمكن الوصول إليه عالميا.
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
إذا كنت تريد معرفة المزيد من مواصلة القراءة أدناه
يمكن لأحد أسئلة المقابلة الأكثر شهرة على النطاق أيضا أن يكفي الاستخدام الدقيق لل let
و var
على النحو التالي؛
عند استخدام let
for (let i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 0 to 9, that is literally AWW!!!
},
100 * i);
}
هذا لأنه عند استخدام let
, بالنسبة لكل تكرار حلقة يتم استبدال المتغير ولديك نسخته الخاصة.
عند استخدام var
for (var i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 10 times 10
},
100 * i);
}
هذا لأنه عند استخدام var
, ، لكل تكرار حلقة يتم استبدال المتغير وشروع مشترك.
إذا قرأت المواصفات بشكل صحيح let
لحسن الحظ يمكن أيضا أن تكون رافدة لتجنب استدعاء الذات وظائف تستخدم لمحاكاة الأعضاء الخاصة فقط - إن نمط التصميم الشعبي الذي يقلل من قابلية القراءة من التعليمات البرمجية، يعقد تصحيح الأخطاء، لا يضيف أي حماية شفرة حقيقية أو فائدة أخرى - باستثناء رغبة شخص ما بحيث تلبي رغبة شخص ما في الدلالات، لذلك توقف عن استخدامها. /تبجح
var SomeConstructor;
{
let privateScope = {};
SomeConstructor = function SomeConstructor () {
this.someProperty = "foo";
privateScope.hiddenProperty = "bar";
}
SomeConstructor.prototype.showPublic = function () {
console.log(this.someProperty); // foo
}
SomeConstructor.prototype.showPrivate = function () {
console.log(privateScope.hiddenProperty); // bar
}
}
var myInstance = new SomeConstructor();
myInstance.showPublic();
myInstance.showPrivate();
console.log(privateScope.hiddenProperty); // error
يرى 'محاكاة الواجهات الخاصة'
بعض الاختراقات مع let
:
1.
let statistics = [16, 170, 10];
let [age, height, grade] = statistics;
console.log(height)
2.
let x = 120,
y = 12;
[x, y] = [y, x];
console.log(`x: ${x} y: ${y}`);
3.
let node = {
type: "Identifier",
name: "foo"
};
let { type, name, value } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined
let node = {
type: "Identifier"
};
let { type: localType, name: localName = "bar" } = node;
console.log(localType); // "Identifier"
console.log(localName); // "bar"
getter و setter مع let
:
let jar = {
numberOfCookies: 10,
get cookies() {
return this.numberOfCookies;
},
set cookies(value) {
this.numberOfCookies = value;
}
};
console.log(jar.cookies)
jar.cookies = 7;
console.log(jar.cookies)
دع vs فار. انها كل شيء عن مجال.
متغيرات فار ويمكن الوصول إليها بشكل أساسي في كل مكان، في حين اسمحوا المتغيرات ليست عالمية والوجود فقط حتى يقتلهم قوس إغلاق.
انظر مثالي أدناه، ولاحظ كيف يعمل المتغير الأسد (Let) بشكل مختلف في وحدة التحكمتين. يصبح خارج نطاقه في وحدة التحكم 2nd.log.
var cat = "cat";
let dog = "dog";
var animals = () => {
var giraffe = "giraffe";
let lion = "lion";
console.log(cat); //will print 'cat'.
console.log(dog); //will print 'dog', because dog was declared outside this function (like var cat).
console.log(giraffe); //will print 'giraffe'.
console.log(lion); //will print 'lion', as lion is within scope.
}
console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.
دع جزءا من ES6. ستفسر هذه الوظائف الفرق بطريقة سهلة.
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
في السابق كان هناك نطاطان فقط في جافا سكريبت، أي وظيفية وعالمية. مع 'let
أدخلت الآن جافا سكريبت الآن block-level
المتغيرات.
للحصول على فهم كامل للكلمة الرئيسية "LET"، ES6: "دعونا" الكلمة الرئيسية لإعلان المتغير في جافا سكريبت سوف يساعد.
الآن أعتقد أن هناك تحديدا أفضل من المتغيرات إلى كتلة من العبارات التي تستخدم let
:
function printnums()
{
// i is not accessible here
for(let i = 0; i <10; i+=)
{
console.log(i);
}
// i is not accessible here
// j is accessible here
for(var j = 0; j <10; j++)
{
console.log(j);
}
// j is accessible here
}
أعتقد أن الناس سيبدأون في استخدام دعونا هنا بعد ذلك، سيكون لديهم نطق مماثل في جافا سكريبت مثل لغات أخرى، جافا، ج #، إلخ.
الأشخاص الذين ليس لديهم فهم واضح حول التراجع في جافا سكريبت يستخدمون خطأ في وقت سابق.
الرفع غير مدعوم باستخدام let
.
مع إزالتها أخطاء النهج الموجودة في JavaScript.
تشير إلى ES6 في العمق: دعونا لفهمها بشكل أفضل.
تحدد هذه المقالة بوضوح الفرق بين فار، واسمحوا
const
هي إشارة لن يتم إعادة تعيين المعرف.
let
, ، هي إشارة إلى إعادة تعيين المتغير، مثل العداد في حلقة، أو مبادلة قيمة في الخوارزمية. كما أنه يشير إلى أن المتغير سيتم استخدامه فقط في الكتلة التي يحددها، والتي ليست دائما وظيفة تحتوي كاملة على الوظيفة.
var
هو الآن أضعف إشارة متاحة عند تحديد متغير في JavaScript. قد يتم إعادة تعيين المتغير أو لا يمكن إعادة تعيين المتغير أو قد لا يتم استخدامه أو لا يجوز استخدامه لوظيفة بأكمله، أو فقط لغرض كتلة أو حلقة.
https://medium.com/javascript-scene/javascript-es6-var-lett-or-ba58b8b8de75#.esmkpbg9b.
كما ذكر أعلاه:
الفرق هو التركيب.
var
يتم استبدالها إلى الأقرب وظيفة كتلة وlet
يتم استبدالها إلى أقرب كتلة مرفقة, ، والتي يمكن أن تكون أصغر من كتلة وظيفة. كلاهما عالميا إذا كان خارج أي block.lets انظر مثالا:
مثال 1:
في كلا المثالين لدي وظيفة myfunc
. myfunc
يحتوي على متغير myvar
يساوي 10. في المثال الأول الذي أتحقق منه إذا myvar
يساوي 10 (myvar==10
). إذا كانت الإجابة بنعم، فأنا أعلن Agian متغير myvar
(الآن لدي متغيرات Myvar) باستخدام var
الكلمة الأساسية وتعيينها قيمة جديدة (20). في السطر التالي، قمت بطباعة قيمتها على وحدة التحكم الخاصة بي. بعد الكتلة الشرطية أنا طباعة قيمة myvar
على وحدة التحكم الخاصة بي. إذا نظرت إلى إخراج myfunc
, myvar
لديه قيمة تساوي 20.
مثال:في المثال الثاني بدلا من استخدام var
الكلمة الرئيسية في بلدي كتلة الشرطية أعلن myvar
استخدام let
الكلمة الرئيسية. الآن عندما أسمي myfunc
أحصل على مخرجتين مختلفتين: myvar=20
و myvar=10
.
وبالتالي فإن الفرق بسيط للغاية، أي نطاقه.
نلقي نظرة على هذه الصورة، لقد أنشأت مثالا بسيطا للغاية لمظاهرة const
و let
المتغيرات. كما ترون، عند محاولة التغيير const
متغير، سوف تحصل على الخطأ (محاولة تجاوز "الاسم" وهو ثابت ")، ولكن إلقاء نظرة على let
عامل...
أولا نعلن let age = 33
, ، وبعد تعيين بعض القيمة الأخرى age = 34;
, ، وهذا ما يرام، لا يمكننا الحصول على أي أخطاء عندما نحاول التغيير let
عامل
أعتقد أن المصطلحات ومعظم الأمثلة هي ساحقة بعض الشيء، والمسألة الرئيسية التي كان لديها شخصيا مع الفرق هو فهم ما هو "كتلة". في مرحلة ما أدركت، ستكون كتلة أي قوسين مجعدين باستثناء IF
بيان. قوس فتح {
من وظيفة أو حلقة ستحدد كتلة جديدة، أي شيء محدد let
داخله، لن يكون متاحا بعد إغلاق القوس }
من نفس الشيء (وظيفة أو حلقة)؛ مع وضع ذلك في الاعتبار، كان من الأسهل فهم:
let msg = "Hello World";
function doWork() { // msg will be available since it was defined above this opening bracket!
let friends = 0;
console.log(msg);
// with VAR though:
for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
console.log(iCount2);
for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
console.log(iCount1);
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);
عندما أحاول حاليا الحصول على فهم متعمق لجافا سكريبت، سأشارك أبحاثي القصيرة التي تحتوي على بعض القطع الكبيرة التي تمت مناقشتها بالفعل بالإضافة إلى بعض التفاصيل الأخرى في منظور مختلف.
فهم الفرق بين فار و يترك يمكن أن يكون أسهل إذا نفهم الفرق بين وظيفة و كتلة النطاق.
دعونا نفكر في الحالات التالية:
(function timer() {
for(var i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack VariableEnvironment //one VariablEnvironment for timer();
// when the timer is out - the value will be the same value for each call
5. [setTimeout, i] [i=5]
4. [setTimeout, i]
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]
####################
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i] [i=5]
LexicalEnvironment
4. [setTimeout, i] [i=4]
LexicalEnvironment
3. [setTimeout, i] [i=3]
LexicalEnvironment
2. [setTimeout, i] [i=2]
LexicalEnvironment
1. [setTimeout, i] [i=1]
LexicalEnvironment
0. [setTimeout, i] [i=0]
متي timer()
ودعا an. overmentcontext. يتم إنشاؤها والتي سوف تحتوي على كل من VariabilenVironment. وكل lexicalenvironments. المقابلة لكل تكرار.
وأبسط مثال
نطاق وظيفة
function test() {
for(var z = 0; z < 69; z++) {
//todo
}
//z is visible outside the loop
}
كتلة النطاق
function test() {
for(let z = 0; z < 69; z++) {
//todo
}
//z is not defined :(
}
أريد ربط هذه الكلمات الرئيسية في سياق التنفيذ، لأن سياق التنفيذ مهم في كل هذا. يحتوي سياق التنفيذ على مرحلتين: مرحلة الإبداع ومرحلة التنفيذ. بالإضافة إلى ذلك، يحتوي كل سياق تنفيذ بيئة متغيرة وبيئة خارجية (بيئتها المعجمية).
خلال مرحلة الإبداع من سياق التنفيذ، لن تظل Var، واسمحوا وتكون COSS تخزين متغيرها في الذاكرة مع قيمة غير محددة في البيئة المتغيرة في سياق التنفيذ المحدد. الفرق في مرحلة التنفيذ. إذا كنت تستخدم مرجع متغير محدد مع VAR قبل تعيين قيمة، فسيكون فقط غير محدد. لن يتم رفع أي استثناء.
ومع ذلك، لا يمكنك الرجوع إلى المتغير المعلن مع السماح أو CONST حتى يتم الإعلان عنه. إذا حاولت استخدامه قبل إعلانه، فسيتم رفع استثناء خلال مرحلة التنفيذ من سياق التنفيذ. الآن ستظل المتغير في الذاكرة، مجاملة مرحلة الإبداع من سياق التنفيذ، ولكن المحرك لن يسمح لك باستخدامه:
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
مع وجود متغير محدد مع Var، إذا كان المحرك لا يمكن أن يجد المتغير في بيئة متغير سياق التنفيذ الحالي، فسوف يرتفع سلسلة النطاق (البيئة الخارجية) وتحقق من بيئة البيئة المتغيرة للبيئة الخارجية للمتغير. إذا لم يتمكن من العثور عليه هناك، فسوف يستمر البحث في سلسلة النطاق. هذا ليس هو الحال مع السماح والموزعة.
الميزة الثانية للسماح لها أنها تقدم نطاق كتلة. يتم تعريف الكتل بواسطة الأقواس المجعد. أمثلة تشمل كتل الوظائف، إذا كتل، كتل، إلخ. عندما تعلن متغيرا مع السماح داخل كتلة، فإن المتغير متاح فقط داخل الكتلة فقط. في الواقع، في كل مرة يتم تشغيل الكتلة، مثل داخل حلقة، سينشئ متغير جديد في الذاكرة.
تقدم ES6 أيضا الكلمة الأساسية CONTS لإعلان المتغيرات. const هو أيضا كتلة النحل. الفرق بين السماح والعامس هو أن المتغيرات CONTS يجب إعلانها باستخدام تهيئة، أو سيتم إنشاء خطأ.
وأخيرا، عندما يتعلق الأمر بسياق التنفيذ، سيتم إرفاق المتغيرات المحددة باستخدام VAR إلى الكائن "هذا". في سياق التنفيذ العالمي، سيكون هذا كائن النافذة في المتصفحات. هذا ليس هو الحال بالنسبة للسماح أو CONST.