今天又開始學習ES6,光看會混淆,於是決定做筆記可以疏通一下自己的思路,又便於以後的鞏固,與大家共勉😉😉。
一、var、let和const的區別與詳解
ES5 只有兩種聲明變量的方法:var命令和function命令。ES6 除了添加let和const命令,還有另外兩種聲明變量的方法:import命令和class命令。所以,ES6 一共有 6 種聲明變量的方法。
今天我只學習了var、let和const,所以只記錄這三個。
- var、let聲明變量,const聲明常量
- var有變量提升,且當var是全局變量時,屬於頂層對象屬性;let、const不存在變量提升,不可重複聲明變量,聲明的變量只在它所在的代碼塊有效,不屬於頂層對象屬性
舉例:
let vs var:
(1) let變量只在他所在的代碼塊內有有效
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b //1
(2)for循環的計數器,就很合適使用let命令。
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
解釋:
在第一個中,變量i是var變量聲明的,i在全局範圍內都有效,也就是說,全局只有一個變量i,只不過i是動態變化的,每循環一次,i的值就變一次。所有數組a的成員裏面的i,指向的都是同一個i,導致運行時輸出的是最後一輪的i的值,也就是 10。
在第二個中,變量i是let聲明的,當前的i只在本輪循環有效,也就是說,每次循環的i都是新的變量。Javascript內部引擎會記住上一輪i的值,初始化本輪i的值,就在上輪的基礎上進行計算。
(3)循環變量的那部分是一個父作用域,而循環體內部是一個單獨的子作用域
for (let i = 0; i < 3; i++) {
console.log(i);
}
// 0 1 2
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
//?
大家覺得打印出來的結果會是什麼?0,1,2 還是 三個abc
答案是,輸出了三次 abc.這表明函數內部的變量i與循環變量i不在同一個作用域,有各自單獨的作用域
(4)不存在變量提升
var命令會發生“變量提升”現象,即變量可以在聲明之前使用,值爲undefined
let命令改變了語法行爲,它所聲明的變量一定要在聲明後使用,否則報錯。
// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;
// let 的情況
console.log(bar); // 報錯ReferenceError
let bar = 2;
(5)暫時性死區
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
解釋:
雖然var聲明瞭全局變量 temp,但是在代碼塊內又聲明瞭let型的局部變量temp,let型的局部變量temp綁定了這個代碼塊,導致在let聲明變量前對temp賦值會報錯。
(6)不允許重複聲明
// 報錯
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() // 不報錯
const:
(1)const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
(2)const的作用域與let命令相同:只在聲明所在的塊級作用域內有效。
(3)const命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置後面使用
本質:
const實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同於常量。但對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至於它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明爲常量必須非常小心。
const a = [];
a.push('Hello'); // 可執行
a.length = 0; // 可執行
a = ['Dave']; // 報錯
聲明瞭數組常量a,這個數組本身是可寫的,但是如果將另一個數組賦值給a就會報錯。
二、頂層對象
(1)頂層對象,在瀏覽器環境指的是window對象,在 Node 指的是global對象。ES5 之中,頂層對象的屬性與全局變量是等價的。
(2)爲了保持兼容性,ES6中,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬於頂層對象的屬性。也就是說,從 ES6 開始,全局變量將逐步與頂層對象的屬性脫鉤。
(3)在語言標準的層面,引入globalThis作爲頂層對象。也就是說,任何環境下,globalThis都是存在的,都可以從它拿到頂層對象,指向全局環境下的this。
三、塊級作用域
(1)允許塊級作用域的任意嵌套
{{{{
{let insane = 'Hello World'}
console.log(insane); // 報錯
}}}};
解釋:
上面代碼使用了一個五層的塊級作用域,每一層都是一個單獨的作用域。第四層作用域無法讀取第五層作用域的內部變量
(2)內層作用域可以定義外層作用域的同名變量
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
(3)塊級作用域的出現,實際上使得獲得廣泛應用的匿名立即執行函數表達式(匿名 IIFE)不再必要了
// IIFE 寫法
(function () {
var tmp = ...;
...
}());
// 塊級作用域寫法
{
let tmp = ...;
...
}
(4)塊級作用域與函數聲明
1.ES5 規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明
2.ES6 規定,塊級作用域之中,函數聲明語句的行爲類似於let,在塊級作用域之外不可引用
3.ES6 的塊級作用域必須有大括號,如果沒有大括號,JavaScript 引擎就認爲不存在塊級作用域