1. 塊作用域{ }
JS中作用域有:全局作用域、函數作用域。沒有塊作用域的概念。ECMAScript 6(簡稱ES6)中新增了塊級作用域。
塊作用域由 { } 包括,if語句和for語句里面的{ }也屬於塊作用域。
我們都知道在javascript里是沒有塊級作用域的,而ES6添加了塊級作用域,塊級作用域能帶來什么好處呢?為什么會添加這個功能呢?那就得了解ES5沒有塊級作用域時出現了哪些問題。
ES5在沒有塊級作用域的情況下出現的問題:
一。在if或者for循環中聲明的變量會泄露成全局變量
for(var i=0;i<=5;i++){ console.log("hello"); } console.log(i); //5
二。內層變量可能會覆蓋外層變量
var temp = new Date(); function f(){ console.log(temp); if(false){ var temp = "hello"; } } f(); //undefined
不管最后是否執行if語句,都會輸出undefined,因為temp會提升到函數頂部,因此覆蓋了外部的變量temp。
上一篇介紹的let和const命令,它們所聲明的變量只在所在的代碼塊內有效,即為js添加了塊級作用域。
【1】允許塊級作用域任意嵌套;
{{{let tmp = "hello world"}}}
【2】外層作用域無法讀取內層作用域的變量;
{{{ {let tmp = "hello world";} console.log(tmp); //error }}}
【3】內層作用域可以定義外層作用域的同名變量
{{{ let tmp = "hello world"; {let tmp = "hello world";} }}}
【4】函數本身的作用域在其所在的塊級作用域之內。
function f(){ console.log("outside"); } (function(){ if(false){ function f(){ console.log("inside"); } } f(); }());
這段代碼如果是在ES5中運行,那么會輸出inside,因為在ES5中,函數會提升到作用域的頂部,如果是在ES6中運行,則會輸出outside,因為在ES6中函數無法提升,所以訪問到的f()是外層的f()。
【5】在ES5中,因為沒有塊級作用域,獲得廣泛運用的是立即執行函數。現在ES6增加了塊級作用域,那么立即執行函數就不再必要了。ES6以前變量的作用域是函數范圍,有時在函數內局部需要一些臨時變量,因為沒有塊級作用域,所以就會將局部代碼封裝到IIEF(立即執行函數)中,這樣達到了想要的效果又不引入多余的臨時變量。而塊作用域引入后,IIEF當然就不必要了!臨時變量被封裝在IIFE中,就不會污染上層函數;而有塊級作用域,就不用封裝成IIEF,直接放到一個塊級中就好。更簡單的說法是,立即執行匿名函數的目的是建立一個塊級作用域,那么現在已經有了真正的塊級作用域,所以立即執行匿名函數就不需要了。
//立即執行函數 (function(){ var temp = "hello world"; }()); //塊級作用域 { var temp = "hello world"; }
2. var、let、const的區別
- var定義的變量,沒有塊的概念,可以跨塊訪問, 不能跨函數訪問,有變量提升。
- let定義的變量,只能在塊作用域里訪問,不能跨塊訪問,也不能跨函數訪問,無變量提升,不可以重復聲明。
- const用來定義常量,使用時必須初始化(即必須賦值),只能在塊作用域里訪問,而且不能修改,無變量提升,不可以重復聲明。注意:const常量,指的是常量對應的內存地址不得改變,而不是對應的值不得改變,所有把應用類型的數據設置為常量,其內部的值是可以改變的,例如:const a={}; a.b=13;//不會報錯 const arr=[]; arr.push(123);//不會報錯
-
let 聲明的變量只在塊級作用域內有效
'use strict'; function func(args){ if(true){ let i = 6; console.log('inside: ' + i); //不報錯 } console.log('outside: ' + i); // 報錯 "i is not defined" }; func();
不存在變量提升,而是“綁定”在暫時性死區
// 不存在變量提升 'use strict'; function func(){ console.log(i); let i; }; func(); // 報錯
在let聲明變量前,使用該變量,它是會報錯的,而不是像var那樣會‘變量提升’。
其實說let沒有‘變量提升’的特性,不太對。或者說它提升了,但是ES6規定了在let聲明變量前不能使用該變量。'use strict'; var test = 1; function func(){ console.log(test); let test = 2; }; func(); // 報錯
如果let聲明的變量沒有變量提升,應該打印’1’(func函數外的test);而它卻報錯,說明它是提升了的,只是規定了不能在其聲明之前使用而已。我們稱這特性叫“暫時性死區(temporal dead zone)”。且這一特性,僅對遵循‘塊級作用域’的命令有效(let、const)。
let使用經典案例:let命令代替閉包功能
閉包實現:var arr = []; for(var i = 0; i < 2; i++){ arr[i] = (function(i){ return function(){ console.log(i); }; }(i)); }; arr[1](); let 實現: 'use strict'; var arr = []; for(let i = 0; i < 2; i++){ arr[i] = function(){ console.log(i); }; }; arr[1]();
剩下 const 命令了!
const 與 let 的使用規范一樣,與之不同的是:
const 聲明的是一個常量,且這個常量必須賦值,否則會報錯。'use strict'; function func(){ const PI; PI = 3.14; console.log(PI); }; func(); // 報錯“Missing initializer in const declaration”