ES5和ES6的區別以及ES6常用特性


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中,有兩種定義letconst變量的新方法。
性能 由於ES5早於ES6,因此某些功能不存在,因此其性能比ES6低。 由於具有新功能和速記存儲實現,因此ES6具有比ES5更高的性能。
支持 許多社區都支持它。 它也有很多社區支持,但是比ES5小。
對象操縱 ES5比ES6耗時。 由於具有解構和速度運算符,因此可以在ES6中更平穩地處理對象操縱。
箭頭函數 在ES5中,functionreturn關鍵字均用於定義函數。 箭頭功能是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))

 

 

 

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM