Koks yra skirtumas tarp „leisti“ ir „var“, kad kintamasis būtų rodomas javascript'e?

„ECMAScript 6“ pristato leidimo pareiškimą . Aš girdėjau, kad tai apibūdinama kaip „vietinis“ kintamasis, bet aš vis dar nežinau, kaip ji elgiasi kitaip nei var raktinis žodis.

Kokie yra skirtumai? Kada reikia naudoti var ?

3619
TM. 17 Bal 2009-04-17 23:09 '09, 11:09 val. 2009-04-17 23:09
ответ 31 atsakymų
  • 1
  • 2

Skirtumas yra teritorijos apibrėžime. var susietas su artimiausiu funkcijų bloku, ir let susietas su artimiausiu uždarymo bloku, kuris gali būti mažesnis už funkcijų bloką. Abi yra pasaulinės, jei yra už bet kurio bloko.

Be to, nepaskelbiant jų uždarymo bloke, kintamieji, kurie buvo deklaruoti su let , nėra prieinami. Kaip matote iš demonstracijos, tai bus išmesti „ReferenceError“ išimtis.

Demo :

 <pre id="results"></pre> 

Pasaulinis:

Jie yra labai panašūs, jei jie naudojami už funkcijų bloko ribų.

 console.log(window.me); // undefined console.log(window.i); // 'able' 

Funkcijos:

Jie yra identiški, kai naudojami šiame funkcijų bloke.

 function allyIlliterate() { //tuce is *not* visible out here for( let tuce = 0; tuce < 5; tuce++ ) { //tuce is only visible in here (and in the for() parentheses) //and there is a separate tuce variable for each iteration of the loop } //tuce is *not* visible out here } function byE40() { //nish *is* visible out here for( var nish = 0; nish < 5; nish++ ) { //nish is visible to the whole function } //nish *is* visible out here } 

nepaisyti:

Darant prielaidą, kad griežtas režimas, var leis jums iš naujo nustatyti tą patį kintamąjį toje pačioje srityje. Kita vertus, let :

 'use strict'; var me = 'foo'; var me = 'bar'; // No problem, `me` is replaced. 
5003
12 июля '12 в 5:53 2012-07-12 05:53 atsakymas pateikiamas „ ThinkingStiff“ liepos 12, 12 d. 5:53 val. 2012-07-12 05:53

Taip pat galite naudoti „ let kad išvengtumėte problemų. Jis susieja naują vertę ir nesaugo senosios nuorodos, kaip parodyta toliau pateiktuose pavyzdžiuose.

Demo

 for(var i = 1; i < 6; i++) { document.getElementById('my-element' + i) .addEventListener('click', function() { alert(i) }) } 

Minėtas kodas yra klasikinė problema uždarant javascript. Nuoroda į kintamąjį i yra saugoma paspaudimo tvarkyklės uždaryme, o ne faktinė vertė i .

border=0

Kiekvienas vieno paspaudimo tvarkytojas nurodys tą patį objektą, nes yra tik vienas skaitiklis, kuriame yra 6, todėl kiekvienam paspaudimui gausite šešis.

Bendras sprendimas - apgaubti anoniminę funkciją ir perduoti ją kaip argumentą. Šios problemos taip pat gali būti vengiamos naudojant let o ne var , kaip parodyta toliau pateiktame kode.

DEMO (išbandyta „Chrome“ ir „Firefox 50“)

 'use strict'; for(let i = 1; i < 6; i++) { document.getElementById('my-element' + i) .addEventListener('click', function() { alert(i) }) } 
494
27 мая '15 в 13:16 2015-05-27 13:16 atsakė Gurpreet Singh gegužės 27 d., 15 val. 15:16 2015-05-27 13:16

Čia pateikiamas leidimo raktinio žodžio paaiškinimas su keliais pavyzdžiais.

tegul labai panašus į var. Pagrindinis skirtumas yra tas, kad var kintamojo apimtis yra visa apimanti funkcija.

Ši Wikipedia lentelė rodo, kurios naršyklės palaiko „JavaScript“ 1.7.

Atminkite, kad palaikomos tik „Mozilla“ ir „Chrome“ naršyklės. IE, „Safari“ ir gal net kiti.

135
17 апр. Atsakymą pateikė Ben S balandžio 17 d 2009-04-17 23:11 '09 at 11:11 PM 2009-04-17 23:11

Koks skirtumas tarp let ir var ?

  • Kintamasis, apibrėžtas naudojant var operatorių, yra žinomas visoje funkcijoje, kurioje jis yra apibrėžtas, pradedant nuo funkcijos pradžios. (*)
  • Kintamasis, apibrėžtas naudojant let pareiškimą, yra žinomas tik bloke, kuriame jis apibrėžtas nuo jo apibrėžimo. (**)

Jei norite suprasti skirtumą, apsvarstykite šį kodą:

 // 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 

Čia matome, kad mūsų kintamasis j žinomas tik pirmajame cikle, bet ne anksčiau, o ne vėliau. Tačiau mūsų kintamasis i žinomas per visą funkciją.

Taip pat apsvarstykite, ar kintamieji, kurių plotas yra intervalas, nežinomi prieš juos paskelbiant, nes jie nėra pakelti. Taip pat neleidžiama perrašyti to paties kintamojo tame pačiame bloke esančiame regione. Dėl to kintamieji, turintys ribotą bloką, mažėja klaidų, nei pasauliniu mastu arba funkciniu požiūriu nukopijuojami kintamieji, kurie sukelia klaidų ir nesukelia klaidų daugelio deklaracijų atveju.


Ar šiandien saugu naudoti?

Kai kurie žmonės teigia, kad ateityje naudosime tik leidimo instrukcijas ir kad var instrukcijos bus pasenusios. „Javascript Guru Kyle Simpson“ parašė labai apgalvotą straipsnį, kodėl taip nėra .

Tačiau šiandien tai tikrai nėra. Tiesą sakant, mums iš tikrųjų reikia savęs paklausti, ar galime naudoti let pareiškimą. Atsakymas į šį klausimą priklauso nuo jūsų aplinkos:

  • Jei serverio pusėje ( Node.js ) rašote kodą, galite saugiai naudoti leidimą .

  • Jei rašote kliento pusę „JavaScript“ ir naudojate transpiliatorių (pvz., „ Traceur“ ), galite saugiai naudoti leidimo pareiškimą, tačiau jūsų kodas greičiausiai bus optimalus našumo požiūriu.

  • Jei rašote kliento pusę „JavaScript“ ir nenaudojate transpilerio, turite apsvarstyti naršyklės palaikymą.

Šiandien, 2018 m. Birželio 8 d., Vis dar yra keletas naršyklių, kurios nepalaiko let !

2019

23 февр. John Slegers atsakymas vasario 23 d 2016-02-23 21:35 '16 at 21:35 pm 2016-02-23 21:35

Gautame atsakyme nėra taško:

 { let a = 123; }; console.log(a); // ReferenceError: a is not defined 
103
02 июня '15 в 23:59 2015-06-02 23:59 atsakymas pateikiamas Lcf.vs 02 birželio 15 d. 23:59 2015-06-02 23:59

let

Blokuoti plotą

Kintamieji, deklaruoti naudojant raktinio žodžio raktą, yra blokuoti, o tai reiškia, kad jie galimi tik tame bloke , kuriame jie buvo paskelbti.

Viršutiniame lygyje (neveikia)

Viršutiniame lygyje deklaruojami kintamieji neleidžia sukurti pasaulinio objekto savybių.

 var globalVariable = 42; let blockScopedVariable = 43; console.log(globalVariable); // 42 console.log(blockScopedVariable); // 43 console.log(this.globalVariable); // 42 console.log(this.blockScopedVariable); // undefined 

Funkcijos viduje

Funkcijos viduje (bet ne bloke) let turėti tą pačią taikymo sritį kaip ir 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 

Vidinis blokas

Kintamieji, deklaruoti naudojant let bloką, negali būti pasiekiami už šio bloko ribų.

 { 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 

Vidiniai ciklai

Kintamieji, deklaruoti su let , gali būti nurodomi tik šioje linijoje.

 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. 

Vyriai su uždarymais

Jei naudojate „ let o ne „ var “ kilpoje, su kiekvienu iteratoriumi gaunate naują kintamąjį. Tai reiškia, kad galite saugiai naudoti kilpą kilpos viduje.

 // 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); } 

Laikina negyva zona

Dėl laikinos negyvos zonos, prieš deklaruojant juos, kuriuos galima deklaruoti, leistini kintamieji negali būti prieinami. Bandymas tai padaryti sukelia klaidą.

 console.log(noTDZ); // undefined var noTDZ = 43; console.log(hasTDZ); // ReferenceError: hasTDZ is not defined let hasTDZ = 42; 

Nėra naujo pranešimo

Jūs negalite deklaruoti to paties kintamojo kelis kartus naudojant let . Jūs taip pat negalite deklaruoti kintamojo naudodami leidimą, turintį tą patį identifikatorių, kaip ir kitą kintamąjį, kuris buvo deklaruotas su 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 labai panašus į let -bloką ir turi TDZ. Tačiau yra du skirtingi dalykai.

Nėra pakartotinio paskyrimo

Kintamasis, deklaruotas naudojant const negali būti perkeliamas.

 const a = 42; a = 43; // TypeError: Assignment to constant variable. 

Atkreipkite dėmesį, kad tai nereiškia, kad vertė yra pastovi. Jo savybės vis dar gali keistis.

 const obj = {}; obj.a = 42; console.log(obj.a); // 42 

Jei norite turėti nekintamą objektą, turite naudoti Object.freeze() .

Reikalingas iniciatorius

Jūs visada turite nurodyti vertę deklaruojant kintamąjį su const .

 const a; // SyntaxError: Missing initializer in const declaration 
55
24 нояб. Michał Perłakowski atsakymas 24 nov. 2016-11-24 01:52 '16 at 1:52 2016-11-24 01:52

Toliau pateikiamas dviejų skirtumų pavyzdys (parama tik pradėta naudoti chromui): 2019

42
06 марта '15 в 13:41 2015-03-06 13:41 atsakymą pateikė vlio20 kovo 06 '15 , 13:41 2015-03-06 13:41

Yra keletas subtilių skirtumų - let mastui taikyti daugiau, kaip ir kintamųjų apimtis, daugiau ar mažiau kitomis kalbomis.

Pavyzdžiui. Jis yra susietas su uždarymo bloku. Jie neegzistuoja iki jų paskelbimo ir pan.

Tačiau verta paminėti, kad let yra tik dalis naujų „Javascript“ diegimų ir yra įvairaus lygio.

41
18 апр. Atsakymas pateikiamas olliej 18 balandžio. 2009-04-18 00:38 '09 0:38 2009-04-18 00:38
  • Kintamasis nepadidėja

    let nepadidinti viso bloko ploto, kuriame jie rodomi. Priešingai, var gali pakilti, kaip parodyta žemiau.

     { console.log(cc); // undefined. Caused by hoisting var cc = 23; } { console.log(bb); // ReferenceError: bb is not defined let bb = 23; } 

    Tiesą sakant, Per @Bergi, Tiek var ir let .

  • Šiukšlių surinkimas

    let bloko tūris yra naudingas uždaryti ir rinkti šiukšles, kad būtų atkurta atmintis. Apsvarstykite

     function process(data) { //... } var hugeData = { .. }; process(hugeData); var btn = document.getElementById("mybutton"); btn.addEventListener( "click", function click(evt){ //.... }); 

    click tvarkytojo atšaukimui nereikia hugeData rodiklio. Teoriškai po process(..) didžiulė duomenų duomenų hugeData galėtų būti surinkta šiukšlių. Tačiau gali būti, kad kai kurie JS varikliai turi išlaikyti šią didžiulę struktūrą, nes click funkcija uždaryta visame regione.

    Tačiau blokų skalė gali padaryti šią didžiulę duomenų struktūros šiukšles.

     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 kilpa

    let į kilpą gali vėl prijungti jį prie kiekvienos kilpos iteracijos, kad ją būtų galima priskirti reikšmei nuo ankstesnės ciklo iteracijos pabaigos. Apsvarstykite

     // print '5' 5 times for (var i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); }, 1000); } 

    Tačiau pakeiskite var su let

     // print 1, 2, 3, 4, 5. now for (let i = 0; i < 5; ++i) { setTimeout(function () { console.log(i); }, 1000); } 

    Kaip let sukuriama nauja leksinė aplinka su šiais pavadinimais a) iniciatoriaus išraiškai; b) kiekviena iteracija (prieš vertinant prieaugio išraišką), išsamesnė informacija čia .

20
17 янв. atsakymas pateikiamas zangw 17 sausis 2016-01-17 18:11 '16 at 18:11 PM 2016-01-17 18:11

Pagrindinis skirtumas yra tūrio skirtumas , nors jis gali būti prieinamas tik matomumo srityje, jis deklaruojamas, nes ciklo metu var, pavyzdžiui, gali būti prieinamas ne cikle. Iš MDN dokumentacijos (pavyzdžiai taip pat iš MDN):

leidžia leisti deklaruoti kintamuosius, kurie yra riboti pagal bloką, pareiškimą arba išraišką, kurioje jis naudojamas. Tai skiriasi nuo var raktinio žodžio, kuris apibrėžia kintamąjį visame pasaulyje arba vietoje visai funkcijai, nepriklausomai nuo bloko apimties.

Kintamieji, deklaruoti su leistinais, turi bloką, kuriame jie yra apibrėžti, taip pat bet kuriuose įdėtuose pogrupiuose. Taigi, leiskite jam dirbti labai panašiai kaip var . Pagrindinis skirtumas yra tas, kad var kintamojo apimtis yra visa įtraukimo funkcija:

 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 }' 

Viršutiniame programų ir funkcijų lygmenyje, skirtingai nei var , nesukurkite pasaulinio objekto nuosavybės. Pavyzdžiui:

 var x = 'global'; let y = 'global'; console.log(this.x); // "global" console.log(this.y); // undefined 

Kai naudojamas bloko viduje, leiskite apriboti kintamojo taikymo sritį šiam blokui. Atkreipkite dėmesį į skirtumą tarp var , kurio taikymo sritis yra toje funkcijoje, kurioje ji yra deklaruota.

 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 

Be to, nepamirškite, kad tai yra ECMA6 funkcija, todėl ji dar nėra visiškai palaikoma, todėl visada geriausia perkelti ją į ECMA5 naudojant „Babel“ ir tt ... daugiau informacijos apie apsilankymą Babel svetainėje

18
22 марта '17 в 17:39 2017-03-22 17:39 atsakymą Alireza pateikė kovo 17 d. 17 val. 5:39 2017-03-03-22 17:39

Čia pateikiamas pavyzdys, kaip pridėti kitų, ką parašė. Tarkime, kad norite sukurti „ adderFunctions funkcijų masyvą, kur kiekviena funkcija užima vieną skaičių argumentą ir grąžina masyvo funkcijos argumento ir indekso sumą. Bandymas generuoti papildomas adderFunctions naudojant kilpą naudojant raktinį žodį var neveiks, kaip būtų naiviai tikimasi:

 // 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 

Pirmiau minėtas procesas nesukuria norimos funkcijų masyvo, nes regionas i viršija bloko, kuriame kiekviena funkcija buvo sukurta, iteraciją. Vietoj to, i ciklo pabaigoje, kiekviename funkcijos adderFunctions , tai reiškia i reikšmę ciklo pabaigoje (1000) kiekvienai anoniminei funkcijai adderFunctions . Tai ne tai, ko norėjome: dabar atmintyje yra 1000 skirtingų funkcijų masyvas, turintis tokį patį elgesį. Ir jei mes vėliau atnaujinsime i reikšmę, mutacija paveiks visas adderFunctions .

Tačiau mes galime bandyti dar kartą naudoti let raktinį žodį:

 // Let 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 scope doesn't extend outside the for loop. console.log(i); // => ReferenceError: i is not defined 

Šį kartą, i atkurta kiekvienoje „ for loop“ iteracijoje. Kiekviena funkcija dabar išsaugo i vertę kuriant funkciją, o adderFunctions elgiasi taip, kaip tikėtasi.

Dabar vaizdas susilieja su dviem elgesiais, ir jūs tikriausiai pamatysite, kodėl nerekomenduojama maišyti naujo let ir const su senu var toje pačioje scenarijoje. Tai gali lemti kai kuriuos efektyviai susietus kodus.

 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 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 

Neleisk, kad tai atsitiktų jums. Naudokite linter.

PASTABA. . Tai yra pavyzdys, kaip mokytis parodyti var / let elgesį kilpose ir uždarymo funkcijose, kurios taip pat bus lengvai suprantamos. Tai būtų baisus būdas pridėti skaičių. Tačiau bendras duomenų surinkimo metodas, kai uždaromos anoniminės funkcijos, gali vykti realiame pasaulyje kitose situacijose. YMMV.

13
18 авг. atsakymas pateikiamas abroze 18 rug . 2014-08-18 03:58 '14 at 3:58 2014-08-18 03:58

Skirtumas priklauso nuo kintamųjų, apie kuriuos deklaruojama, apimties .

Praktikoje yra daug naudos, kurią daro skirtumai šioje srityje:

  • let kintamiesiems matyti tik artimiausiame uždarymo bloke ( { ... } ).
  • let kintamieji naudojami tik kodų eilutėse, kurios atsiranda po to, kai deklaruojamas kintamasis (net jei jie yra pakelti !).
  • let kintamiesiems nepaisyti šių variantų arba let .
  • let pasauliniams kintamiesiems įtraukti į pasaulinį window objektą.
  • let kintamieji būtų lengvai naudojami su uždarymu (jie nesukelia rasės sąlygų ).

Leistini apribojimai sumažina kintamųjų matomumą ir padidina tikimybę, kad netikėtai susidūrimai bus surasti anksčiau. Dėl to lengviau sekti ir naudoti kintamuosius, įskaitant pasiekiamumą (padedant atkurti nepanaudotą atmintį).

Todėl let , let kintamieji greičiausiai sukels problemų, kai jie naudojami didelėse programose arba kai savarankiškai sukurtos sistemos sujungiamos naujais ir netikėtais būdais.

var может по-прежнему быть полезным, если вы уверены, что хотите использовать односвязную функцию при использовании замыкания в цикле (# 5) или для объявления внешне видимых глобальных переменных в вашем коде (# 4). Использование var для экспорта может быть вытеснено, если export мигрирует из пространства транспилера и на основной язык.

Pavyzdžiai

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. Нет переоформления: Следующий код демонстрирует, что переменная, объявленная с помощью let , не может быть повторно описана позже:

 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); }