一、聲明變量
es5之前,聲明變量只有var聲明、隱式聲明和函數聲明。而es6中則多了let和const。
(1)let和const的“塊級作用域”:
var a = 1;
function test(){
var a = 2;
}
test();
console.log(a);
最終結果是"1",這是因為在js中,var有“函數作用域”,所以var a = 2的聲明只能在函數test區域里面有效,在外面則不會生效,現在去掉函數:
var a =1;
{
var a = 2;
}
console.log(a);
得到的結果則是“2”,這是因為var沒有“塊級作用域”。
像c、java等其他語言中,變量的聲明都有“塊級作用域”,所謂“塊”,就是以花括號{}為邊界,而花括號里面的區域,就是這個聲明可以被訪問的域區,而花括號之外就不可以了。在es6中,新添加的let的const同樣擁有“塊級作用域”,將上面代碼中的var改成let:
let a = 1;
{
let a = 2;
}
console.log(a);
最后輸出的結果依然是“1”,改成const也是這個結果。
塊級作用域,最大的應用,就是每篇let和const教學文章都會提到的for循環:
for( var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
},100)
}
3次輸出的結果都是“2”;
而改用let:
for( let i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
},100)
}
則3次輸出的結果分別是“0、1、2”;
(2)var有提升變量的作用,而let和const則沒有。
console.log(a);
var a = 1;
則輸出的結果是:
因為var有提升變量的功能,但是不能提升賦值,所以輸出的結果就是undefined,上面的代碼就相當于:
var a;
console.log(a);
a = 1;
如果把var改成let或者const,例如:
console.log(a);
let/const a = 1;
則得到的結果就是:
造成這樣的原因,是因為在let和const聲明某個變量之前,會形成一個“Temporal Dead Zone”(暫時性死區),在這個區域里是無法訪問這個變量。
(3)var可以重復聲明,而let和const在同一塊級作用域中不可以:
(1)var a = 1;
var a = 2;
(2)let a = 1;
let a = 2;
(3)const a = 1;
const a = 2;
(4)let a = 1;
const a = 2;
(5)let a = 1;
var a = 2;
(6)const a = 1;
var a = 2;
console.log(a);
后面5種情況都會報錯:
[注意,let和const在不同塊級作用域則可以同時存在,參見(1)“塊級作用域部分”];
(4)const是常量不可變,let可變:
const的全拼“constant”就是不可變的意思
let a = 1;
a = a+1;
console.log(a);
輸出的結果是“2”,如果改成const:
const a = 1;
a = a +1;
console.log(a);
則會報錯:
需要注意的是,const的不可變并不是完全不可變,例如:
const a = [];
a.push(1);
console.log(a);
則輸出的結果是“[1]”,這是因為const更像是“constant reference”。
也就是說,地址不能改變,而地址指向的值可以改變。
二、解構賦值(Destructuring):
es6之前,變量聲明的結構是十分固定的,變量名放在等號的左邊,而數組[]、對象{}和字符串則是放在等號的右邊。而在es6中,它們的位置可以互相調換了。
let arr = [1,2,3];
let first = arr[0];
console.log(first);
則輸出的結果是‘1’。
let arr = [1,2,3];
let [first] = arr;
console.log(first);
則輸出的結果還是‘1’。
而對象的解構,屬性名與變量名需要保持一致,否則會輸出undefined
let {first}={'a':1};
console.log(first); //undefined;
左側的變量可以通過逗號的形式跳過右側對應的值:
var [,b] = [1,2,3];
console.log(b); // 輸出結果為‘2’;
解構賦值的優勢,就是能省去不少額外的聲明,最典型的應用,就是json,例如后臺通過ajax傳過來的數據都是json,那是用的時候就不需要一個屬性一個屬性的聲明了:
let json = {'username':xxx, 'option':'a', 'score':'100'}
let {username, option, score} = json;
console.log(username);
// 不用解構的話,想獲取json中username的值,就要寫成 json.username。
還有一個就是給函數的參數設定默認值:
function test(a,b){
if(a == true){
var b = 1;
}else{
var b = 2;
}
console.log(b);
}
test(false);
而使用解構賦值的話:
function test(a,{b = 2}){
if(a == true){
var b = 1;
}
console.log(b);
}
test(true,{});
另外要注意一點,解構賦值只能獲取值,不能改變值:
let json = {num:1, num2:2};
let { num2 } = json;
console.log(num2 ); // 輸出的結果為2;
num2 = 3;
console.log( json) // 輸出的結果為{num:1, num2:2},而不是{num:1, num2:3};
通過解構賦值,json里num2的值被賦給了num2。但是反過來,如果你想通過直接修改num2的值來修改json里num2的值,這樣是不行的。
所以有時你可能還要再包一層json:
let json = { nums : { num:1 ,num2:2} };
let {nums} = json;
nums.num2 = 3;
console.log(json) // 輸出結果為{nums : { num:1 ,num2:3} };
三、模板字符串(template strings)----字符串拼接:
es5之前字符串的拼接是通過把字符串放在雙引號或單引號里,而變量則通過‘+’和字符串拼接起來,而在es6中則是把字符串放在反引號`` 里,變量則放在$符號和花括號里,例如:
const word = "world";
console.log(`hi! ${word}.`);
輸出的結果為:
而這個${}被稱為“模板替換”(template substitutions),在這里你可以放任意JavaScript表達式(運算表達式,函數的調用)或者是某個對象屬性、另一個模板字符串。
(1)換行
模板字符串添加了一個新的功能,支持換行。把兩句話放在一個雙引號或者單引號里,在兩句話之間通按回車鍵進行換行,即:
console.log('this is line1,
this is line2');
js會認為這句話少了半邊引號,導致報錯,要想實現換行,只能在兩句話之間加轉移字符‘\n’。
如果換用反引號,即:
這樣是不會報錯的,只是輸出的結果則為:
這是因為第二句話并不是在第二行最開始的地方書寫,而是留有一定的空隙。由于模板字符串會保留反引號之內每一行的所有空格,所以導致這些空隙也被保留了下了。如果改成:
那么兩句話的開頭就是對齊的。
當然,如果你想要實現在html里換行,模板字符串里直接回車也是不會生效的,只有加上換行標簽</br>才行。
document.write(`this is line1,
</br>this is line2`)
(2)嵌套
提到模板字符串,大部分的文章都會提到類似“模板字符串之間還可以進行嵌套”之類的話,而這個嵌套,也主要是指在${}里放入另一個模板字符串。例如聲明一個變量:
let b = 'world';
console.log(``${a}``); // 報錯,可見模板字符串不能直接嵌套在另一個反引號里。
console.log(`${ `hello $` }`); // 輸出‘hello world’
如果聲明多個變量:
let a = 'hello';
let b = 'world';
console.log(`${a `$`}`); //報錯:a is not a function(…),可見${}里面如果存在變量時,也不能放入另一個模板字符串。
(3)標簽模板(tagged template)
定義一個test函數:
function test(){
console.log('hello');
};
test ``;
最后結果輸出:
然而``調用函數和()調用函數還是有一定的區別,現在改成通過傳參的方式調用函數:
function test(data){
console.log(data);
};
test `hello`;
結果輸出的卻是數組:
這就是模板字符串的‘標記模板’屬性,所謂“標記模板”,就是定義一個函數,然后把該函數的函數名放在模板字符串的前面,這樣就能將該函數和模板字符串聯系起來,然后通過這個函數對模板字符串進行處理。