js 中 var let const 解析

一.var
含義:變量聲明,無論發(fā)生在何處,都在執(zhí)行任何代碼之前進行處理。

注意:
1.聲明變量的作用域限制在其聲明位置的上下文中,而非聲明變量總是全局的

function x() {  
  y = 1;  // 在嚴格模式(strict mode)下會拋出ReferenceError異常。  
  var z = 2;
}
x();
console.log(y); // 打印"1" 。
console.log(z); // 拋出ReferenceError: z未在x外部聲明。

2. 聲明變量在任何代碼執(zhí)行前創(chuàng)建,而非聲明變量只有在執(zhí)行賦值操作的時候才會被創(chuàng)建。

console.log(a);   // 拋出ReferenceError。console.log('still going...'); // 永不執(zhí)行。

var a;
console.log(a);  // 打印"undefined"或""(不同瀏覽器實現(xiàn)不同)。
console.log('still going...'); // 打印"still going..."

3. 聲明變量是它所在上下文環(huán)境的不可配置屬性,非聲明變量是可配置的(如非聲明變量可以被刪除)。

var a = 1;b = 2;
delete this.a; // 在嚴格模式(strict mode)下拋出TypeError,其他情況下執(zhí)行失敗并無任何提示。
delete this.b;
console.log(a, b); // 拋出ReferenceError。// 'b'屬性已經被刪除。

建議:建議始終聲明變量,無論它們是在函數(shù)還是全局作用域內。

參考例子:
變量提升:

 var a = 2;
 function foo() {
     console.log(a); //undefined
     var a = 10;
     console.log(a); //10
 }
 foo();

留意其中的順序:

var x = y, y = 'A';
console.log(x + y); // undefinedA

在這里,x和y在代碼執(zhí)行前就已經創(chuàng)建了,而賦值操作發(fā)生在創(chuàng)建之后。當"x = y"執(zhí)行時,y已經存在,所以不拋出ReferenceError,并且它的值是'undefined'。所以x被賦予 undefined 值。然后,y被賦予'A'。于是在執(zhí)行完第一行之后,x === undefined && y === 'A'才出現(xiàn)了這樣的結果。

多個變量的初始化

var x = 0;
function f(){  
  var x = y = 1; // x在函數(shù)內部聲明,y不是!
}
f();
console.log(x, y); // 0, 1// x是全局變量。// y是隱式聲明的全局變量。

隱式全局變量和外部函數(shù)作用域

看起來像是隱式全局作用域的變量也有可能是其外部函數(shù)變量的引用。

var x = 0;  // x是全局變量,并且賦值為0。
console.log(typeof z); // undefined,因為z還不存在。
function a() { // 當a被調用時,  
  var y = 2;  // y被聲明成函數(shù)a作用域的變量,然后賦值成2。        
  console.log(x, y);  // 0 2  
  function b() {      // 當b被調用時,    
    x = 3;  // 全局變量x被賦值為3,不生成全局變量。   
    y = 4;  // 已存在的外部函數(shù)的y變量被賦值為4,不生成新的全局變量。      z = 5;  // 創(chuàng)建新的全局變量z,并且給z賦值為5。  
  }        // (在嚴格模式下(strict mode)拋出ReferenceError)  b();    // 調用b時創(chuàng)建了全局變量z。  
  console.log(x, y, z);  // 3 4 5
}
a();                  // 調用a時同時調用了b。
console.log(x, z);    // 3 5
console.log(typeof y); // undefined,因為y是a函數(shù)的本地(local)變量。

二.let

含義:允許你聲明一個作用域被限制在塊級中的變量、語句或者表達式。與var關鍵字不同的是,它聲明的變量只能是全局或者整個函數(shù)塊的。

注意:let聲明的變量只在其聲明的塊或子塊中可用,這一點,與var相似。二者之間最主要的區(qū)別在于var聲明的變量的作用域是整個封閉函數(shù)。

function varTest() {  
  var x = 1;  
  if (true) {    
    var x = 2;  // 同樣的變量!   
    console.log(x);  // 2 
   }  
  console.log(x);  // 2
}
function letTest() {  
  let x = 1;  
  if (true) {   
    let x = 2;  // 不同的變量    
    console.log(x);  // 2  
 } 
 console.log(x);  // 1
}

作用:

1.簡化內部函數(shù)代碼

當用到內部函數(shù)的時候,let會讓你的代碼更加簡潔。

var list = document.getElementById('list');
for (let i = 1; i <= 5; i++) {  
  let item = document.createElement('li');         
  item.appendChild(document.createTextNode('Item ' + i)); 
  item.onclick = function(ev) { 
    console.log('Item ' + i + ' is clicked.');  };
  list.appendChild(item);
}// to achieve the same effect with 'var'// you have to create a different context// using a closure to preserve the value
for (var i = 1; i <= 5; i++) {  
  var item = document.createElement('li'); 
  item.appendChild(document.createTextNode('Item ' + i)); 
  (function(i){    
  item.onclick = function(ev) {  
    console.log('Item ' + i + ' is clicked.');   
  };  
})(i);  
list.appendChild(item);
}

以上示例的工作原理是因為(匿名)內部函數(shù)的五個實例引用了變量i的五個不同實例。注意,如果你將let替換為var,則它將無法正常工作,因為所有內部函數(shù)都將返回相同的i:6的最終值。此外,我們可以通過將創(chuàng)建新元素的代碼移動到每個循環(huán)的作用域來保持循環(huán)更清晰。

每次循環(huán)的i其實都是一個新的變量 注意for循環(huán)的作用域(父子作用域)

在程序或者函數(shù)的頂層,let并不會像var一樣在全局對象上創(chuàng)造一個屬性,比如:

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

2.模仿私有接口

在處理構造函數(shù)的時候,可以通過let綁定來共享一個或多個私有成員,而不使用閉包:

var Thing;
{  
   let privateScope = new WeakMap();  
   let counter = 0; 
   Thing = function() {    
   this.someProperty = 'foo';   
    privateScope.set(this, {
    hidden: ++counter,    
   });  
  };  
  Thing.prototype.showPublic = function() {    
    return this.someProperty;  
  };  
  Thing.prototype.showPrivate = function() {    
    return privateScope.get(this).hidden;  
  };
}
console.log(typeof privateScope);// "undefined"
var thing = new Thing();
console.log(thing);// Thing {someProperty: "foo"}
thing.showPublic();// "foo"
thing.showPrivate();// 1

暫存死區(qū)的錯誤

在相同的函數(shù)或塊作用域內重新聲明同一個變量會引發(fā)SyntaxError。

if (x) {  
  let foo;
  let foo; // TypeError thrown.
}

在 ECMAScript 2015 中,let綁定不受變量提升的約束,這意味著let聲明不會被提升到當前執(zhí)行上下文的頂部。在塊中的變量初始化之前,引用它將會導致ReferenceError(而使用 var 聲明變量則恰恰相反,該變量的值是 undefined )。該變量處于從塊開始到初始化處理的“暫存死區(qū)”。

function do_something() {
  console.log(bar); // undefined
  console.log(foo); // ReferenceError: foo is not defined
  var bar = 1;
  let foo = 2;
}

在switch聲明中你可能會遇到這樣的錯誤,因為它只有一個塊.

switch (x) {
  case 0:
    let foo;
    break;   
  case 1:
    let foo; // TypeError for redeclaration.
    break;
}

但是,重要的是要指出嵌套在case子句內的塊將創(chuàng)建一個新的塊作用域的詞法環(huán)境,這不會產生上面顯示的重新聲明錯誤。

let x = 1;
switch(x) {
  case 0: {
    let foo;
    break;
  } 
  case 1: {
    let foo;
    break;  }
}

與詞法作用域結合的暫存死區(qū)

由于詞法作用域,表達式(foo + 55)內的標識符“foo”會解析為if塊的foo,而不是覆蓋值為33的foo。在這一行中,if塊的“foo”已經在詞法環(huán)境中創(chuàng)建,但尚未達到(并終止)其初始化(這是語句本身的一部分):它仍處于暫存死區(qū)。

function test(){
  var foo = 33;
  if (true) {
      let foo = (foo + 55); // ReferenceError
  }
}
test();

這種現(xiàn)象可能會使您陷入以下情況。指令let n of n.a已經在for循環(huán)塊的私有范圍內,因此標識符“n.a”被解析為位于指令本身的第一部分(“l(fā)et n”)中的'n'對象的屬性'a' ,由于尚未達成和終止其聲明,因此仍處于暫存死區(qū)。

function go(n) {
  // n here is defined!
  console.log(n); // Object {a: [1,2,3]}
  for (let n of n.a) { // ReferenceError    console.log(n);
  }
}
go({a: [1, 2, 3]});

一.const

含義:此聲明創(chuàng)建一個常量,其作用域可以是全局或本地聲明的塊。 與var變量不同,全局常量不會變?yōu)榇翱趯ο蟮膶傩浴P枰粋€常數(shù)的初始化器。

注意:

// 注意: 常量在聲明的時候可以使用大小寫,但通常情況下全部用大寫字母。

// 定義常量MY_FAV并賦值7
const MY_FAV = 7;
// 報錯
MY_FAV = 20;
// 輸出 7
console.log("my favorite number is: " + MY_FAV);
// 嘗試重新聲明會報錯 
const MY_FAV = 20;
//  MY_FAV 保留給上面的常量,這個操作會失敗
var MY_FAV = 20; 
// 也會報錯
let MY_FAV = 20;
// 注意塊范圍的性質很重要
if (MY_FAV === 7) {
// 沒問題,并且創(chuàng)建了一個塊作用域變量 
MY_FAV    
// (works equally well with let to declare a block scoped non const variable)    
let MY_FAV = 20;    
// MY_FAV 現(xiàn)在為 20    
console.log('my favorite number is ' + MY_FAV);    
// 這被提升到全局上下文并引發(fā)錯誤    
var MY_FAV = 20;}
// MY_FAV 依舊為7
console.log("my favorite number is " + MY_FAV);
// 常量要求一個初始值
const FOO; 
// SyntaxError: missing = in const declaration
// 常量可以定義成對象
const MY_OBJECT = {"key": "value"};
// 重寫對象和上面一樣會失敗
MY_OBJECT = {"OTHER_KEY": "value"};
// 對象屬性并不在保護的范圍內,下面這個聲明會成功執(zhí)行
MY_OBJECT.key = "otherValue";// 也可以用來定義數(shù)組
const MY_ARRAY = [];
// It's possible to push items into the array
// 可以向數(shù)組填充數(shù)據(jù)
MY_ARRAY.push('A'); 
// ["A"]
// 但是,將一個新數(shù)組賦給變量會引發(fā)錯誤MY_ARRAY = ['B']
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容