個人主頁:學習前端的小z
個人專欄:JavaScript 精粹
本專欄旨在分享記錄每日學習的前端知識和學習筆記的歸納總結,歡迎大家在評論區交流討論!
ES5、ES6介紹
文章目錄
- 💯聲明命令 let、const
- 🍟1 let聲明符
- 🎲1.1 基礎特性
- 🎲1.2 不進行變量提升
- 🎲1.3 致死域(暫時性死區)
- 🎲1.4 無法重復聲明
- 🎲1.5 塊作用域 (block scope)
- 🎲1.6 塊作用域下的函數聲明
- 🍟2 const聲明符
- 🎲2.1 聲明時必須賦值
- 🎲2.2 值為對象
- 💯解構賦值
- 🍟1 數組解構
- 🎲1.1 基礎變量賦值
- 🎲1.2 聲明分別賦值
- 🎲1.3 解構默認值
- 🎲1.4 交換變量值
- 🎲1.5 解析函數返回的數組
- 🎲1.6 忽略返回值/跳過某項
- 🎲1.7 賦值數組剩余值給一個變量
- 🎲1.8 嵌套數組解構
- 🍟2 對象解構
- 🎲2.1 基礎對象解構
- 🎲2.2 無聲明解構
- 🎲2.3 賦值給新變量名
- 🎲2.4 解構默認值
- 🎲2.5 賦值給新變量名的同時提供默認值
- 🎲2.6 嵌套對象和數組解構
- 🎲2.7 可計算對象屬性名與解構
- 🎲2.8 同時使用數組和對象解構
- 🎲2.9 注意點
- 🍟3 函數參數的解構賦值
- 🍟4 解構的用途
- 🎲4.1 交換變量的值
- 🎲4.2 從函數返回多個值
- 🎲4.3 函數參數的定義
- 🎲4.4 提取 JSON 數據
- 🎲4.5 函數參數的默認值
💯聲明命令 let、const
🍟1 let聲明符
ES6 新增了
let
命令,用來聲明變量。它的用法類似于var
,但是所聲明的變量,只在let
命令所在的代碼塊內有效。
🎲1.1 基礎特性
if(true){let a = 'let';var b = 'var';
}console.log(b); //var
console.log(a); //Uncaught ReferenceError: a is not defined---------------------------------------------------------//使用let,聲明的變量僅在塊級作用域內({ })有效,最后輸出的是 6。
for(let i = 0; i < 10; i++){}
console.log(i); //Uncaught ReferenceError: i is not defined
上面代碼在代碼塊之中,分別用let
和var
聲明了兩個變量。然后在代碼塊之外調用這兩個變量,結果let
聲明的變量報錯,var
聲明的變量返回了正確的值。這表明,let
聲明的變量只在它所在的代碼塊有效。
// 在循環體表達式中let聲明迭代變量i是let聲明的,當前的i只在本輪循環有效,所以每一次循環的i其實都是一個新的變量
var a = [];
for (let i = 0; i < 10; i++) {a.push(function () {console.log(i);});
}
a[5](); // 5
// 在循環體表達式中let聲明 和循環體中let 聲明同名變量是不沖突的 設置循環變量的那部分是一個父作用域,而循環體內部是一個單獨的子作用域。相當于每次循環{}內部都會單獨開一個局部作用域
for (let i = 0; i < 3; i++) {let i = 'abc';console.log(i);
}
🎲1.2 不進行變量提升
var
命令會發生“變量提升”現象,即變量可以在聲明之前使用,值為undefined
。
let
命令改變了語法行為,它所聲明的變量一定要在聲明后使用,否則報錯。 這一點需要特殊注意
// var 的情況
console.log(num); // 輸出undefined
var num = 2;-------------------------// let 的情況
console.log(age); // 報錯ReferenceError
let age = 16;
🎲1.3 致死域(暫時性死區)
只要塊級作用域內存在
let
命令,它所聲明的變量就“綁定”(binding)這個區域,不再受外部的影響。
//正常調用
let num = 10;
if (true) {console.log(num); //10
}------------------//if { }內部let為獨立局部作用域 訪問為 20
let num = 10;
if (true) {let num = 20;console.log(num); //20
}------------------//發生致死 因為fn局部作用域中有let聲明num 所以在作用域內 不能先調用再使用let聲明。
let num = 10;
function fn() {console.log(num); //Uncaught ReferenceError: Cannot access 'num' before initializationlet num = 20;
}
fn();------------------//typeof操作影響typeof num;
let num; //Uncaught ReferenceError: Cannot access 'num' before initialization
當程序的控制流程在新的作用域(module function 或 block 作用域)進行實例化時,在此作用域中用let/const聲明的變量會先在作用域中被創建出來,但因此時還未進行詞法綁定,所以是不能被訪問的,如果訪問就會拋出錯誤。因此,在這運行流程進入作用域創建變量,到變量可以被訪問之間的這一段時間,就稱之為暫時死區。
es6規定 let
/const
會使區域形成封閉的作用域 若在聲明之前使用變量就會發生錯誤, 在代碼塊
內使用let命令聲明變量之前 該變量都無法使用,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變量就已經存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現,才可以獲取和使用該變量。使用let 確保先 聲明 再 使用 變量 不要混用let和var在同一個作用域鏈上 避免死區
🎲1.4 無法重復聲明
let
不允許在相同作用域內,重復聲明同一個變量。
//發生致死 因為var會變量提升。
let num = 10;
if (true) {var num = 20; //Uncaught SyntaxError: Identifier 'num' has already been declaredconsole.log(num);
}
🎲1.5 塊作用域 (block scope)
在ES5中,只全局作用域和函數作用域。這會導致函數作用域覆蓋了全局作用域;亦或者循環中的變量泄露為全局變量。
EcmaScript 6引入了塊級作用域(block scope),塊級作用域只能在塊中被訪問,以下兩種情況可以創建塊級作用域的變量。
-
在函數中
-
在被
{
和}
包裹的塊中
{var x = 10;
}
console.log(x); //10------------------{let x = 10;
}
console.log(x);//x is not defined------------------//函數有兩個代碼塊,都聲明了變量num,運行后輸出 3。這表示外層代碼塊不受內層代碼塊的影響。如果兩次都使用var定義變量num,最后輸出的值才是 6。
function fn() {let num = 3;if (true) {let num = 6;}console.log(num); // 3
}
🎲1.6 塊作用域下的函數聲明
ES5 規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明。
ES6 的塊級作用域必須有大括號,如果沒有大括號,JavaScript 引擎就認為不存在塊級作用域。
function a() {console.log('我是全局作用域內聲明的函數a');
}(function () {if (false) {function a() {console.log('我是塊級作用域內聲明的函數a');}}a();
})();----------------------function a() {console.log('我是全局作用域內聲明的函數a');
}
(function () {var a;if (false) {a = function () {console.log('我是塊級作用域內聲明的函數a');}}a();
})();
為了減輕因此產生的不兼容問題,ES6 在附錄 B里面規定,瀏覽器的實現可以不遵守上面的規定,有自己的行為方式。
- 允許在塊級作用域內聲明函數。
- 函數聲明類似于
var
,即會提升到全局作用域或函數作用域的頭部。 - 同時,函數聲明還會提升到所在的塊級作用域的頭部。
🍟2 const聲明符
const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。 const與let在 塊作用域 重復聲明 致死域的問題上是一致的const
的作用域與let
命令相同:只在聲明所在的塊級作用域內有效。
const DATA = '我是常量 不能改變哦';
DATA // '我是常量 不能改變哦'DATA = '改一個試試看';
// TypeError: Assignment to constant variable.
const
聲明的變量不得改變值,這意味著,const
一旦聲明變量,就必須立即初始化,不能留到以后賦值。
🎲2.1 聲明時必須賦值
const X;
//Uncaught SyntaxError: Missing initializer in const declaration
🎲2.2 值為對象
const KEY_MAP = {a:1,b:2
}KEY_MAP['a'] = 2; //不報錯-----------const ARRAY = [];
ARRAY.push('something'); // 可執行
ARRAY.length = 0; // 可執行
ARRAY = ['something']; // 報錯
💯解構賦值
ES6提供了更簡潔的賦值模式, 從數組和對象中提取值. 這被稱為解構(Destructuring)。
[a, b] = [50, 100];console.log(a);
// expected output: 50console.log(b);
// expected output: 100[a, b, ...rest] = [10, 20, 30, 40, 50];console.log(rest);
// expected output: [30, 40, 50]
我們有很多種不同的方式使用 JavaScript 解構。
🍟1 數組解構
數組解構是極為簡單整潔的,在復制表達式的左側使用數組字面量。數組字面量中的每個變量名稱映射為解構數組的相同索引項。
🎲1.1 基礎變量賦值
let foo = ['one', 'two', 'three'];let [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"
🎲1.2 聲明分別賦值
你可以通過變量聲明分別解構賦值。舉例:首先,聲明變量,然后分別賦值。
// 聲明變量
let a, b;
// 然后分別賦值
[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2
🎲1.3 解構默認值
如果解構取出的值是 undefined
,可以設置默認值:
let a, b;
// 設置默認值
[a = 5, b = 7] = [1];
console.log(a); // 1
console.log(b); // 7
在上面的例子中,我們給 a
和 b
設置了默認值。這種情況下,如果 a
或 b
的值是 undefined
,它將賦值默認值 5
給 a
7
給 b
🎲1.4 交換變量值
解構可以在一個解構表達式中交換兩個變量值
let a = 1;
let b = 3;[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
如果你想不使用解構交換變量值,將必須提供一個緩存變量或者同解構一起使用
🎲1.5 解析函數返回的數組
可以解構函數返回的數組。
function c() {return [10, 20];
}let a, b;
[a, b] = c();
console.log(a); // 10
console.log(b); // 20
在上面的例子中,c()
的返回值 [10, 20]
可以在單獨的一行代碼中使用解構解析。
🎲1.6 忽略返回值/跳過某項
你也可以跳過一些沒有用的返回值。
function c() {return [1, 2, 3];
}let [a, , b] = c();
console.log(a); // 1
console.log(b); // 3
在罕見的情況下,你想忽略所有的值。
[, ,] = c();
當然,這是不會發生的。
🎲1.7 賦值數組剩余值給一個變量
當你使用數組解構,你可以賦值數組剩余部分給一個單獨的變量。
let [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]
小心結尾的逗號語法錯誤,它將爆發如果在剩余元素的左側使用結尾逗號:
let [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma
🎲1.8 嵌套數組解構
像對象一樣,你也可以使用數組嵌套解構。這里有一個例子:
const color = ['#FF00FF', [255, 0, 255], 'rgb(255, 0, 255)'];// Use nested destructuring to assign red, green and blue
// 使用嵌套解構賦值 red, green, blue
const [hex, [red, green, blue]] = color;console.log(hex, red, green, blue); // #FF00FF 255 0 255
🍟2 對象解構
🎲2.1 基礎對象解構
let x = { y: 22, z: true };
let { y, z } = x; // let {y:y,z:z} = x;的簡寫console.log(y); // 22
console.log(z); // true
🎲2.2 無聲明解構
你可以使用解構分別從它的聲明賦值變量。這意味著在上面的例子中不需要創建變量 x
。
let y, z;({ y, z } = { y: 1, z: 2 });
注意:圓括號
(...)
包裹賦值聲明是必須的當使用對象字面量解構賦值無聲明變量。
{a, b} = {a: 1, b: 2}
不是有效的獨立語法,左側的{a, b}
被考慮為代碼塊而不是一個對象字面量。因此,
({a, b} = {a: 1, b: 2})
是有效的, 等價于var {a, b} = {a: 1, b: 2}
。
(...)
表達式需要前置分號或者它可能用于在前一行執行函數。
🎲2.3 賦值給新變量名
當使用對象解構時你也可以改變變量的名稱,如下例子:
let o = { p: 22, q: true };
let { p: foo, q: bar } = o;console.log(foo); // 22
console.log(bar); // true
例子中,var {p: foo} = o
獲取對象 o
的屬性名 p
,然后賦值給一個名稱為 foo
的變量。
🎲2.4 解構默認值
如果解構取出的對象值是 undefined
你也可以設置默認值。
let { a = 10, b = 5 } = { a: 3 };console.log(a); // 3
console.log(b); // 5
🎲2.5 賦值給新變量名的同時提供默認值
let { a: aa = 10, b: bb = 5 } = { a: 3 };console.log(aa); // 3
console.log(bb); // 5
🎲2.6 嵌套對象和數組解構
const metadata = {title: 'Scratchpad',translations: [{locale: 'de',localization_tags: [],last_edit: '2014-04-14T08:43:37',url: '/de/docs/Tools/Scratchpad',title: 'JavaScript-Umgebung',},],url: '/en-US/docs/Tools/Scratchpad',
};let {title: englishTitle, // 重命名translations: [{title: localeTitle, // 重命名},],
} = metadata;console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"
🎲2.7 可計算對象屬性名與解構
當使用解構改變對象屬性的名稱時,可以使用對象計算屬性名。
let key = 'z';
let { [key]: foo } = { z: 'bar' };console.log(foo); // "bar"
在上面的例子中,我們計算變量鍵值并改變它的名稱為 foo
。
🎲2.8 同時使用數組和對象解構
在解構中數組和對象可以聯合使用:
const props = [{ id: 1, name: 'Fizz' },{ id: 2, name: 'Buzz' },{ id: 3, name: 'FizzBuzz' },
];const [, , { name }] = props;console.log(name); // "FizzBuzz"
所有的解構賦值語法是相同的,是在賦值符號左側聲明從源變量取出的值。舉例來說:
let x = [1, 2, 3, 4, 5];
let [y, z] = x;
console.log(y); // 1
console.log(z); // 2
🎲2.9 注意點
(1)如果要將一個已經聲明的變量用于解構賦值,必須非常小心。
// 錯誤的寫法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
上面代碼的寫法會報錯,因為 JavaScript 引擎會將{x}
理解成一個代碼塊,從而發生語法錯誤。只有不將大括號寫在行首,避免 JavaScript 將其解釋為代碼塊,才能解決這個問題。
// 正確的寫法
let x;
({x} = {x: 1});
上面代碼將整個解構賦值語句,放在一個圓括號里面,就可以正確執行。
(2)解構賦值允許等號左邊的模式之中,不放置任何變量名。因此,可以寫出非常古怪的賦值表達式。
({} = [true, false]);
({} = 'abc');
({} = []);
上面的表達式雖然毫無意義,但是語法是合法的,可以執行。
(3)由于數組本質是特殊的對象,因此可以對數組進行對象屬性的解構。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
上面代碼對數組進行對象解構。數組arr
的0
鍵對應的值是1
,[arr.length - 1]
就是2
鍵,對應的值是3
。方括號這種寫法,屬于“屬性名表達式”
🍟3 函數參數的解構賦值
函數的參數也可以使用解構賦值。
function add([x, y]){return x + y;
}add([1, 2]); // 3
上面代碼中,函數add
的參數表面上是一個數組,但在傳入參數的那一刻,數組參數就被解構成變量x
和y
。對于函數內部的代碼來說,它們能感受到的參數就是x
和y
。
下面是另一個例子。
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
函數參數的解構也可以使用默認值。
function move({x = 0, y = 0} = {}) {return [x, y];
}move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
上面代碼中,函數move
的參數是一個對象,通過對這個對象進行解構,得到變量x
和y
的值。如果解構失敗,x
和y
等于默認值。
注意,下面的寫法會得到不一樣的結果。
function move({x, y} = { x: 0, y: 0 }) {return [x, y];
}move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
上面代碼是為函數move
的參數指定默認值,而不是為變量x
和y
指定默認值,所以會得到與前一種寫法不同的結果。
undefined
就會觸發函數參數的默認值。
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
🍟4 解構的用途
變量的解構賦值用途很多。
🎲4.1 交換變量的值
let x = 1;
let y = 2;[x, y] = [y, x];
上面代碼交換變量x
和y
的值,這樣的寫法不僅簡潔,而且易讀,語義非常清晰。
🎲4.2 從函數返回多個值
函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里返回。有了解構賦值,取出這些值就非常方便。
// 返回一個數組function example() {return [1, 2, 3];
}
let [a, b, c] = example();// 返回一個對象function example() {return {foo: 1,bar: 2};
}
let { foo, bar } = example();
🎲4.3 函數參數的定義
解構賦值可以方便地將一組參數與變量名對應起來。
// 參數是一組有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);// 參數是一組無次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
🎲4.4 提取 JSON 數據
解構賦值對提取 JSON 對象中的數據,尤其有用。
let jsonData = {id: 42,status: "OK",data: [867, 5309]
};let { id, status, data: number } = jsonData;console.log(id, status, number);
// 42, "OK", [867, 5309]
上面代碼可以快速提取 JSON 數據的值。
🎲4.5 函數參數的默認值
jQuery.ajax = function (url, {async = true,beforeSend = function () {},cache = true,complete = function () {},crossDomain = false,global = true,// ... more config
} = {}) {// ... do stuff
};
指定參數的默認值,就避免了在函數體內部再寫var foo = config.foo || 'default foo';
這樣的語句。
JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript