let和const命令
let命令
基本用法
let用來聲明變量,但所聲明的變量只在let命令所在的代碼塊內有效,下面的代碼在let命令的代碼塊外引用a會報錯:
{
let a = 10;
}
a // ReferenceError: a is not defined.
for循環(huán)適合用let命令
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
上面代碼中的i 是let聲明的,當前的i只在本輪有效,每一個循環(huán)的i都是一個新的變量。
注意:for循環(huán)的設置循環(huán)變量的部分是一個父作用域,而循環(huán)體內是一個單獨的子作用域
不存在變量提升
“變量提升”即變量可以在聲明之前使用,值為undefined。而let命令不存在變量提升,它所聲明的變量一定要在聲明之后使用,否則會報錯。
暫時性死區(qū)
只要塊級作用域內存在let命令,它所聲明的變量就綁定這個區(qū)域,不再受外部的影響
“暫時性死區(qū)”是指在代碼塊中,使用let聲明變量之前,變量都是不可用的,否則會報錯。
如果在變量使用let命令聲明之前,使用typeof操作符會報錯:
typeof x; // ReferenceError
let x;
但如果變量根本沒有被聲明,則不糊報錯:
typeof undeclared_variable // "undefined"
較隱蔽死區(qū):
-
下面代碼中,參數x的默認值等于y,但此時y還沒有聲明,會報錯
function bar(x = y, y = 2) { return [x, y]; } bar(); // 報錯 -
下面代碼中,使用let命令聲明變量時,變量還沒有聲明完成,會報錯
// 不報錯 var x = x; // 報錯 let x = x; // ReferenceError: x is not defined
不允許重復聲明
let不允許在同一個作用域內重復聲明函數
// 報錯
function func() {
let a = 10;
var a = 1;
}
// 報錯
function func() {
let a = 10;
let a = 1;
}
因此不能再函數內部重新聲明參數
function func(arg) {
let arg;
}
func() // 報錯
function func(arg) {
{
let arg;
}
}
func() // 不報錯
塊級作用域
使用塊級作用域的原因
內層變量可能會覆蓋外層變量(因為存在變量提升)
用來計數的循環(huán)變量可能會泄露為全局變量
es6的塊級作用域
es6允許塊級作用域任意嵌套,外層作用域無法讀取內層作用域的變量,內層作用域可以定義外層作用域的同名變量
塊級作用域的出現,使立即執(zhí)行函數表達式(IIFE)不再必要。
// IIFE 寫法
(function () {
var tmp = ...;
...
}());
// 塊級作用域寫法
{
let tmp = ...;
...
}
塊級作用域與函數聲明
背景:ES5 規(guī)定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明。但瀏覽器沒有遵守這個規(guī)定。
es5中,在if內聲明是函數會被提到函數頭部,若執(zhí)行以下代碼:
(function () {
if (false) {
// 重復聲明一次函數f
function f() { console.log('I am inside!'); }
}
f();
}());
那么實際執(zhí)行的是:
(function () {
function f() { console.log('I am inside!'); }
if (false) {
}
f();
}());
es6引入塊級作用域,允許在塊級代碼中聲明函數,函數在塊級作用域之外不可用.
以下三條規(guī)則只對es6的瀏覽器有用(其他環(huán)境將塊級作用域聲明的函數當做let命令處理):
允許在塊級作用域內聲明函數
函數聲明類似于var,會提升到全局做用域或函數作用域的頭部
函數聲明還會提升到所在塊級作用域的頭部
應該避免在塊級作用域聲明函數,如果需要,應寫成函數表達式,避免寫成函數聲明語句
// 函數聲明語句
{
let a = 'secret';
function f() {
return a;
}
}
// 函數表達式
{
let a = 'secret';
let f = function () {
return a;
};
}
注意:es6的塊級作用域允許聲明函數的規(guī)則只在使用大括號的情況下成立,否則會報錯。
const命令
基本用法
const聲明一個只讀常量,一旦聲明,常量的值不改變;且一旦聲明變量,必須立刻初始化,否則會報錯
作用域:只在所聲明的塊級作用域有效。
const命令聲明的常量也不提升,也存在暫時性死區(qū),也不可重復聲明
本質
const實際上是變量指向的內存地址固定。對于復合類型的數據(對象和數組),變量指向的內存地址保存的是指向數據的指針。且const只保證這個指針是固定的,對于數據結構可不可變無法控制
const foo = {};
// 為 foo 添加一個屬性,可以成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only
如果像凍結對象,用object.freeze方法
const foo = Object.freeze({});
// 常規(guī)模式時,下面一行不起作用;
// 嚴格模式時,該行會報錯
foo.prop = 123;
下面代碼將對象徹底凍結(對象的屬性也凍結):
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
頂層對象的屬性
頂層對象:在瀏覽器中指window對象,在node指的是global對象。
在es6中,var和function命令聲明的全局變量,依舊是頂層對象的屬性;let、const、class命令生命的全局變量不是頂層對象的屬性:
var a = 1;
// 如果在 Node 的 REPL 環(huán)境,可以寫成 global.a
// 或者采用通用方法,寫成 this.a
window.a // 1
let b = 1;
window.b // undefined
global對象
this變量的局限性(背景):
全局環(huán)境中,
this會返回頂層對象。但是,Node 模塊和 ES6 模塊中,this返回的是當前模塊。函數里面的
this,如果函數不是作為對象的方法運行,而是單純作為函數運行,this會指向頂層對象。但是,嚴格模式下,這時this會返回undefined。不管是嚴格模式,還是普通模式,
new Function('return this')(),總是會返回全局對象。
很難找到一種方法可以在所有情況下都取得頂層對象。有一個提案,在語言標準的層面,引入global作為頂層對象。也就是說,在所有環(huán)境下,global都是存在的,都可以從它拿到頂層對象。
墊片庫system.global模擬了這個提案,可以在所有環(huán)境拿到global,如下:
// CommonJS 的寫法
require('system.global/shim')();
// ES6 模塊的寫法
import shim from 'system.global/shim'; shim();
下面代碼將頂層對象放入變量global:
// CommonJS 的寫法
var global = require('system.global')();
// ES6 模塊的寫法
import getGlobal from 'system.global';
const global = getGlobal();