ECMAScript是ECMA International定義的商標腳本語言規范。 創建它是為了標准化JavaScript。 ES腳本語言具有許多實現,流行的實現是JavaScript。 通常,ECMAScript用於萬維網的客戶端腳本。
ES5是ECMAScript 5的縮寫,也被稱為ECMAScript2009。ECMAScript標准的第六版是ES6或ECMAScript6。它也被稱為ECMAScript2015。ES6是JavaScript語言的主要增強,允許我們編寫程序。ES6適用於復雜的應用程序。盡管ES5和ES6在本質上有一些相似之處,但它們之間也有許多不同之處。
ES5和ES6之間的比較列表如下:
ES5和ES6之間的區別
| 比較項 | ES5 | ES6 |
|---|---|---|
| 定義 | ES5是ECMAScript(由ECMA International定義的商標腳本語言規范)的第五版。 | ES6是ECMAScript(ECMA International定義的商標腳本語言規范)的第六版。 |
| 發布 | 它於2009年推出。 | 它於2015年推出。 |
| 數據類型 | ES5支持原始數據類型,包括字符串,數字,布爾值,空值和未定義(undefined)。 |
在ES6中,對JavaScript數據類型進行了一些補充。 它引入了一種新的原始數據類型symbol以支持唯一值。 |
| 定義變量 | 在ES5中,只能使用var關鍵字定義變量。 |
在ES6中,有兩種定義let和const變量的新方法。 |
| 性能 | 由於ES5早於ES6,因此某些功能不存在,因此其性能比ES6低。 | 由於具有新功能和速記存儲實現,因此ES6具有比ES5更高的性能。 |
| 支持 | 許多社區都支持它。 | 它也有很多社區支持,但是比ES5小。 |
| 對象操縱 | ES5比ES6耗時。 | 由於具有解構和速度運算符,因此可以在ES6中更平穩地處理對象操縱。 |
| 箭頭函數 | 在ES5中,function和return關鍵字均用於定義函數。 |
箭頭功能是ES6中引入的新功能,通過它不需要function關鍵字來定義函數。 |
| 循環 | 在ES5中,使用了for循環來遍歷元素。 |
ES6引入了for?of循環的概念,以對可迭代對象的值執行迭代。 |
代碼轉換
到目前為止,還沒有完全支持ES6功能的瀏覽器。 但是,我們可以使用轉譯將ES6代碼轉換為ES5代碼。
有兩個主要的編譯器Babel和Traceur,用於在構建過程中將ES6代碼轉換為ES5代碼。
點差運算符(…)
它在ES6中引入,使合並數組和對象變得容易。
ES6常用特性:
一、let和const命令
1、let:
(1)基本用法
ES6 新增了let命令,用來聲明變量。類似於var,但是所聲明的變量,只在let命令所在的代碼塊內有效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
(2)let在for循環中使用
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[2](); // 10
a[6](); // 10
這個例子中,i 由 var 聲明,在全局范圍有效,a 的所有組員里面的 i 都是指向同一個 i,導致所有組員運行輸出的都是最后一輪的 i 的值。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[2](); // 2
a[6](); // 6
這個例子中, i 由 let 聲明,當前的 i 只在本輪循環有效,所有每次循環的 i 其實都是一個新的變量。
Tips: 每輪循環的 i 都是重新聲明的,JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的 i 時,就在上一輪循環的基礎上進行計算。
(3)for循環的特別之處
在 for 循環中,設置循環變量的那部分是一個父作用域,而循環內部是一個單獨的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc'; // 和上一行for循環計數器的i不在同一作用域,所以可以聲明成功。類比下面要講的function(arg){let arg;}會報錯,就是作用域問題
console.log(i);
}
// abc
// abc
// abc
(4)不能重復聲明
let 不允許在相同作用域內,重復聲明同一個變量。
function func() {
var a = 10;
let a = 1;
}
func(); //Identifier 'a' has already been declared
function func() {
let a = 10;
let a = 1;
}
func(); //Identifier 'a' has already been declared
function func(arg) {
let arg;
}
func(); //Identifier 'arg' has already been declared
function func(arg) {
{
let arg;
}
}
func(); //不報錯
(5)不存在變量提升
var 命令會發生“變量提升”現象,即變量可以在聲明之前使用,值為 undefined。 let 命令改變了語法行為,它所聲明的變量一定要在聲明后使用,否則會報錯。
console.log(foo); //undefined var foo = 2; console.log(bar); // ReferenceError: bar is not defined let bar = 2;
(6)暫時性死區
ES6規定,如果區塊中存在 let 和 const 命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前使用這些變量,就會報錯。這在語法上稱為 “暫時性死區”。
var tmp = 123;
if(true){
tmp = 'abc'; // ReferenceError: tmp is not defined
let tmp;
}
Tips:“暫時性死區”的本質就是:只要一進入當前作用域,所要使用的變量就已經存在了,但不可獲取(否則報錯),只有等到聲明的那一行代碼出現,才可以獲取和使用。
2、const:
const命令的用法和let相似,最大不同點就是:const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
Tips:這個不可改變的是指針,所以對於const聲明的對象和數組還是可以改變的。如果真的想將對象凍結,應該使用Object.freeze方法。
3、let和const部分補充:
ES5 只有兩種聲明變量的方法:var命令和function命令。
ES6 除了添加let和const命令,另外兩種聲明變量的方法:import命令和class命令。所以,ES6 一共有 6 種聲明變量的方法。
頂層對象:在瀏覽器環境指的是window對象,在 Node 指的是global對象。ES5 之中,頂層對象的屬性與全局變量是等價的。
ES6為了保持兼容性,規定:a. var 命令和 function 命令聲明的全局變量,依舊是頂層對象的屬性。b. let,const,class 命令聲明的全局變量,不屬於頂層對象的屬性。
var a = 1; window.a; // 1 let b = 2; window.b; // undefined
二、變量的解構賦值
ES6 允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構(Destructuring)。
1、數組的解構賦值
(1)基本用法
// 之前,為變量賦值,可以直接指定值 let a = 1; let b = 2; let c = 3; // ES6中允許這樣 let [a, b, c] = [1, 2, 3]; // 可以從數組中提取值,按照對應位置,對變量賦值。
本質上,這種寫法屬於“模式匹配”,只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值。下面是一些使用嵌套數組進行解構的例子。
let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [ , , third] = ["foo", "bar", "baz"]; third // "baz" let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ['a']; x // "a" y // undefined - 如果解構不成功,變量的值就等於 undefined。 z // []
如果解構不成功,變量的值就等於undefined。
上面一種情況是解構不成功的;另一種情況是不完全解構,即等號左邊的模式,只匹配一部分的等號右邊的數組。這種情況下,解構依然可以成功。
let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4
上面兩個例子,都屬於不完全解構,但是可以成功。
如果等號的右邊不是數組,那么將會報錯。
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
(2)默認值
解構賦值允許指定默認值。例如:
let [foo = true] = []; foo;// true let [x, y = 'b'] = ['a']; x;//'a' y;//'b'
Tips:ES6 內部使用嚴格相等運算符(===)去判斷一個位置是否有值。所以,只有當一個數組成員嚴格等於 undefined ,默認值才會生效。例如:
let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null - 默認值就不會生效,因為null不嚴格等於undefined
默認值可以引用解構賦值的其他變量,但該變量必須已經聲明。
let [x = 1, y = x] = [1, 2]; x;//1 y;//2 let [x = y, y = 1] = [];// ReferenceError: y is not defined -- let的暫時性死區 let [x = y, y = 1] = [1, 2];
2、對象的解構賦值
(1)基本用法
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
對象的解構與數組的解構,一個重要的不同點在於: 數組元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量名與屬性同名,才能取到正確的值。
如果沒有與變量名對應的屬性名,導致取不到值,最后等於undefined。
如果變量名和屬性名不一樣,必須寫才下面這樣:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
這實際上說明,對象的解構賦值是下面形式的簡寫:
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
也就是說,對象的解構賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。真正被賦值的是后者,而不是前者。
let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
上面代碼中,foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。
(2)默認值
對象的解構也可以指定默認值。默認值生效的條件是對象的屬性值嚴格等於 undefined。
let {x, y = 5} = {x: 1};
x;// 1
y;// 5
let { message: msg = 'Something went wrong' } = {};
msg;//"Something went wrong"
Tips:如果要將一個已經聲明的變量用於解構賦值,需要注意:
let x;
{x} = {x:1};
//上面的代碼會報錯,因為JavaScript引擎會將 {x} 理解為一個代碼塊,從而發生語法錯誤。只有不將大括號寫在行首,避免JavaScript將其解釋為代碼塊,才能解決這個問題。
//正確的寫法,將需要解構的部分用括號()包起來
let x;
({x} = {x:1});
3、字符串的解構賦值
字符串也可以解構賦值。這是因為此時,字符串被轉換成了一個類似數組的對象。
const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"
類似數組的對象都有一個 length 屬性,因此還可以對這個屬性解構賦值。
let {length: len} = 'hello';
len;// 5
4、數值和布爾值的解構賦值
解構賦值時,如果等號右邊是數值和布爾值,則會先轉化為對象:
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
上面代碼中,數值和布爾值的包裝對象都有 toString 屬性,因此變量 s 都可以取到值。
解構賦值的規則是:只要等號右邊的值不是對象或數組,就先將其轉化為對象。 由於 undefined 和 null 無法轉化為對象,所以對他們進行解構賦值都會報錯。
5、函數參數的解構賦值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
上面代碼中,函數 add 的參數看似是一個數組,但在傳入參數時,數組參數就解構為變量 x 和 y。對於函數內部代碼來說,參數就是 x 和 y。
函數參數的解構也可以使用默認參數:
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 指定默認值。
6、解構的用途
(1)交換變量的值
let x = 1; let y = 2; [x, y] = [y, x]; x;// 2 y;// 1
(2)從函數返回多個值
函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里面返回。有了解構賦值,取出這些值就非常方便。
//返回一個數組
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
a;//1
b;//2
c;//3
//返回一個對象
function example(){
return {
foo: 1,
bar: 2
}
}
let {foo, bar} = example();
foo;//1
bar;//2
(3)函數參數的定義
解構賦值可以方便的將一組參數與變量名對應起來。
//參數是一組有序值
function f([x, y, z]) {}
f([1, 2, 3]);
//參數是一組無序值
function f({x, y, z}) {}
f({x: 1, y: 2, z: 3});
(4)提取JSON數據
let data = {
id: 1,
status: 'ok',
data: [867, 5612]
};
let {id, status, data:number} = data;
console.log(id, status, number);// 1 "ok" [867, 5612]
(5)遍歷Map解構
const map = new Map();
map.set('first','hello');
map.set('second','world');
for (let [key, value] of map) {
console.log(key + ' is ' + value );
}
//first is hello
//second is world
// 獲取鍵名
for (let [key] of map) {}
// 獲取鍵值
for (let [,value] of map) {}
三、函數的默認參數
1、基本用法
ES6 之前,不能直接為函數的參數指定默認值,只能采用變通的方法。
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代碼檢查函數log的參數y有沒有賦值,如果沒有,則指定默認值為World。這種寫法的缺點在於,如果參數y賦值了,但是對應的布爾值為false,則該賦值不起作用。就像上面代碼的最后一行,參數y等於空字符,結果被改為默認值。
為了避免這個問題,通常需要先判斷一下參數y是否被賦值,如果沒有,再等於默認值。
if (typeof y === 'undefined') {
y = 'World';
}
ES6 允許為函數的參數設置默認值,即直接寫在參數定義的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello -- 上面說過,ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。所以,只有當一個數組成員嚴格等於 undefined ,默認值才會生效。
2、與解構賦值默認值結合使用
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
上面代碼只使用了對象的解構賦值默認值,沒有使用函數參數的默認值。只有當函數foo的參數是一個對象時,變量x和y才會通過解構賦值生成。
如果函數foo調用時沒提供參數,變量x和y就不會生成,從而報錯。
通過提供函數參數的默認值,就可以避免這種情況。
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
下面是另一個解構賦值默認值的例子:
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報錯
上面代碼中,如果函數fetch的第二個參數是一個對象,就可以為它的三個屬性設置默認值。
這種寫法不能省略第二個參數,如果結合函數參數的默認值,就可以省略第二個參數。這時,就出現了雙重默認值。
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
上面代碼中,函數fetch沒有第二個參數時,函數參數的默認值就會生效,然后才是解構賦值的默認值生效,變量method才會取到默認值GET。
因此,與解構賦值默認值結合使用時,切記,函數要定義默認參數,防止函數不傳參時報錯。
四、模板字符串
模板字符串是增強版的字符串,用反引號 (`) 標識。它可以當普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
1、基本用法
let greeting = `\`Yo\` World!`; // `Yo` World -- 使用反斜杠轉義反引號
// 多行字符串
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
Tips:
a. 如果在模板字符串中需要使用反引號,則前面需要使用反斜杠轉義。
b. 如果使用模板字符串表示多行字符串,所有的空格和縮進都會被保留在輸出之中。
2、在模板字符串使用變量
(1) 在模板字符串中嵌入變量,需要將變量名寫在 ${} 之中。
(2) 大括號內部可以放入任意的JavaScript表達式,可以進行運算,以及引用對象屬性。
(3) 模板字符串之中還能調用函數。
(4) 如果大括號中的值不是字符串,將按照一般的規則轉為字符串。如: 如果大括號中是一個對象,將默認調用對象的 toString 方法。
(5) 如果模板字符串中的變量沒有聲明將會報錯。
(6) 如果大括號內部是一個字符串,將原樣輸出。
//普通字符串
`In JavaScript '\n' is a line-feed.`;
`Hello ${'World'}`; // "Hello World"
//字符串中嵌入變量
let name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`); // Hello Bob, how are you today?
//字符串中嵌入 JavaScript 表達式
`${x} + ${y} = ${x + y}`;// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`;// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`;// "3"
// 字符串中嵌入函數
function fn(){
return "hello world"
}
`foo ${fn()} bar`;// "foo hello world bar"
// 字符串中嵌入對象
`${obj}`;// "[object Object]"
// 字符串中嵌入未聲明的變量
`Hello, ${place}`;// ReferenceError: place is not defined
五、箭頭函數
ES6 允許使用 “箭頭” (=>)定義函數。
1、基本用法
var f = v => v;
//等同於
var f = function (v) {
return v;
}
=> 前面的部分是函數的參數,=> 后面的部分是函數的代碼塊。
(1)如果箭頭函數不需要參數或需要多個參數,就使用一個圓括號代表參數部分。
var f = () => 5;
//等同於
var f = function (v) {
return 5
};
var sum = (num1, num2) => num1 + num2;
//等同於
var sum = function(num1, num2){
return num1 + num2;
}
(2)如果箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,並且使用 return 語句返回。
var sum = (num1,num2) => { return num1 + num2; }
(3)由於大括號會被解釋為代碼塊,所以如果箭頭函數直接返回一個對象,就必須在對象外面加上括號,否則會報錯。
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
getTempItem(1);//{id: 1, name: "Temp"}
let getTempItem = id => {
return { id: id, name: "Temp" };
}
getTempItem(1);// {id: 1, name: "Temp"}
(4)箭頭函數內部,可以嵌套箭頭函數
function insert (value) {
return {into: function (array){
return {after: function (afterValue) {
array.splice(array.indexOf(ahterValue)+1, 0, value);
return array;
}
}
}
}
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
//使用箭頭函數改寫
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue)+1, 0, value);
return array;
}
})
});
2、箭頭函數與變量解構結合使用
const full = ({ first, last }) => first + ' ' + last;
full({first: 1, last: 2});// "1 2"
// 等同於
function full(person) {
return person.first + ' ' + person.last;
}
full({first: 1, last: 2});// "1 2"
3、rest 參數與箭頭函數結合
const numbers = (...nums) => nums; numbers(1,2,3,4);// [1, 2, 3, 4] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1,2,3,4);// [1,[2,3,4]]
4、箭頭函數的優點
(1)簡化代碼
const isEven = n => n%2 == 0; isEven(3);// false isEven(4);// true
(2)簡化回調函數
[1, 2, 3].map( x => x*x ); //[1, 4, 9] [3,2,5].sort((a, b) => a - b); // [2, 3, 5]
5、箭頭函數的注意點——this
ES5中 this 的指向是可變的,但是箭頭函數中 this 指向固定化。
var handler = {
id: '123',
init: function () {
document.addEventListener('click',
event => this.doSomethisng(event.type), false);
},
doSomethisng: function (type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};
handler.init(); //Handling click for 123 -- this指向handle -- this是屬於函數的一個對象,誰調用指向誰(es5中)
this 指向的固定化,並不是因為箭頭函數內部有綁定 this 的機制,實際原因是箭頭函數內部根本沒有自己的this,導致內部的this 就是外層代碼塊的 this 。
箭頭函數的this是比較難理解的,但只要能想象到他是如何從ES5轉化來的,就可以完全理解它的作用對象。 箭頭函數轉成 ES5 的代碼如下:
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
Tips:
(1) 函數體內的 this 對象,就是定義時所在的對象,而不是使用時所在的對象。
(2) 不可以當構造函數,即不可以使用 new 命令,因為它沒有 this,否則會拋出一個錯誤。
(3) 箭頭函數沒有自己的 this,所以不能使用 call()、apply()、bind() 這些方法去改變 this 指向。
(4) 不可以使用arguments 對象,該對象在函數體內不存在。如果要用,可以使用rest參數代替。
六、數組的擴展(擴展運算符)
1、擴展運算符
(1)基本用法
擴展運算符(spread)是三個點(...)。它好比 rest 參數的逆運算,將一個數組轉為用逗號分隔的參數序列。
console.log(...[1, 2, 3]) // 1 2 3
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5
[...document.querySelectorAll('div')] // [<div>, <div>, <div>]
該運算符主要用於函數調用。擴展運算符與正常的函數參數可以結合使用,非常靈活。
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);
如果擴展運算符后面是一個空數組,則不產生任何效果。 [...[], 1] // [1]
注意,擴展運算符如果放在括號中,JavaScript 引擎就會認為這是函數調用,否則就會報錯。
(...[1,2]) // Uncaught SyntaxError: Unexpected number console.log((...[1,2])) // Uncaught SyntaxError: Unexpected number console.log(...[1,2]) // 不會報錯
2、替代函數的apply方法
由於擴展運算符可以展開數組,所以不再需要apply方法,將數組轉為函數的參數了。
// ES5 的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的寫法
function f(x, y, z) {
// ...
}
let args = [0, 1, 2];
f(...args);
3、擴展運算符引用
(1)復制數組
數組是復合的數據類型,直接復制的話,只是復制了指向底層數據結構的指針(淺拷貝),而不是克隆一個全新的數組。
如何實現深拷貝呢?下面看看ES5和ES6的實現:
// ES5 const a1 = [1, 2]; const a2 = a1.concat(); // ES6 const a1 = [1, 2]; // 寫法一 const a2 = [...a1]; // 寫法二 const [...a2] = a1; a2[0] = 2; a1 // [1, 2]
(2)合並數組
const arr1 = ['a', 'b']; const arr2 = ['c']; const arr3 = ['d', 'e']; // ES5 的合並數組 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6 的合並數組 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
不過,這兩種方法都是淺拷貝,使用的時候需要注意。如果修改了原數組的成員,會同步反映到新數組。
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];
a3[0] === a1[0] // true
a4[0] === a1[0] // true
(3)與解構賦值結合
const [first, ...rest] = [1, 2, 3, 4, 5]; first // 1 rest // [2, 3, 4, 5] const [first, ...rest] = []; first // undefined rest // [] const [first, ...rest] = ["foo"]; first // "foo" rest // []
注意:如果將擴展運算符用於數組賦值,只能放在參數的最后一位,否則會報錯。
const [...butLast, last] = [1, 2, 3, 4, 5]; // 報錯 const [first, ...middle, last] = [1, 2, 3, 4, 5]; // 報錯
(4)字符串
擴展運算符可以將字符串和函數參數arguments轉成真正的數組
[...'hello'] // [ "h", "e", "l", "l", "o" ]
function (){
let arr = [...arguments];
}
七、對象的擴展
1、屬性的簡潔表示法
ES6 允許直接寫入變量和函數,作為對象的屬性和方法。這樣的書寫更加簡潔。
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同於
const baz = {foo: foo};
上面代碼表明,ES6 允許在對象之中,直接寫變量。這時,屬性名為變量名, 屬性值為變量的值
屬性簡寫:
function f(x, y) {
return {x, y};
}
// 等同於
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
方法簡寫:
const o = {
method() {
return "Hello!";
}
};
// 等同於
const o = {
method: function() {
return "Hello!";
}
};
CommonJS 模塊輸出一組變量,就非常合適使用簡潔寫法。
let ms = {};
function getItem (key) {
return key in ms ? ms[key] : null;
}
function setItem (key, value) {
ms[key] = value;
}
function clear () {
ms = {};
}
module.exports = { getItem, setItem, clear };
// 等同於
module.exports = {
getItem: getItem,
setItem: setItem,
clear: clear
};
2、屬性名表達式
JavaScript 定義對象的屬性,有兩種方法。
// 方法一 obj.foo = true; // 方法二 obj['a' + 'bc'] = 123;
上面代碼的方法一是直接用標識符作為屬性名,方法二是用表達式作為屬性名,這時要將表達式放在方括號之內。
但是,如果使用字面量方式定義對象(使用大括號),在 ES5 中只能使用方法一(標識符)定義屬性。
var obj = {
foo: true,
abc: 123
};
ES6 允許字面量定義對象時,用方法二(表達式)作為對象的屬性名,即把表達式放在方括號內。
let propKey = 'foo';
let obj = {
[propKey]: true,
['a' + 'bc']: 123
};
表達式還可以用於定義方法名。比如我們熟悉的Vuex定義Mutation就是推薦這種方式:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
var test = {
'attr0' : 'attr1',
'attr1' : 'attr2',
'attr2' : 'attr3',
'attr3' : 'I'm here',
}
// 輸出: I'm here , 方括號可以接受任何JS語句,最后都被轉化為字符串。利用這個字符串在key-value集合中尋址。
console.log(test[test[test[test['attr0']]]]);
八、Array 的擴展方法
擴展運算符(展開語法):擴展運算符可以將數組或者對象轉為用逗號分隔的參數序列
let ary = [1, 2, 3]; ...ary // 1, 2, 3 console.log(...ary); // 1 2 3,相當於下面的代碼 console.log(1,2,3);
擴展運算符可以應用於合並數組
// 方法一 let ary1 = [1, 2, 3]; let ary2 = [3, 4, 5]; let ary3 = [...ary1, ...ary2]; // 方法二 ary1.push(...ary2);
構造函數方法:Array.from()
將偽數組或可遍歷對象轉換為真正的數組
//定義一個集合 let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; //轉成數組 let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
方法還可以接受第二個參數,作用類似於數組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數組
let arrayLike = { "0": 1, "1": 2, "length": 2 } let newAry = Array.from(arrayLike, item => item *2)//[2,4] 注意:如果是對象,那么屬性需要寫對應的索引
實例方法:find()
用於找出第一個符合條件的數組成員,如果沒有找到返回undefined
let ary = [{ id: 1, name: '張三' }, { id: 2, name: '李四' }]; let target = ary.find((item, index) => item.id == 2);//找數組里面符合條件的值,當數組中元素id等於2的查找出來,注意,只會匹配第一個
實例方法:findIndex()
用於找出第一個符合條件的數組成員的位置,如果沒有找到返回-1
let ary = [1, 5, 10, 15]; let index = ary.findIndex((value, index) => value > 9); console.log(index); // 2
實例方法:includes()
判斷某個數組是否包含給定的值,返回布爾值。
[1, 2, 3].includes(2) // true [1, 2, 3].includes(4) // false
arr.splice(index,len,[item])
表示從數組中刪除或者添加元素,返回新數組保存所有刪除的元素
index表示要操作的索引位置,len表示要刪除的個數,如果len的值為0表示不刪除,item表示要添加的元素,可以是多個
let ary = [10, 20, 50]; // 把20給刪除 // ary.splice(1, 1); // console.log(ary); // 把20刪除,替換成30 // ary.splice(1, 1, 30); // console.log(ary); // 在20前面加上15 // ary.splice(1, 0, 15); // console.log(ary); // 在50前面加上30,40 ary.splice(2, 0, 30, 40); console.log(ary);
arr.filter()
返回數組,包含了符合條件的所有元素。如果沒有符合條件的元素則返回空數組
// 返回數組,包含了符合條件的所有元素。如果沒有符合條件的元素則返回空數組 // let result = ary.filter(item => item > 150); // console.log(result)
String 的擴展方法
實例方法:repeat()
repeat方法表示將原字符串重復n次,返回一個新字符串
'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello"
Set 數據結構
ES6 提供了新的數據結構 Set。它類似於數組,但是成員的值都是唯一的,沒有重復的值。
Set本身是一個構造函數,用來生成 Set 數據結構
const s = new Set();
Set函數可以接受一個數組作為參數,用來初始化。
const set = new Set([1, 2, 3, 4, 4]);//{1, 2, 3, 4}
實例方法
add(value):添加某個值,返回 Set 結構本身
delete(value):刪除某個值,返回一個布爾值,表示刪除是否成功
has(value):返回一個布爾值,表示該值是否為 Set 的成員
clear():清除所有成員,沒有返回值
const s = new Set(); s.add(1).add(2).add(3); // 向 set 結構中添加值 s.delete(2) // 刪除 set 結構中的2值 s.has(1) // 表示 set 結構中是否有1這個值 返回布爾值 s.clear() // 清除 set 結構中的所有值 //注意:刪除的是元素的值,不是代表的索引
遍歷
Set 結構的實例與數組一樣,也擁有forEach方法,用於對每個成員執行某種操作,沒有返回值。
s.forEach(value => console.log(value))

