var let const 定義方式比較

Node.js var let const 定義方式比較

定義方式

定義方式 說明
const 不會被重新指定值的變數 e.g. const PI = 3.14159
let 在指定區塊才有效用的變數
var 在整個函式或區塊中皆可以取用

var

在 function 外的 var 可以被 function 取用

var my_name = "KJ";
function sayMyName() {
    // KJ
    console.log(my_name);
}

sayMyName();

在 function 內的 var 無法被外面取用

function defineMyName() {
    var my_name = "KJ";
}
//  my_name is not defined
console.log(my_name);

同樣變數 可以 重新定義

不建議使用,會導致變數定義混亂

var my_name = "Kay";
var my_name = "Jay";

// Jay
console.log(my_name);

let

在 function 內的 let 無法被外面取用

function defineMyName() {
    let my_name = "KJ";
}
//  my_name is not defined
console.log(my_name);

內部變數與外部變數名稱一樣不會互相污染

let my_name = "Kay";
if (true) {
    let my_name = "Jay";
    // Jay
    console.log(my_name);
}
// Kay
console.log(my_name);

同樣變數無法重新定義

let my_name = "Kay";
// SyntaxError: Identifier 'my_name' has already been declared
let my_name = "Jay";

const

常數無法重新被賦予值

const my_name = "Kay";
my_name = "Jay";
// TypeError: Assignment to constant variable.
console.log(my_name);
const employee = {
    name: "Kay",
    age: 18
};

// TypeError: Assignment to constant variable.
employee = {
    name: "Jay",
    age: 17
}

常數物件數值可以被異動

const employee = {
    name: "Kay",
    age: 18
};
// { name: 'Kay', age: 18 }
console.log(employee);

// 變更常數物件數值
employee.name = "Jay";
employee.age = 17;

// { name: 'Jay', age: 17 }
console.log(employee);

同樣變數無法重新定義

const my_name = "Kay";
// SyntaxError: Identifier 'my_name' has already been declared
const my_name = "Jay";

必須賦予初始值

// SyntaxError: Missing initializer in const declaration
const my_name;

var vs let vs const

類型 var let const
scope 範圍 global 全域 block 區塊 block 區塊
re-declared 重新定義 X X
update 更新 X
賦予初始值 不限制 不限制 必要
function run() {
    var my_name = "Kay";
    let my_age = 19;

    // Kay 19
    console.log(my_name, my_age);

    {
        var their_name = "Jay"
        let their_age = 17;
        // Jay 17
        console.log(their_name, their_age);
    }

    // Jay
    console.log(their_name);
    // ReferenceError: their_age is not defined
    console.log(their_age);
}

run();

賦予值異動

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 var value: " + i);
    };
}
for (var j = 0; j < 3; j++) {
    // and now let's run each one to see
    funcs[j]();
}
// My var value: 3
// My var value: 3
// My var value: 3

var i 最後已經變成 3 了,所以最後會印出三行 My var value: 3

var funcs = [];
// let's create 3 functions
for (let i = 0; i < 3; i++) {
    // and store them in funcs
    funcs[i] = function() {
        // each should log its value.
        console.log("My let value: " + i);
    };
}
for (var j = 0; j < 3; j++) {
    // and now let's run each one to see
    funcs[j]();
}
// My let value: 0
// My let value: 1
// My let value: 2

let i 只作用在 for 的 scope 中,所以最後會分別印他們當初傳進去的值

經典問題

Scope 數值

var a = 1;
var b = 1;
var e = 1;

function test(a) {
    // undefined
    console.log(a);
    var b = 2;
    // 2
    console.log(b);

    if (true) {
        let c = 5;
        var d = 6;
        const e = 7;
    }

    // undefined
    console.log(c);
    // 6
    console.log(d);
    // 1
    console.log(e);
}

test();

i 執行到後面已經變成 6 了,然後接下來會執行 setTimeout 的程式,印出來的變數都是 6

for(var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i)
    }, 0)
}
// 6
// 6
// 6
// 6
// 6

解決方式

使用 let 將變數定在 block 中,區塊中會保留原本的數值

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

使用 IIFE 方式,事先將要處理的變數傳入會立即執行的函式,將變數包在 block 中,這樣執行的時候也可以拿到正確的值

// 方法 2 IIFE
for(var i = 1; i <= 5; i++) {
    (function (x) {
        setTimeout(function() { console.log(x) }, 0)
    })(i)
}

// 1
// 2
// 3
// 4
// 5

參考資料