سؤال

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 على الاطلاق:

Support table


كيفية تتبع دعم المتصفح

للحصول على نظرة عامة حديثة على المتصفحات التي تدعم 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):
enter image description here

كما ترون 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.

الفرق في مجال من المتغيرات المعلنة مع كل منها.

في الممارسة العملية، هناك عدد من العواقب المفيدة للفرق في النطاق:

  1. let المتغيرات مرئية فقط في أقرب مرفق منع ({ ... }).
  2. let المتغيرات غير صالحة للاستعمال فقط في خطوط التعليمات البرمجية التي تحدث بعد يتم الإعلان عن المتغير (على الرغم من يتم رفعها!).
  3. let قد لا تكون المتغيرات reteclared من قبل var أو let.
  4. عالمي let لا تتم إضافة المتغيرات إلى العالمية window هدف.
  5. 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.

let keyword

مثال:في المثال الثاني بدلا من استخدام var الكلمة الرئيسية في بلدي كتلة الشرطية أعلن myvar استخدام let الكلمة الرئيسية. الآن عندما أسمي myfunc أحصل على مخرجتين مختلفتين: myvar=20 و myvar=10.

وبالتالي فإن الفرق بسيط للغاية، أي نطاقه.

enter image description here

نلقي نظرة على هذه الصورة، لقد أنشأت مثالا بسيطا للغاية لمظاهرة 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top