欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javascript中的var、let、const最佳實踐

 更新時間:2025年07月01日 09:36:09   作者:前端風(fēng)云志  
var,let and const是JavaScript中三種定義變量的方式,它們之間有什么區(qū)別呢?下面給大家介紹javascript中的var、let、const最佳實踐,感興趣的朋友一起看看吧

簡介

varlet and const是JavaScript中三種定義變量的方式,它們之間有什么區(qū)別呢?這是前端面試中常見的一道題,今天我們來一文說透它。
letconst區(qū)別不大,主要是const聲明的是常量,不可修改,而let聲明的變量是可修改的。所以我們重點放在varlet上。

變量初始化

聲明變量的同時為其賦值叫做初始化。

  • varlet聲明的變量都可以不賦值,此時變量的值為undefined。
  • const聲明的變量必須賦值,否則會報錯。
// `var`和`let`聲明的變量可以不賦值,此時變量的值為`undefined`。
var num; // num的值是undefined
num = 1; // num的值是1
let str; // str的值是undefined
str = 'hello'; // str的值是'hello' 
// `const`聲明的變量必須賦值,否則會報錯。
const a; // SyntaxError: Missing initializer in const declaration

變量提升 - Hoisting

Hoisting這個詞中文譯為提升,就是將變量的聲明提升到其作用域的頂部,注意提升的是聲明,而不是賦值。

  • var聲明的變量會被提升至其作用域頂部。
  • letconst聲明的變量不會被提升。(注意這個說法有爭議,詳見MDN
  • 提升只針對變量聲明,不包括賦值。

如果var是在全局作用域聲明的,那么它會被提升到全局作用域的頂部。

console.log(name); // undefined
var name = 'Philip';

以上代碼等價于:

var name; // `var`聲明的變量會被提升到其作用域頂部。
console.log(name); // undefined
name = 'Philip';

如果var是在函數(shù)作用域聲明的,那么它會被提升到函數(shù)作用域的頂部。

function printName() {
  console.log(name); // undefined
  var name = 'Philip';
}
printName();

以上代碼等價于:

function printName() {
  var name; // `var`聲明的變量會被提升到其作用域頂部。
  console.log(name); // undefined
  name = 'Philip';
}
printName();

let和const聲明的變量不會被提升。

對于letconst,它們不會被提升,所以下面代碼會報錯。

console.log(num); // ReferenceError: Cannot access 'num' before initialization
const num = 1;

前面說過,關(guān)于letconst是否被提升有爭議。

  • 一種說法是letconst不會被提升,所以在聲明之前訪問會報錯。
  • 另一種說法是letconst會被提升,但是在聲明之前訪問會拋出Temporal Dead Zone錯誤。

比如下面的代碼:

const x = 1;
{
  console.log(x); // ReferenceError: Cannot access 'x' before initialization
  const x = 2;
}

這段代碼會報錯,但是如果我們把{}內(nèi)的const x = 2;注釋掉,那么代碼就不會報錯。如果const x = 2沒有被提升的話,那么console.log(x)應(yīng)該可以訪問到全局的const x = 1,而不會報錯。換句話說:因為const x = 2被提升了,所以console.log(x)訪問的是提升后的x,而此時x還沒有被初始化,所以報錯。

提升只針對變量聲明,不包括賦值。

下面的代碼會報錯,因為x = 1是賦值,并不是聲明,所以不會提升。(注意:如果變量聲明前沒有加varletconst,那么其實產(chǎn)生的是一個意外的全局變量。)

console.log(x); // ReferenceError: x is not defined
x = 1;

如果有同名函數(shù)和變量,那么提升后,變量位于函數(shù)之前(或者說函數(shù)會覆蓋變量)。

以下代碼中有一個同名的函數(shù)和變量。

console.log(foo); // [Function: foo], not undefined.
function foo() {
  console.log('function foo');
}
var foo = 1;

提升后代碼如下:

var foo;
function foo() {
  console.log('function foo');
}
console.log(foo);
foo = 1;

面試題

看幾道面試題,以下幾段代碼輸出什么?

  • 第一題
a = 2;
var a;
console.log(a); // 2

解決var提升的問題很簡單,就是按照提升規(guī)則將代碼重寫一下,上面的代碼等價于如下代碼,結(jié)果一目了然。

var a;
a = 2;
console.log(a); // 2
  • 第二題
var a = true;
foo();
function foo() {
  if (a) {
    var a = 10;
  }
  console.log(a);
}

只要函數(shù)內(nèi)部有var聲明的變量,那么所有全局聲明的var變量都會被忽略,以上代碼提升后等價于如下代碼(注意function也有提升),函數(shù)內(nèi)部的var永遠(yuǎn)會覆蓋全局的var。

var a = true;
function foo() {
  var a; // value of a is `undefined`
  if (a) {
    a = 10; // never executed.
  }
  console.log(a);
}
foo();
  • 第三題
function fn() {
  console.log(typeof foo);
  var foo = 'variable';
  function foo() {
    return 'function';
  }
  console.log(typeof foo);
}
fn();

還是那句話,此類題目的解法就是按照提升規(guī)則把代碼重新寫一遍,以上代碼提升后等價于如下代碼:

function fn() {
  var foo;
  function foo() {
    return 'function';
  }
  console.log(typeof foo);
  foo = 'variable';
  console.log(typeof foo);
}
fn();

所以輸出結(jié)果是functionstring。

變量的作用域

  • var聲明的變量有只兩種作用域:全局作用域和函數(shù)作用域。(沒有塊級作用域)
  • letconst聲明的變量有三種作用域:全局作用域,函數(shù)作用域和塊級作用域。
  • var聲明的全局變量會掛載到window對象上,而letconst不會。
  • letconst有臨時性死區(qū),而var沒有。

面試題

第一題

以下代碼輸出什么?

let x = 1;
{
  let x = 2;
}
console.log(x);

答案:1,因為let有塊級作用域,所以let x = 2只在{}內(nèi)有效。

第二題

以下代碼輸出什么?

var x = 1;
{
  var x = 2;
}
console.log(x);

答案:2,因為var沒有塊級作用域,所以var x = 2會覆蓋外部的var x = 1。

第三題

以下代碼輸出什么?

let name = 'zdd';
{
  console.log(name); 
  let name = 'Philip';
}

答案:ReferenceError: Cannot access 'name' before initialization。因為let有塊級作用域,所以console.log(name);訪問的是let name = 'Philip';之前的name,而此時name還沒有被初始化,處于暫時性死區(qū)中,所以報錯。

第四題

以下代碼輸出什么?

'use strict';
{
  function foo() {
    console.log('foo');
  }
}
foo();

答案:ReferenceError: foo is not defined。因為foo是在塊級作用域內(nèi)聲明的,所以在外部無法訪問。但是如果我們把'use strict';去掉,那么代碼就可以正常運行。因為在非嚴(yán)格模式下,函數(shù)聲明會被提升到全局作用域。

第五題

以下代碼輸出什么?

(() => {
  let x;
  let y;
  try {
    throw new Error();
  } catch (x) {
    x = 1;
    y = 2;
    console.log(x);
  }
  console.log(x);
  console.log(y);
})();

答案:1 undefined 2。因為catch中的x是一個新的變量,不是外部的x,所以x = 1只會改變catch中的x,而不會改變外部的x。而y = 2不是catch的參數(shù),只是在catch中賦值的,所以會改變外部的y。

暫時性死區(qū) - Temporal Dead Zone

TDZ即Temporal Dead Zone - 中文名暫時性死區(qū),是指letconst聲明的變量在其作用域開始到變量聲明之間的這段區(qū)域。在暫時性死區(qū)內(nèi)無法訪問變量,訪問會報錯。

function foo() {
  console.log(b); // ReferenceError: Cannot access 'b' before initialization
  let a = 1;
  const b = 2;
}
foo();

對于以上代碼,常量b的暫時性死區(qū)開始于函數(shù)的第一行,終止于b的聲明,而console.log(b);這句恰恰在暫時性死區(qū)內(nèi)訪問了b,所以會報錯。

面試題

以下代碼輸出什么?

function foo() {
  console.log(typeof bar);
  const bar = 1;
}
foo();

答案:
ReferenceError: Cannot access 'bar' before initialization因為console.log(typeof bar);這句在bar的暫時性死區(qū)內(nèi)訪問了bar,所以會報錯。可以看到,即使強如typeof這種幾乎不會報錯的操作符也無法規(guī)避暫時性死區(qū)。

如果我們把const bar = 1;去掉,那么代碼就不會報錯。typeof操作符對于沒有聲明的變量不會報錯,而是返回undefined

function foo() {
  console.log(typeof bar); // 輸出undefined
}

重新聲明- Redeclaration

  • var聲明的變量可以被重復(fù)聲明,后聲明的覆蓋先聲明的。
  • letconst聲明的變量不可以被重復(fù)聲明。

面試題

看幾道面試題,以下幾段代碼輸出什么?

  • 第一題
var a = 1;
function foo() {
  var a = 2;
  {
    var a = 3;
    console.log(a);
  }
  console.log(a);
}
foo();
console.log(a);

答案:3 3 1, 這個題主要考察兩個知識點:

  • var聲明的變量沒有塊級作用域。
  • var聲明的變量可以被重復(fù)聲明,后聲明的會覆蓋先聲明的。
    所以var a = 3會覆蓋外部的var a = 2,但是var a = 2不會覆蓋最外面的var a = 1。因為var有函數(shù)作用域。

以上代碼提升后等價于如下代碼:

var a;
a = 1;
function foo() {
  var a;
  var a; // redeclaration
  a = 2;
  {
    a = 3;
    console.log(a);
  }
  console.log(a);
}
foo();
console.log(a);

注意:面試題中凡事用{}包裹var的都是障眼法,var沒有塊級作用域。

第二題

這道題比較簡單,考察的是let的塊級作用域,代碼輸出2, 1。因為let有塊級作用域。let a = 2只在{}內(nèi)有效。

function foo() {
  let a = 1;
  {
    let a = 2;
    console.log(a);
  }
  console.log(a);
}
foo();

意外的全局變量

如果我們聲明變量的時候忘記了寫varlet或者const,那么這個變量就是所謂的Accidental Global Variables,意思是意外的全局變量。

function f1() {
  b = 2; // accident global variable
}
f1();
console.log(b); // 2

面試題

以下代碼輸出什么?

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  })
}

答案:3 3 3
因為var沒有塊級作用域,所以setTimeout內(nèi)的i都是指向同一個i,而setTimeout是異步的,其回調(diào)函數(shù)代碼需要先進(jìn)入宏任務(wù)隊列,待for循環(huán)結(jié)束后才能執(zhí)行,此時i已經(jīng)是3了。關(guān)于這道題的詳細(xì)解釋,請看這篇。

最佳實踐

  • 如今ES6已經(jīng)普及,對于業(yè)務(wù)代碼來說,基本不需要使用var了,var目前只有JS框架或者底層工具庫才會使用。

  • 對于letconst,優(yōu)先使用const,只有在需要修改變量的情況下才使用let。

  • 經(jīng)典for循環(huán)使用let,因為循環(huán)變量會被修改。

    for (let i = 0; i < 5; i++) {
      console.log(i);
    }
  • for...infor...of使用const,因為循環(huán)變量不會被修改。

    const arr = [1, 2, 3];
    for (const item of arr) {
      console.log(item);
    }
    const obj = {a: 1, b: 2};
    for (const key in obj) {
      console.log(key);
    }

到此這篇關(guān)于javascript中的var、let、const的文章就介紹到這了,更多相關(guān)js var let const內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論