ES6let命令和塊級作用域和const命令
2018年3月7日
ES6 let命令和塊級作用域和const命令
一、let命令
1.ES6 新增了let命令,用來宣告變數。它的用法類似於var,但是所宣告的變數,只在let命令所在的程式碼塊內有效。
{
let a = 10;
var b = 1;
}
console.log(b);//1
console.log(a);//Uncaught ReferenceError: a is not defined
上面程式碼let宣告的變數只在它所在的程式碼塊有效。
2.計數器i只在for迴圈體內有效,for迴圈設定迴圈變數的那部分是一個父作用域,迴圈體內部是一個單獨的子作用域。
for (let i = 0; i < 10; i++) {
//...
}
console.log(i);//Uncaught ReferenceError: i is not defined
上面程式碼中,計數器i只在for迴圈體內有效,在迴圈體外引用就會報錯。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();//10
上面程式碼中,變i量是var命令宣告的,i是全域性變數。每一次迴圈,變數i的值都會發生改變,而迴圈內被賦給陣列a的函式內部的console.log(i)的i指向全域性的i。導致執行時輸出的是最後一輪的i的值,也就是 10。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();//6
上面程式碼中,變數i是let宣告的,當前的i只在本輪迴圈有效,所以每一次迴圈的i其實都是一個新的變數,所以最後輸出的是6。JavaScript 引擎內部會記住上一輪迴圈的值,初始化本輪的變數i時,就在上一輪迴圈的基礎上進行計算。
for (let i = 0; i < 3; i++) {
let i = `hello`;
console.log(i);
}
//hello
//hello
//hello
上面程式碼中,輸出3次hello。函式內部的變數i與迴圈變數i不在同一個作用域,有各自單獨的作用域。for迴圈有一個特別之處,設定迴圈變數的那部分是一個父作用域,迴圈體內部是一個單獨的子作用域。
3.不存在變數提升。let命令改變了語法行為,它所宣告的變數一定要在宣告後使用,否則報錯。
console.log(a);//undefined
var a = 1;
console.log(b);//Uncaught ReferenceError: b is not defined
let b = 1;
上面程式碼中,變數a用var命令宣告,會發生變數提升,指令碼開始執行時,變數a已經存在了,但沒有值,所以會輸出undefined。變數b用let命令宣告,不會發生變數提升。在宣告它之前,變數b是不存在的,這時就會丟擲一個錯誤。
4.暫時性死區。暫時性死區和let、const語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有宣告變數後,才可以獲取和使用該變數。
var a = 123;
if (true) {
a = `hello`; // ReferenceError
let a;
}
上面程式碼中,全域性變數a已經存在,在當前作用域中宣告之後才可以使用。
typeof x; // ReferenceError
let x;
上面程式碼中,在宣告之前typeof執行時丟擲一個ReferenceError。
typeof a;//undefined
如上,變數a沒有被宣告,typeof不報錯,“暫時性死區”意味著typeof不再是百分之百安全的操作。
function a(x = y, y = 2) {
return [x, y];
}
a();//Uncaught ReferenceError: y is not defined
如上,引數x預設值等於另一個引數y,而此時y還沒有宣告,屬於“死區”,這裡是隱式宣告容易犯錯。
function a(x = 2, y = x) {
return [x, y];
}
a();//[2, 2]
如上隱式宣告,y的預設值是x,就不會報錯,此時x已經宣告瞭。
5.不允許重複宣告,不允許在相同作用域內重複宣告,不能在函式內部重新宣告引數。
function func() {
let a = 10;
let a = 1;
}
// ReferenceError 報錯
function func() {
let a = 10;
var a = 1;
}
// ReferenceError 報錯
不允許在相同作用域內,重複宣告同一個變數。
function func(arg) {
let arg; // ReferenceError 報錯
}
function func(arg) {
{
let arg; // undefined 不報錯
}
}
不能在函式內部重新宣告引數。
二、塊級作用域
1.塊級作用域,ES5 只有全域性作用域和函式作用域,沒有塊級作用域,帶來很多不合理的場景。塊級作用域允許任意巢狀,外層作用域無法讀取內層作用域的變數,內層作用域可以定義外層作用域的同名變數,塊級作用域寫法可以替代立即執行函式表示式(IIFE)。
var tmp = new Date();
function f() {
console.log(tmp);
if (true) {
var tmp = `hello world`;
}
}
f();// undefined
上面程式碼中,if程式碼塊的外部使用外層的tmp變數,內部使用內層的tmp變數。但是,函式f執行後,輸出結果為undefined,原因在於變數提升,導致內層的tmp變數覆蓋了外層的tmp變數。
var s = `hello`;
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i);
上面程式碼中,變數i只用來控制迴圈,但是迴圈結束後,它並沒有消失,洩露成了全域性變數。
function f() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
f();// 5
上面程式碼中,外層程式碼塊不受內層程式碼塊的影響。
{{{{{let a = `Hello World`}}}}};
{{{{
{let a = `Hello World`}
console.log(a); // 報錯
}}}};
上面程式碼中,塊級作用域允許任意巢狀,外層作用域無法讀取內層作用域的變數。
{{{{
let a = `Hello World`;
{let a = `Hello World`}
}}}};
上面程式碼中,內層作用域可以定義外層作用域的同名變數。
// IIFE 寫法
(function () {
var a = ...;
}());
// 塊級作用域寫法
{
let a = ...;
}
上面程式碼中,塊級作用域的出現,實際上使獲得廣泛應用的立即執行函式表示式(IIFE)不再必要了。
2.塊級作用域與函式,函式在塊級作用域之外不可引用,應避免在塊級作用域內宣告函式,如果確實需要,也應該寫成函式表示式,而不是函式宣告語句,只在使用大括號的情況下成立。
// 瀏覽器的 ES6 環境
function f() { console.log(`I am outside!`); }
(function () {
if (false) {
// 重複宣告一次函式f
function f() { console.log(`I am inside!`); }
}
f();
}());
// Uncaught TypeError: f is not a function
ES6 規定,塊級作用域之中允許宣告函式,語句的行為類似於let,在塊級作用域之外不可引用。
上面程式碼在ES6環境中,實際執行程式碼如下:
// 瀏覽器的 ES6 環境
function f() { console.log(`I am outside!`); }
(function () {
var f = undefined;
if (false) {
function f() { console.log(`I am inside!`); }
}
f();
}());
// Uncaught TypeError: f is not a function
如上,考慮到環境導致的行為差異太大,應該避免在塊級作用域內宣告函式。
// 函式宣告語句
{
let a = `secret`;
function f() {
return a;
}
}
// 函式表示式
{
let a = `secret`;
let f = function () {
return a;
};
}
如上,如果確實需要,也應該寫成函式表示式,而不是函式宣告語句。
// 不報錯
`use strict`;
if (true) {
function f() {}
}
// 報錯
`use strict`;
if (true)function f() {}
如上,ES6 的塊級作用域允許宣告函式的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。
三、const命令:const宣告一個只讀的常量。一旦宣告變數,就必須立即初始化,不能留到以後賦值。只在宣告所在的塊級作用域內有效。存在暫時性死區,只能在宣告的位置後面使用。不可重複宣告。宣告常量的值不能改變(數值、字串、布林值),對於複合型別的資料(物件和陣列)需要凍結。
1.必須立刻初始化。
const a;// SyntaxError: Missing initializer in const declaration
2.const的作用域與let命令相同:只在宣告所在的塊級作用域內有效。
{
const a = 1;
}
a // ReferenceError: a_d is not defined
3.const命令宣告的常量也是不提升,同樣存在暫時性死區,只能在宣告的位置後面使用。
{
console.log(a);// ReferenceError: a is not defined
const a = 1;
}
4.const宣告的常量,與let一樣不可重複宣告。
{
var a = 1;
let b = `hello`;
const a = 2; // SyntaxError: Identifier `a` has already been declared
const b = `world`; // SyntaxError: Identifier `b` has already been declared
}
5.常量的值不能改變,將一個複合型別資料(物件、陣列)宣告為常量必須非常小心,需要凍結物件使用Object.freeze方法,。
const a = 1;
a = 2;// TypeError: Assignment to constant variable.
對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指標,const只能保證這個指標是固定的,至於它指向的資料結構是不是可變的,就完全不能控制了。
{
const a = [`hello`];
a.push(`world`);
console.log(a); // ["hello", "world"]
a = [`js`]; // TypeError: Assignment to constant variable.
}
如上,陣列a本身是可寫的,但是如果將另一個陣列賦值給a,就會報錯。
{
const a = Object.freeze([`hello`]);
a.push(`world`);// TypeError: Cannot add property 1, object is not extensible
}
如上,想將物件凍結,應該使用Object.freeze方法。
{
const a = [`hello`];
let func = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === `object` ) {
func( obj[key] );
}
});
};
func(a);
Object.isFrozen(a); // true
}
如上,除了將物件本身凍結,物件的屬性也應該凍結,使用Object.isFrozen方法檢測是否凍結返回true。
ES6改變了很多,更加有邏輯性,目前還沒有完全支援ES6的JavaScript代理(無論是瀏覽器環境還是伺服器環境),熱衷於使用語言最新特性的開發者需要將ES6程式碼轉譯為ES5程式碼。儘管ES6做了大量的更新,但是它依舊完全向後相容以前的版本,標準化委員會決定避免由不相容版本語言導致的“web體驗破碎”。結果是,所有老程式碼都可以正常執行,整個過渡也顯得更為平滑,但隨之而來的問題是,多年的老問題依然存在。
Babel轉化工具
ES6相容性
相關文章
- ES6 let和const命令
- 【譯】深入理解 ES2015,第一趴:塊作用域 let 和 const
- JavaScript 塊級作用域JavaScript
- JavaScript const 命令JavaScript
- ping命令的作用和原理簡述
- 阮一峰的ES6---let和const命令
- let與const命令
- JavaScript塊級作用域宣告函式JavaScript函式
- ES6之塊級作用域
- js的作用域和作用域鏈JS
- 11-程式碼塊和變數的作用域變數
- 深入理解JavaScript作用域和作用域鏈JavaScript
- nginx 命令和訊號及平滑升級Nginx
- C語言 關鍵字const的作用 const int* 和int *const 的區別C語言
- nginx啟動命令和停止命令。Nginx
- linux中file命令和find命令Linux
- ES6語法(一)塊級作用域、字串字串
- linux命令大全-linux命令使用和管理Linux
- SpringMVC(3)-request域和session域的作用和區別SpringMVCSession
- Linux系統中的管道命令、grep命令、sed命令和awk命令Linux
- CMD和ENTRYPOINT命令
- 重讀《深入理解ES6》 —— 塊級作用域
- js基礎梳理-如何理解作用域和作用域鏈?JS
- Docker 最常用的映象命令和容器命令Docker
- JavaScript之作用域和閉包JavaScript
- JavaScript中變數和作用域JavaScript變數
- js 的詞法作用域和 thisJS
- linux 命令尾部&的作用Linux
- 深入學習js之——詞法作用域和動態作用域JS
- ES6深入學習(一)塊級作用域詳解
- NodeJS和命令列程式NodeJS命令列
- CMD和power shell命令
- 【linux命令】setfacl和getfaclLinux
- openssl命令列和API命令列API
- Linux命令分享- 新建使用者和組命令Linux
- sed命令和find命令的結合的使用
- 對js中執行環境、作用域和作用域鏈的理解JS
- 深入學習js之——詞法作用域和動態作用域#2JS