搜尋
首頁web前端js教程javascript ES6 新增了let指令使用介紹(圖文教學)

ES6新增了let指令,用來宣告變數。它的用法類似var,但是所宣告的變量,只在let指令所在的程式碼區塊內有效

let指令

#基本用法

# #ES6新增了let指令,用來宣告變數。它的用法類似var,但是所宣告的變量,只在let指令所在的程式碼區塊內有效。

{
 let a = 10;
 var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

上面程式碼在程式碼區塊之中,分別用let和var宣告了兩個變數。然後在程式碼區塊之外呼叫這兩個變量,結果let宣告的變數報錯,var宣告的變數回傳了正確的值。這表明,let聲明的變數只在它所在的程式碼區塊有效。

for迴圈的計數器,就很適合使用let指令。

for (let i = 0; i < 10; i++) {}

console.log(i);
//ReferenceError: i is not defined

上面程式碼中,計數器i只在for迴圈體內有效,在迴圈體外引用就會報錯。

下面的程式碼如果使用var,最後輸出的是10。

var a = [];
for (var i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 10

上面程式碼中,變數i是var宣告的,在全域範圍內都有效。所以每一次循環,新的i值都會覆蓋舊值,導致最後輸出的是最後一輪的i的值。

如果使用let,宣告的變數只在區塊層級作用域內有效,最後輸出的是6。

var a = [];
for (let i = 0; i < 10; i++) {
 a[i] = function () {
  console.log(i);
 };
}
a[6](); // 6

上面程式碼中,變數i是let宣告的,目前的i只在本輪迴圈有效,所以每一次迴圈的i其實都是一個新的變量,所以最後輸出的是6。

不存在變數提升

let不像var那樣會發生「變數提升」現象。所以,變數一定要在宣告後使用,否則報錯。

console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError

var foo = 2;
let bar = 2;

上面程式碼中,變數foo用var指令聲明,會發生變數提升,也就是當腳本開始執行時,變數foo已經存在了,但是沒有值,所以會輸出undefined。變數bar用let指令聲明,不會發生變數提升。這表示在宣告它之前,變數bar是不存在的,這時如果用到它,就會拋出錯誤。

暫時性死區

只要在區塊層級作用域內有let指令,它所宣告的變數就「綁定」(binding)這個區域,不再受外部的影響。

var tmp = 123;

if (true) {
 tmp = &#39;abc&#39;; // ReferenceError
 let tmp;
}

上面程式碼中,存在全域變數tmp,但是區塊級作用域內let又宣告了一個局部變數tmp,導致後者綁定這個區塊級作用域,所以在let宣告變數前,對tmp賦值會報錯。

ES6明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。

總之,在程式碼區塊內,使用let指令宣告變數之前,變數都是不可用的。這在語法上,稱為「暫時性死區」(temporal dead zone,簡稱TDZ)。

if (true) {
 // TDZ开始
 tmp = &#39;abc&#39;; // ReferenceError
 console.log(tmp); // ReferenceError

 let tmp; // TDZ结束
 console.log(tmp); // undefined

 tmp = 123;
 console.log(tmp); // 123
}

上面程式碼中,在let指令宣告變數tmp之前,都屬於變數tmp的「死區」。

「暫時性死區」也意味著typeof不再是百分之百安全的操作。

typeof x; // ReferenceError
let x;

上面程式碼中,變數x使用let指令聲明,所以在宣告之前,都屬於x的“死區”,只要用到該變數就會報錯。因此,typeof運行時就會拋出一個ReferenceError。

作為比較,如果一個變數根本沒有被聲明,使用typeof反而不會報錯。

typeof undeclared_variable // "undefined"

上面程式碼中,undeclared_variable是一個不存在的變數名,結果回傳「undefined」。所以,在沒有let之前,typeof運算子是百分之百安全的,永遠不會報錯。現在這一點不成立了。這樣的設計是為了讓大家養成良好的程式設計習慣,變數一定要在聲明之後使用,否則就報錯。

有些「死區」比較隱蔽,不太容易發現。

function bar(x = y, y = 2) {
 return [x, y];
}

bar(); // 报错

上面程式碼中,呼叫bar函數之所以報錯(某些實作可能不會報錯),是因為參數x預設值等於另一個參數y,而此時y還沒有聲明,屬於」死區「。如果y的預設值是x,就不會報錯,因為此時x已經宣告了。

function bar(x = 2, y = x) {
 return [x, y];
}
bar(); // [2, 2]

ES6規定暫時性死區和let、const語句不出現變量提升,主要是為了減少運行時錯誤,防止在變量聲明前就使用這個變量,從而導致意料之外的行為。這樣的錯誤在ES5是很常見的,現在有了這種規定,避免這類錯誤就很容易了。

總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到聲明變數的那一行程式碼出現,才可以取得和使用該變數。

不允許重複宣告

let不允許在相同作用域內,重複宣告同一個變數。

// 报错
function () {
 let a = 10;
 var a = 1;
}

// 报错
function () {
 let a = 10;
 let a = 1;
}

因此,不能在函數內部重新宣告參數。

function func(arg) {
 let arg; // 报错
}

function func(arg) {
 {
  let arg; // 不报错
 }
}

區塊層級作用域

為什麼需要區塊層級作用域?

ES5只有全域作用域和函數作用域,沒有區塊級作用域,這帶來很多不合理的場景。

第一種場景,內層變數可能會覆寫外層變數。

var tmp = new Date();

function f() {
 console.log(tmp);
 if (false) {
  var tmp = "hello world";
 }
}

f(); // undefined
###上面程式碼中,函數f執行後,輸出結果為undefined,原因在於變數提升,導致內層的tmp變數覆蓋了外層的tmp變數。 ###

第二种场景,用来计数的循环变量泄露为全局变量。

var s = &#39;hello&#39;;

for (var i = 0; i < s.length; i++) {
 console.log(s[i]);
}

console.log(i); // 5

上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

ES6的块级作用域

let实际上为JavaScript新增了块级作用域。

function f1() {
 let n = 5;
 if (true) {
  let n = 10;
 }
 console.log(n); // 5
}

上面的函数有两个代码块,都声明了变量n,运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var定义变量n,最后输出的值就是10。

ES6允许块级作用域的任意嵌套。

{{{{{let insane = &#39;Hello World&#39;}}}}};

上面代码使用了一个五层的块级作用域。外层作用域无法读取内层作用域的变量。

{{{{
 {let insane = &#39;Hello World&#39;}
 console.log(insane); // 报错
}}}};

内层作用域可以定义外层作用域的同名变量。

{{{{
 let insane = &#39;Hello World&#39;;
 {let insane = &#39;Hello World&#39;}
}}}};

块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。

// IIFE 写法
(function () {
 var tmp = ...;
 ...
}());

// 块级作用域写法
{
 let tmp = ...;
 ...
}

块级作用域与函数声明

函数能不能在块级作用域之中声明,是一个相当令人混淆的问题。

ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。

// 情况一
if (true) {
 function f() {}
}

// 情况二
try {
 function f() {}
} catch(e) {
}

上面代码的两种函数声明,根据ES5的规定都是非法的。

但是,浏览器没有遵守这个规定,为了兼容以前的旧代码,还是支持在块级作用域之中声明函数,因此上面两种情况实际都能运行,不会报错。不过,“严格模式”下还是会报错。

// ES5严格模式
&#39;use strict&#39;;
if (true) {
 function f() {}
}
// 报错

ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。

// ES6严格模式
&#39;use strict&#39;;
if (true) {
 function f() {}
}
// 不报错

ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。

function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 if (false) {
  // 重复声明一次函数f
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());

上面代码在 ES5 中运行,会得到“I am inside!”,因为在if内声明的函数f会被提升到函数头部,实际运行的代码如下。

// ES5版本
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 function f() { console.log(&#39;I am inside!&#39;); }
 if (false) {
 }
 f();
}());

ES6 的运行结果就完全不一样了,会得到“I am outside!”。因为块级作用域内声明的函数类似于let,对作用域之外没有影响,实际运行的代码如下。

// ES6版本
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 f();
}());

很显然,这种行为差异会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。

允许在块级作用域内声明函数。
函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
同时,函数声明还会提升到所在的块级作用域的头部。
注意,上面三条规则只对ES6的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。

前面那段代码,在 Chrome 环境下运行会报错。

// ES6的浏览器环境
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 if (false) {
  // 重复声明一次函数f
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());
// Uncaught TypeError: f is not a function

上面的代码报错,是因为实际运行的是下面的代码。

// ES6的浏览器环境
function f() { console.log(&#39;I am outside!&#39;); }
(function () {
 var f = undefined;
 if (false) {
  function f() { console.log(&#39;I am inside!&#39;); }
 }

 f();
}());
// Uncaught TypeError: f is not a function

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

// 函数声明语句
{
 let a = &#39;secret&#39;;
 function f() {
  return a;
 }
}

// 函数表达式
{
 let a = &#39;secret&#39;;
 let f = function () {
  return a;
 };
}

另外,还有一个需要注意的地方。ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

// 不报错
&#39;use strict&#39;;
if (true) {
 function f() {}
}

// 报错
&#39;use strict&#39;;
if (true)
 function f() {}

do 表达式

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

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

上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量。

现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式。

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

上面代码中,变量x会得到整个块级作用域的返回值。

JavaScript ES6 的 let 和 var 的比较

在javascript 1.7中, let 关键词被添加进来, 我听说它声明之后类似于”本地变量“, 但是我仍然不确定它和 关键词 var 的具体区别。

回答:
不同点在于作用域, var关键词的作用域是最近的函数作用域(如果在函数体的外部就是全局作用域), let 关键词的作用域是最接近的块作用域(如果在任何块意外就是全局作用域),这将会比函数作用域更小。
同样, 像var 一样, 使用let 声明的变量也会在其被声明的地方之前可见。

下面是Demo 例子。

全局(Global)

当在函数体之外它们是平等的。

let me = &#39;go&#39;; //globally scoped 
var i = &#39;able&#39;; //globally scoped

函数(Function)
当瞎下面这种, 也是平等的。

function ingWithinEstablishedParameters() { 
  let terOfRecommendation = &#39;awesome worker!&#39;; //function block scoped 
  var sityCheerleading = &#39;go!&#39;; //function block scoped 
};

块(Block)
这是不同点, let 只是在 for 循环中, var 却是在整个函数都是可见的。

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

ES6使用技巧總結

#ES6實作全螢幕捲動外掛步驟詳解

es6中Class特性使用詳解

以上是javascript ES6 新增了let指令使用介紹(圖文教學)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python vs. JavaScript:學習曲線和易用性Python vs. JavaScript:學習曲線和易用性Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境