1.let
ES6新增了let指令,用來聲明變量。它的用法類似于var,但是所聲明的變量隻在let指令所在的代碼塊内有效
建立index.html,檔案内容為
<script type="text/javascript">
{
var a = 12;
}
console.log(a);
</script>
從console中可以列印出a的值

在ES6中,使用let聲明a的值,然後再次列印a的值,會報錯
<script type="text/javascript">
{
let a = 12;
}
console.log(a);
</script>
報錯如下所示,
上面兩個例子中,分别用let和var聲明了兩個變量。然後在代碼塊之外調用這兩個變量,結果let聲明的變量報錯,var聲明的變量傳回了正确的值。這表明,let聲明的變量隻在它所在的代碼塊有效
修改上面的代碼:
<script type="text/javascript">
{
var a = 12;
var a = 20;
}
console.log(a);
</script>
結果如下
同樣的把上面的var改為let,
<script type="text/javascript">
{
let a = 12;
let a = 20;
}
console.log(a);
</script>
重新整理浏覽器後可以看到,抛出的異常又不一樣了
因為let聲明的變量是塊級作用域,不能重複聲明
再次修改上面的代碼
<script type="text/javascript">
var a = [];
for(var i=0;i <10;i++){
a[i] = function () {
console.log(i);
};
}
a[6]();
</script>
重新整理浏覽器,得到的結果為:
修改代碼,把var改為let
<script type="text/javascript">
var a = [];
for(let i=0;i <10;i++){
a[i] = function () {
console.log(i);
};
}
a[6]();
</script>
再次重新整理浏覽器,得到的結果為:
上面代碼中,變量i是let聲明的,目前的i隻在本輪循環有效,是以每一次循環的i其實都是一個新的變量,是以最後輸出的是6。
如果每一輪循環的變量i都是重新聲明的,那它怎麼知道上一輪循環的值,進而計算出本輪循環的值?這是因為 JavaScript 引擎内部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算
修改代碼
<script type="text/javascript">
console.log(foo);
var foo = 2;
</script>
重新整理浏覽器後,得到的結果為
同樣的,把var改為let,又會出現異常
var指令會發生”變量提升“現象,即變量可以在聲明之前使用,值為undefined。按照一般的邏輯,變量應該在聲明語句之後才可以使用。
為了糾正這種現象,let指令改變了文法行為,它所聲明的變量一定要在聲明後使用,否則報錯。
上面代碼中,變量foo用var指令聲明,會發生變量提升,即腳本開始運作時,變量foo已經存在了,但是沒有值,是以會輸出undefined。變量bar用let指令聲明,不會發生變量提升。
這表示在聲明它之前,變量bar是不存在的,這時如果用到它,就會抛出一個錯誤。
ES5 隻有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
1.1 内層變量可能會覆寫外層變量。
<script type="text/javascript">
var tmp = new Date();
console.log(tmp)
function f() {
console.log(tmp);
if(false){
var tmp = "hello world"
}
}
f();
</script>
程式執行結果
上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,内部使用内層的tmp變量。但是,函數f執行後,輸出結果為undefined,原因在于變量提升,導緻内層的tmp變量覆寫了外層的tmp變量。
1.2 用來計數的循環變量洩露為全局變量。
<script type="text/javascript">
var str = "hello";
for(var i=0;i < str.length;i ++){
console.log(str[i])
}
console.log(i)
</script>
執行結果:
2.模闆字元串
傳統的JavaScript語言,輸出很長的資訊時,通常都是使用"+"号進行拼接的。
這種方法相當繁瑣不友善,ES6引入了模闆字元串解決這個問題
模闆字元串(template string)是增強版的字元串,用反引号(`)辨別。它可以當作普通字元串使用,也可以用來定義多行字元串,或者在字元串中嵌入變量
上面代碼中的模闆字元串,都是用反引号表示。如果在模闆字元串中需要使用反引号,則前面要用反斜杠轉義。
3.箭頭函數
ES6允許使用"箭頭"(=>)定義函數
var f = a => a
等同于
var f = function(a){
return a;
}
如果箭頭函數不需要參數或需要多個參數,就使用括号代表參數部分
//無參函數
var f = () => 5;
等同于
var f = function(){return 5};
//多個形參的函數
var f = (num1,num2) => num1 + num2
等同于
var f = function(num1,num2){
return num1 + num2;
}
使用箭頭函數需要注意的點:
3.1 函數體内的this對象,就是定義時所有的對象,而不是使用時所在的對象
代碼:
<script type="text/javascript">
var animal = {
name:"小狗",
age:3,
fav:function () {
// this是使用時定義的對象
console.log(this);
console.log(this.name);
}
};
animal.fav();
</script>
使用箭頭函數定義上面的函數
<script type="text/javascript">
var animal = {
name:"小狗",
age:3,
fav: ()=>{
// this指向定義時所在的對象(window)
console.log(this);
console.log(this.name);
}
};
animal.fav();
</script>
3.2 箭頭函數内部不可以使用arguments對象,該對象在函數體内不存在
再次修改上面的代碼,列印函數傳遞的參數arguments
<script type="text/javascript">
var animal = {
name:"小狗",
age:3,
fav:function () {
// 沒有使用箭頭函數,可以使用arguments擷取傳遞的參數
console.log(arguments);
console.log(this.name);
}
};
animal.fav(2,3,4);
</script>
使用箭頭函數定義上面的fav函數,再次列印函數傳遞的參數arguments
<script type="text/javascript">
var animal = {
name:"小狗",
age:3,
fav: () => {
// 使用箭頭函數時,arguments無法使用
console.log(arguments);
console.log(this.name);
}
};
animal.fav(2,3,4);
</script>
執行結果
4. 對象的單體模式
為了解決箭頭函數this指向的問題 推出來一種寫法 對象的單體模式
程式執行結果:
5. 面向對象
5.1 構造函數的方式建立對象
<script type="text/javascript">
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.showName = function () {
console.log(this.name)
}
var dog = new Animal("dog", 2);
console.log(dog.name);
console.log(dog.age);
</script>
上面這種寫法跟傳統的面向對象語言(比如 Java 和 python)差異很大,很容易讓新學習這門語言的程式員感到困惑。
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模闆。
通過class關鍵字,可以定義類。ES6 的class可以看作隻是一個文法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法隻是讓對象原型的寫法更加清晰、更像面向對象程式設計的文法而已。
5.2 class建立對象
修改上面的代碼,用class建立對象
<script type="text/javascript">
class Animal {
// 類似于python中的__init__方法
constructor(name, age) {
this.name = name;
this.age = age;
}
showName() {
console.log(this.name);
}
}
var d = new Animal("dog", 3);
d.showName();
</script>
上面代碼定義了一個“類”,可以看到裡面有一個constructor方法,這就是構造方法,而this關鍵字則代表執行個體對象。
也就是說,ES5 的構造函數Animal,對應 ES6 的Animal類的構造方法。
Animal類除了構造方法,還定義了一個showName方法。注意,定義"類"的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗号分隔,加了會報錯。
上面代碼表示,類本身就指向了類的構造函數。
使用的時候,也是直接對類使用new指令,跟構造函數的用法完全一緻。
5.3 constructor方法
constructor方法是類的預設方法,通過new指令生成對象執行個體時,自動調用該方法。
一個類必須有constructor方法,如果沒有顯式定義,一個空的constructor方法會被預設添加。
class Animal {
}
// 等同于
class Animal {
constructor() {}
}
上面代碼中,定義了一個空的類Point,JavaScript 引擎會自動為它添加一個空的constructor方法。