ES6基本語法之--let命令和const命令

  • 1、let命令

  • 作用域

let命令與var基本相似橱乱,只是let所聲明的變量只在let代碼塊內(nèi)有效粱甫。

for (let i = 0; i < 10; i++) {
  // ...
}
console.log(i);
// ReferenceError: i is not defined

因為變量使用let聲明茶宵,所以在for循環(huán)內(nèi)乌庶,i變量可以被訪問,但是for循環(huán)外蛤吓,i變量就不可以訪問会傲,所以最后一行代碼會報錯。

  • 此外 淌山,for循環(huán)還有一個特別之處,就是設置循環(huán)變量的那部分是一個父作用域德绿,而循環(huán)體內(nèi)部是一個單獨的子作用域退渗。
rainbow=function () {
    for (let i=1;i<3;i++){
        let i='abc';
        console.log(i);
    }
};
rainbow();

上面代碼正確運行会油,輸出了3次abc翻翩。這表明函數(shù)內(nèi)部的變量i與循環(huán)變量i不在同一個作用域,有各自單獨的作用域胶征。

  • let命令不存在變量提升

var命令會發(fā)生”變量提升“現(xiàn)象睛低,即變量可以在聲明之前使用暇昂,值為undefined伴嗡。這種現(xiàn)象多多少少是有些奇怪的急波,按照一般的邏輯,變量應該在聲明語句之后才可以使用瘪校。
為了糾正這種現(xiàn)象澄暮,let命令改變了語法行為,它所聲明的變量一定要在聲明后使用阱扬,否則報錯泣懊。

rainbow=function () {
// var 的情況
    console.log(foo);// 輸出undefined
    var foo=2;
// let 的情況
    console.log(bar);// 報錯ReferenceError
    let bar=2
};
rainbow();

上面代碼中,變量foo用var命令聲明麻惶,會發(fā)生變量提升馍刮,即腳本開始運行時,變量foo已經(jīng)存在了窃蹋,但是沒有值静稻,所以會輸出undefined。變量bar用let命令聲明押搪,不會發(fā)生變量提升垂谢。這表示在聲明它之前,變量bar是不存在的焚虱,這時如果用到它,就會拋出一個錯誤。

  • 暫時性死區(qū)

只要塊級作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個區(qū)域饮亏,不再受外部的影響。

var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代碼中,存在全局變量tmp砰识,但是塊級作用域內(nèi)let又聲明了一個局部變量tmp,導致后者綁定這個塊級作用域越平,所以在let聲明變量前瀑粥,對tmp賦值會報錯避咆。
ES6明確規(guī)定,如果區(qū)塊中存在let和const命令,這個區(qū)塊對這些命令聲明的變量,從一開始就形成了封閉作用域剂府。凡是在聲明之前就使用這些變量,就會報錯湾笛。
總之,在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的闰靴。這在語法上幅恋,稱為“暫時性死區(qū)”(temporal dead zone淑翼,簡稱 TDZ)诵盼。

if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError
  let tmp; // TDZ結束
  console.log(tmp); // undefined
  tmp = 123;
  console.log(tmp); // 123
}

上面代碼中戒财,在let命令聲明變量tmp之前饮寞,都屬于變量tmp的“死區(qū)”。
“暫時性死區(qū)”也意味著typeof不再是一個百分之百安全的操作。

typeof x; // ReferenceError
let x;

上面代碼中咨油,變量x使用let命令聲明法瑟,所以在聲明之前氓扛,都屬于x的“死區(qū)”,只要用到該變量就會報錯。因此,typeof運行時就會拋出一個ReferenceError犯建。

  • 不允許重復聲明

let不允許在相同作用域內(nèi)否彩,重復聲明同一個變量。

// 報錯
function () {
  let a = 10;
  var a = 1;
}
// 報錯
function () {
  let a = 10;
  let a = 1;
}

因此肌毅,不能在函數(shù)內(nèi)部重新聲明參數(shù)呜舒。

function func(arg) {
  let arg; // 報錯
}
function func(arg) {
  {
    let arg; // 不報錯
  }
}

  • 2般婆、塊級作用域

ES5 只有全局作用域和函數(shù)作用域,沒有塊級作用域晋辆,這帶來很多不合理的場景宇整。
第一種場景,內(nèi)層變量可能會覆蓋外層變量。

var tmp = new Date();
function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}
f(); // undefined
function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

上面的函數(shù)有兩個代碼塊,都聲明了變量n忽你,運行后輸出5简逮。這表示外層代碼塊不受內(nèi)層代碼塊的影響蕉堰。如果兩次都使用var定義變量n羹奉,最后輸出的值才是10诀拭。

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};
  • 塊級作用域與函數(shù)聲明
    ES6 引入了塊級作用域筒占,明確允許在塊級作用域之中聲明函數(shù)奏窑。ES6 規(guī)定,塊級作用域之中纬朝,函數(shù)聲明語句的行為類似于let骄呼,在塊級作用域之外不可引用共苛。
function f() { console.log('I am outside!'); }
(function () {
  if (false) {
    // 重復聲明一次函數(shù)f
    function f() { console.log('I am inside!'); }
  }
  f();
}());

上面代碼在 ES5 中運行,會得到“I am inside!”蜓萄,因為在if內(nèi)聲明的函數(shù)f會被提升到函數(shù)頭部隅茎,實際運行的代碼如下。

// ES5 環(huán)境
function f() { console.log('I am outside!'); }
(function () {
  function f() { console.log('I am inside!'); }
  if (false) {
  }
  f();
}());

ES6 就完全不一樣了嫉沽,理論上會得到“I am outside!”辟犀。因為塊級作用域內(nèi)聲明的函數(shù)類似于let
,對作用域之外沒有影響绸硕。但是混滔,如果你真的在 ES6 瀏覽器中運行一下上面的代碼糙申,是會報錯的,這是為什么呢宁舰?
原來喊括,如果改變了塊級作用域內(nèi)聲明的函數(shù)的處理規(guī)則凭需,顯然會對老代碼產(chǎn)生很大影響狸棍。為了減輕因此產(chǎn)生的不兼容問題瀏覽器的實現(xiàn)可以不遵守上面的規(guī)定胆胰,有自己的行為方式.

  • 允許在塊級作用域內(nèi)聲明函數(shù)。
  • 函數(shù)聲明類似于var垮斯,即會提升到全局作用域或函數(shù)作用域的頭部郎仆。
  • 同時,函數(shù)聲明還會提升到所在的塊級作用域的頭部甚脉。
    注意,上面三條規(guī)則只對 ES6 的瀏覽器實現(xiàn)有效铆农,其他環(huán)境的實現(xiàn)不用遵守牺氨,還是將塊級作用域的函數(shù)聲明當作let
    處理狡耻。
    根據(jù)這三條規(guī)則,在瀏覽器的 ES6 環(huán)境中猴凹,塊級作用域內(nèi)聲明的函數(shù)夷狰,行為類似于var
    聲明的變量。
    考慮到環(huán)境導致的行為差異太大郊霎,應該避免在塊級作用域內(nèi)聲明函數(shù)沼头。如果確實需要,也應該寫成函數(shù)表達式书劝,而不是函數(shù)聲明語句进倍。
// 函數(shù)聲明語句
{
  let a = 'secret';
  function f() {
    return a;
  }
}
// 函數(shù)表達式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

另外,還有一個需要注意的地方购对。ES6 的塊級作用域允許聲明函數(shù)的規(guī)則猾昆,只在使用大括號的情況下成立,如果沒有使用大括號骡苞,就會報錯垂蜗。

// 不報錯
'use strict';
if (true) {
  function f() {}
}
// 報錯
'use strict';
if (true)
  function f() {}
  • do表達式

本質(zhì)上,塊級作用域是一個語句解幽,將多個操作封裝在一起贴见,沒有返回值。

{
  let t = f();
  t = t * t + 1;
}

上面代碼中躲株,塊級作用域將兩個語句封裝在一起片部。但是,在塊級作用域以外徘溢,沒有辦法得到t的值吞琐,因為塊級作用域不返回值,除非t是全局變量然爆。
現(xiàn)在有一個提案站粟,使得塊級作用域可以變?yōu)楸磉_式,也就是說可以返回值曾雕,辦法就是在塊級作用域之前加上do奴烙,使它變?yōu)閐o表達式。

let x = do {
  let t = f();
  t * t + 1;
};

上面代碼中剖张,變量x會得到整個塊級作用域的返回值切诀。


  • 3、const命令

const聲明一個只讀的常量搔弄。一旦聲明幅虑,常量的值就不能改變。

const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.

const聲明的變量不得改變值顾犹,這意味著倒庵,const一旦聲明變量褒墨,就必須立即初始化,不能留到以后賦值擎宝。

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

const的作用域與let命令相同:只在聲明所在的塊級作用域內(nèi)有效郁妈。

if (true) {
  const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined

const命令聲明的常量也是不提升,同樣存在暫時性死區(qū)绍申,只能在聲明的位置后面使用噩咪。

if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}

上面代碼在常量MAX聲明之前就調(diào)用,結果報錯极阅。
const聲明的常量胃碾,也與let一樣不可重復聲明。

var message = "Hello!";
let age = 25;
// 以下兩行都會報錯
const message = "Goodbye!";
const age = 30;

const本質(zhì):
const實際上保證的涂屁,并不是變量的值不得改動书在,而是變量指向的那個內(nèi)存地址不得改動。對于簡單類型的數(shù)據(jù)(數(shù)值拆又、字符串儒旬、布爾值),值就保存在變量指向的那個內(nèi)存地址帖族,因此等同于常量栈源。但對于復合類型的數(shù)據(jù)(主要是對象和數(shù)組),變量指向的內(nèi)存地址竖般,保存的只是一個指針甚垦,const只能保證這個指針是固定的,至于它指向的數(shù)據(jù)結構是不是可變的涣雕,就完全不能控制了艰亮。因此,將一個對象聲明為常量必須非常小心挣郭。

const foo = {};
// 為 foo 添加一個屬性迄埃,可以成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only

上面代碼中兑障,常量foo儲存的是一個地址侄非,這個地址指向一個對象。不可變的只是這個地址流译,即不能把foo指向另一個地址逞怨,但對象本身是可變的,所以依然可以為其添加新屬性福澡。

const a = [];
a.push('Hello'); // 可執(zhí)行
a.length = 0;    // 可執(zhí)行
a = ['Dave'];    // 報錯

上面代碼中叠赦,常量a是一個數(shù)組,這個數(shù)組本身是可寫的革砸,但是如果將另一個數(shù)組賦值給a除秀,就會報錯窥翩。
如果真的想將對象凍結,應該使用Object.freeze方法鳞仙。

const foo = Object.freeze({});

// 常規(guī)模式時,下面一行不起作用笔时;
// 嚴格模式時棍好,該行會報錯
foo.prop = 123;

上面代碼中,常量foo指向一個凍結的對象允耿,所以添加新屬性不起作用借笙,嚴格模式時還會報錯。
除了將對象本身凍結较锡,對象的屬性也應該凍結业稼。下面是一個將對象徹底凍結的函數(shù)。

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};

ES6 聲明變量的六種方法:
var, function, let, const,import,class


  • 4蚂蕴、ES6 聲明變量的六種方法

頂層對象低散,在瀏覽器環(huán)境指的是window對象,在Node指的是global對象骡楼。ES5之中熔号,頂層對象的屬性與全局變量是等價的。

window.a = 1;
a // 1
a = 2;
window.a // 2

ES6為了改變這一點鸟整,一方面規(guī)定引镊,為了保持兼容性,var命令和function命令聲明的全局變量篮条,依舊是頂層對象的屬性弟头;另一方面規(guī)定,let命令涉茧、const命令赴恨、class命令聲明的全局變量,不屬于頂層對象的屬性降瞳。也就是說嘱支,從ES6開始,全局變量將逐步與頂層對象的屬性脫鉤挣饥。

var a = 1;
// 如果在Node的REPL環(huán)境除师,可以寫成global.a
// 或者采用通用方法,寫成this.a
window.a // 1
let b = 1;
window.b // undefined

上面代碼中扔枫,全局變量a由var命令聲明汛聚,所以它是頂層對象的屬性;全局變量b由let命令聲明短荐,所以它不是頂層對象的屬性倚舀,返回undefined叹哭。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痕貌,隨后出現(xiàn)的幾起案子风罩,更是在濱河造成了極大的恐慌,老刑警劉巖舵稠,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件超升,死亡現(xiàn)場離奇詭異,居然都是意外死亡哺徊,警方通過查閱死者的電腦和手機室琢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來落追,“玉大人盈滴,你說我怎么就攤上這事〗文疲” “怎么了巢钓?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疗垛。 經(jīng)常有香客問我竿报,道長,這世上最難降的妖魔是什么继谚? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任烈菌,我火速辦了婚禮,結果婚禮上花履,老公的妹妹穿的比我還像新娘芽世。我一直安慰自己,他們只是感情好诡壁,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布济瓢。 她就那樣靜靜地躺著,像睡著了一般妹卿。 火紅的嫁衣襯著肌膚如雪旺矾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天夺克,我揣著相機與錄音箕宙,去河邊找鬼。 笑死铺纽,一個胖子當著我的面吹牛柬帕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼陷寝,長吁一口氣:“原來是場噩夢啊……” “哼锅很!你這毒婦竟也來了?” 一聲冷哼從身側響起凤跑,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤爆安,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后仔引,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹏控,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年肤寝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抖僵。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲤看,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耍群,到底是詐尸還是另有隱情义桂,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蹈垢,位于F島的核電站慷吊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏曹抬。R本人自食惡果不足惜溉瓶,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谤民。 院中可真熱鬧堰酿,春花似錦、人聲如沸张足。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽为牍。三九已至哼绑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碉咆,已是汗流浹背抖韩。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疫铜,地道東北人帽蝶。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親励稳。 傳聞我的和親對象是個殘疾皇子佃乘,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • let 和 const 命令 let 命令 塊級作用域 const 命令 頂層對象的屬性 gl...
    安小明閱讀 981評論 0 0
  • let 命令 塊級作用域 const 命令 頂層對象的屬性 global 對象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice閱讀 1,630評論 0 2
  • let 命令 塊級作用域 const 命令 頂層對象的屬性 global 對象 let 命令 基本用法 ES6 新...
    卞卞村長L閱讀 594評論 0 0
  • let 基本用法 let命令,用來聲明變量驹尼。用法類似于var趣避,但聲明的變量,只在let命令所在的代碼塊內(nèi)有效新翎。 f...
    oWSQo閱讀 438評論 0 0
  • 本文屬個人筆記,不做詳解亏吝,僅供參考岭埠! let命令 基本用法 ES6 新增了let命令,用來聲明變量蔚鸥。它的用法類似于...
    R_yan閱讀 29,019評論 6 18