1、let命令用法:
a、let用來聲明變量,類似var,但用let聲明的變量,只能在其代碼塊中引用,相當於私有變量,不會被外界所獲取:
function fn(){
let a = 1
console.log(a) 這里可以直接調用
}
console.log(a) 在函數外面訪問輸出就會報錯
以后在for循環中比較適合用let:
var a = [];
for (var i = 0; i < 10; i++)
a[i] = function ();
console.log(i)
}
}
a[6]();這里會輸出10
用var聲明的i相當於全局變量,在全局范圍內都有效,所以全局只有一個變量i
。每一次循環,變量i
的值都會發生改變,而循環內被賦給數組a
的函數內部的console.log(i)
,里面的i
指向的就是全局的i
。也就是說,所有數組a
的成員里面的i
,指向的都是同一個i
,導致運行時輸出的是最后一輪的i
的值,也就是10。
把var換成let,最后輸出的結果就是6
還有在fou循環中,設置循環的部分是父作用域,在循環體內是子作用域:
for ( let i = 0; i < 10; i++){
let i = 'aaa';
console.log(i)
}
最終輸出10次'aaa',這是因為聲明在不同塊內,相互是不一樣的。
b、不存在變量提升了
console.log(a) 輸出結果是undefind
var a = 1;
console.log(b) 報錯
let b = 1;
這是因為用var聲明變量之前調用,因為變量提升的原因,即輸出的時候,變量已經存在,但沒有給a賦值,所以輸出undefined,但用let聲明變量只會在聲明后存在,之前調用就會報錯。
c、暫時性死區
let tem = 'abc';
if(true){
console.log(tem);
let tem;
}
這是就會報錯,因為如果在一個塊中存在let一個變量,那在let這個變量之前,不能出現這個變量!ES6明確規定,如果區塊中存在let
和const
命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。在代碼塊內,使用let
命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。
所以,在這個區域內使用tepyof就不太准確了。比如:
tepyof x;
let x;
這時就會報錯,而沒有聲明的變量反而不會報錯:
tepyof obj; undefined
所以一定要在聲明變量再使用!!!
let x = x;
這樣也是會報錯的,使用let
聲明變量時,只要變量在還沒有聲明完成前使用,就會報錯。上面這行就屬於這個情況,在變量x
的聲明語句還沒有執行完成前,就去取x
的值,導致報錯”x 未定義“。
ES6 規定暫時性死區和let
、const
語句不出現變量提升,主要是為了減少運行時錯誤,防止在變量聲明前就使用這個變量,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。
總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變量就已經存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現,才可以獲取和使用該變量。
d、不允許重復聲明
function fn(){
let a = 1;
var a = 2;(或者let a = 3;)
} 報錯
function fn2 (arg){
let arg = 2;
} 報錯
這樣的操作都會報錯,在同一個作用域下,不允許重復聲明一個變量,也不允許變量名和參數名相同
2、塊級作用域
以前只有全局作用域和函數作用域,現在增加了塊級作用域,為什么增加塊級作用域?
原因一:內層變量會覆蓋掉外層變量
var time = new Date();
function times () {
console.log(time);
if(false){
var time = 'hello';
}
}
times();
這時輸出的是undefined;因為變量提升的原因,函數作用域中存在私有變量time,但沒有賦值,所以就會覆蓋掉全局變量。
原因二:用來計數的循環變量泄漏成全局變量
var str = 'hello';
for (var i = 0; i < str.length; i++){
console.log(str[i]);
}
console.log(i);
在外層也會訪問到變量i;這樣i就變成來全局變量。
let為javascript提供來塊級作用域:
function fn(){
let n = 5;
if(true){
n = 10;
}
console.log(n);
}
這時輸出的還是 5 ,雖然內層和外層都定義了n,但外層不受內層的影響。如果換成var,輸出結果就是 10
ES6允許塊級任意嵌套:
{{{{{let n = 1;}}}}} 這是5層塊級作用域,外層的訪問不到內層的;
{{{
console.log(n);
{let n = 2;}
}}} 這是就會報錯。
{{{
let n = 1;
{let n = 2}
}}} 外層和內層允許存在相同的變量名,相互不受影響。
塊級作用域的出現,使得自執行函數(IIFE)不再必要了:
IIFE:
(function () {...})()
塊級作用域:
{let...}
do 表達式
本質上,塊級作用域是一個語句,將多個操作封裝在一起,沒有返回值。
{ let t = f(); t = t * t + 1; }
上面代碼中,塊級作用域將兩個語句封裝在一起。但是,在塊級作用域以外,沒有辦法得到t
的值,因為塊級作用域不返回值,除非t
是全局變量。
現在有一個提案,使得塊級作用域可以變為表達式,也就是說可以返回值,辦法就是在塊級作用域之前加上do
,使它變為do
表達式,然后就會返回內部最后執行的表達式的值。
let x = do { let t = f(); t * t + 1; };
上面代碼中,變量x
會得到整個塊級作用域的返回值(t * t + 1
)。
3、const基本用法:
const聲明常量,一旦聲明,其值將不能改變。所以也就意味着,一旦使用const聲明變量就要及時賦值,不能先聲明再賦值。
a、const和let一樣,只在聲明的作用域中起作用。
b、同樣不提升,只能在聲明后才能使用。
c、同樣不能重復聲明。
4、本質
const的本質就是引用地址不能變,但對於地址內部的信息是可以修改的,尤其是聲明的對象和數組這樣引用類型的:
const arr = [];
arr.lenght 可以執行;
arr.push(23) 可以執行
arr = ['abs'] 報錯
const str = {};
str.prop = 'abc' 可執行
str = {} 指向另一個對象就會報錯
如果真的要凍結對象,需要用到 Object.freeze方法
const str = Object.freeze({});
str.prop = 'abc' 在嚴格模式下就會報錯,在非嚴格模式下不起作用。
除了將對象本身凍結,對象的屬性也應該凍結。下面是一個將對象徹底凍結的函數。
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,后期再補充。