深刻淺出ES6知識大合集

1.ES6的聲明方式

ES6一共有三種聲明方式:javascript

  1. var:是variable的縮寫,全局變量;
  2. let:局部變量;
  3. const:常量

var聲明
在ES6中,var被定義爲全局變量,咱們作個測試:在區塊中定義一個var的變量a,而後在區塊外看看可否打印出來。java

{
    var a = 1;
}
console.log(a);
複製代碼

你會發現,這個時候,a是能夠打印出來的,這就說明var定義的是全局變量。node

let聲明
let是ES6新引入的語法,是一個局部變量,咱們也作一個測試:在區塊中定義一個let變量a,看看在區塊外可否打印出來。es6

{
    let a = 1;
}
console.log(a);
複製代碼

你會發現,這個時候,瀏覽器會報 a is not defined的錯誤,說明了let確實定義的是一個局部變量,只能函數內或者區塊內訪問。正則表達式

const聲明算法

咱們在寫代碼的時候,可能有時候須要定義一個變量,這個變量在被定義之後,業務層就不會在對這個變量進行修改,簡單的說,咱們須要定義一個只聲明一次的常量。ES6很好的爲咱們作到了這一點,這就是const。json

注意一點,const定義的常量最好使用大寫字母,若是是兩個字母,用_分開,好比const PRODUCT_ID。咱們一樣作一個測試,看看const定義的常量是否能夠被更改。數組

const FORM_ID = 1;
FORM_ID =2;
複製代碼

這個時候你會發現,在編譯的過程當中就出錯了,會提示你"a" is read-only,意思很簡單,當你定義了const的常量之後,就不容許在去修改了。promise

2.變量的解構賦值

這是ES6的一個特別實用的功能,ES6容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構。解構賦值在實際開發中能夠大量減小咱們的代碼量,而且讓咱們的程序結構更清晰。瀏覽器

數組的解構賦值

之前,對變量賦值只能這樣寫:

var a = 1;
var b = 2;
var c = 3;
複製代碼

那麼如今,經過數組解構賦值的方式咱們能夠這樣簡化了:

let [a,b,c] = [1,2,3];
複製代碼

學過函數映射的同窗應該都能看懂,就是按照對應的位置關係進行賦值。注意,左邊和右邊必定要統一,否則會解構失敗。

let [[a,b],c,[d,e]] = [[1,2],3,[4,5]];
複製代碼

解構賦值可不止這麼點功能,它仍是能夠進行默認賦值的,那麼咱們繼續往下看:

解構的默認賦值

在看例子以前,咱們來解釋一下解構的默認賦值:當對變量解構賦值的時候,若是沒有賦值,或者賦值爲undefined,那麼變量就會讀取默認賦值。

let [a=1] = [];
複製代碼

這個時候,打印出來的a就是1.

let [a=1,b] = [undefined,2];
複製代碼

這個時候打印出來的a的值一樣是1.

注意:

let [a=1,b] = [null,2];
複製代碼

此時打印出來的a的值是null,而不是1,那麼這是爲何呢?緣由很簡單:ES6內部是使用嚴格相等運算符(===)判斷一個位置是否有值的。因此,若是一個數組成員不嚴格等於undefined,默認值是不會生效的。

對象的解構賦值

對象的解構賦值與數組有一個不一樣,數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。下面看個例子:

let {a,b,c} = {
    c:"3",
    a:"1",
    b:"2"
}
複製代碼

這個時候,輸出的a,b,c的值分別是1,2,3

其實,上面的寫法是對象解構賦值的簡寫,真正的寫法應該是:

let {
    a:a,
    b:b,
    c:c
} = {
    c:"3",
    a:"1",
    b:"2"
}
複製代碼

其實對象的解構賦值的內部機制,是先找到同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。若是以爲不太懂,能夠看下面的列子:

let {
    yuan:will
} = {
    yuan:1
}
複製代碼

這個時候,當你console.log(yuan)的時候,會報錯,說 yuan is not defined,而console.log(will),會發現will的值是1,如今明白了麼,是否是很簡單。

接下來,咱們來看看二次賦值的一個坑,上代碼:

let a;
{a} = {a:1};
複製代碼

這樣在編譯的時候就報錯了,緣由就是解析器會將起首的大括號,理解成一個代碼塊,而不是賦值語句,解決方法很簡單,用()包起來就能夠了。

let a;
({a} = {a:1});
複製代碼

固然啦,對象解構賦值也是能夠設置默認值的,下面列一個很簡單的默認賦值的demo:

let {a=1,b,c} ={
    a:undefined,
    c:3
}
複製代碼

這個時候,打印出來的a,b,c分別是1,undefined,3,因此看出來了麼,當你定義了變量,可是並無賦值的時候,是不報錯的,而是賦值爲undefined(b)

字符串的解構賦值

這個用的很少,因此我這個小菜雞也沒怎麼去特別研究,寫個例子吧:

const [A,B,C,D] = 'will';
複製代碼

這個時候打印出來的A,B,C,D的值分別爲w,i,l,l

用途

解構賦值除了對對象賦值之外,仍是有蠻多其餘用途的,下面列一些大神總結的吧。

  • 交換變量的值

    [x,y] = [y,x];
    複製代碼

  • 從函數返回多個值

    函數只能返回一個值,若是要返回多個值,只能將它們放在數組或對象裏返回。有了解構賦值,取出這些值就很是方便。

    // 返回一個數組
    function example() { 
      return [1, 2, 3];
    }
    var [a,b,c] = example();
    
    // 返回一個對象
    function example() { 
      return { a: 1, b: 2 };
    }
    var {a,b} = example();
    複製代碼

    這個函數方式用的仍是老的,後面的我都會用es6的語法,若是你們看不懂不要緊,能夠直接跳轉先看箭頭函數那一塊知識,再回頭看前面內容。


  • 函數參數的定義

    解構賦值能夠方便地將一組參數與變量名對應起來。

    //參數是一組有序的值
    let example =([a,b,c]) =>{
        ...
    }
    example([1,2,3]);
    
    //參數是一組無序的值
    let example =({a,c,b}) =>{
        ...
    }
    example({a:1,b:2,c:3});
    複製代碼

  • 提取JSON數據

    let jsonData = {
        a:1,
        b:"2",
        c:[1,2]
    }
    let {a,b,c} = jsonData;
    複製代碼

還有一些用途我就不一一列舉了,感興趣的同窗能夠上網找。

3.擴展運算符和reset運算符

擴展運算符

如今有個問題問你們,如今要編寫一個方法,它的參數是不肯定的,若是用es5的來寫,是否是很複雜,可是es6輕鬆就能解決這個問題,那就是擴展運算符!

let example =(...arg) {
    console.log(arg[0]);
    console.log(arg[1]);
    console.log(arg[2]);
    console.log(arg[3]);
}
example(1,2,3);
複製代碼

這個時候打印出來的會是1,2,3,undefined。這樣就太好了,即便傳值少了,也不會報錯,只是打印undefined.

那麼擴展運算符又有什麼用處呢,下面咱們來看看:

1.數組的深度拷貝

首先來看一個es5的demo:

let arrOne = [1,2,3];
let arrTwo = arrOne;
arrTwo.push(4);
console.log(arrOne);
複製代碼

這個時候你會發現,打印出來的arrOne是[1,2,3,4],這不是咱們想要的,因此呢,咱們就能夠用擴展運算符解決,往下看:

let arrOne = [1,2,3];
let arrTwo = [...arrOne];
arrTwo.push(4);
console.log(arrOne);
複製代碼

那麼如今打印出來的arrOne就是[1,2,3],完美解決!

2.字符串轉換數組

let str = 'will';
let arr = [...str];
console.log(arr);
複製代碼

那麼打印出來的數組就是['w','i','l','l'];

reset運算符

reset運算符也是經過...來表示,它和擴展運算符很是類似,你不須要去特別的區分。

let example =(a,...arg) => {
    for(var item of arg) {
        console.log(item);
    }
}
example(1,2,3,4,5,6);
複製代碼

這個時候打印臺會打印出2,3,4,5,6,for..of循環後面會有介紹,暫時你們能夠理解成一個循環。

4.字符串模板和標籤模板

字符串模板

首先,咱們先看一下之前咱們是怎麼實現字符串拼接的:

let will = 'yuan';
let str = '歡迎您,' + yuan + ',咱們的好朋友';
複製代碼

很顯然,這樣的方式既麻煩又容易出錯,特別是在進行復雜的字符串拼接的時候,ES6的字符串模板就很好的解決了這個問題,咱們如今來看看ES6的字符串模板是怎麼作的:

let will = 'yuan';
let str = `歡迎您,${will},咱們的好朋友`;
複製代碼

除了支持變量插值,字符串模板仍是支持簡單的計算的:

let str =`1+2的值是:${1+2};`
複製代碼

這樣打印出來的就是:1+2的值是:3

其實這只是很簡單的兩個案例,對於${}中的數據結構,不只僅能夠是變量,還能夠放入任意的JavaScript表達式,能夠進行運算,以及引入對象屬性。咱們看下面一個例子。

let example =(arr) => `
    <ul>
    ${
        arr.map((data) =>{
            return`<li>${data}</li>`
        }).join('')
    }
    </ul>
`
let node = example([1,2,3]);

document.write(node);
複製代碼

這樣打印出來的是什麼呢,會是

那麼提個小問題,我爲何在後面加上join()方法呢,你們能夠想一想。

標籤模板

標籤模板其實並非一個模板,和字符串模板是徹底不同的,你們千萬不要按照字符串模板的思惟去理解。標籤模板是函數的一種特殊的調用形式,緊跟在後面的字符串模板是這個函數的參數,你們不理解不要緊,咱們先看一個實例:

let a = 1;
let b = 2;
example`我是${a+b},你是${b-a*5}`
function example(arr,a,b){
    console.log(arr);
    console.log(a);
    console.log(b);
}
複製代碼

先解釋一下:這個example函數的第一個參數是一個數組,數組則是被變量分隔開來的每一個字符串,後面的參數則對應每一個字符串模板當中的變量,那麼打印出來的結果就顯而易見了:

須要注意的是,第一個打印出來的arr是三個數據,後面會有一個空字串,理由很簡單,我就不解釋了。

下面,咱們來看一個複雜的例子,也是標籤模板比較有用的一個用途,就是防止用戶的惡意輸入。

function SaferHTML(templateData){
    let s = templateData[0];//templateData取的是第一個參數,那麼就是arr數組,第一個那就是<p>
    let i;
    for(i = 1;i<arguments.length;i++){ //arguments數組包含的是所有的參數,在這個例子中,也就是[arr數組,變量];
        var arg = String(arguments[i]);
        s += arg.replace(/&/g,'&amp;')
                .replace(/</g,'&lt;')
                .replace(/>/g,'&gt;');
        s += templateData[i];
    }
    return s;
}
let sender = '<script>alert("abc")</script>';
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
console.log(message);
複製代碼

這個例子上面的標註都已經說明了,你們能看得懂麼?看不懂仔細鑽研哦

字符串查找

在ES6以前,咱們都是使用indexOf()來查找字符串是否存在,indexOf()的強大並不純粹只是字符串的查找,因此咱們缺乏了純粹的字符串查找的方法,很幸運,ES6一共提供了三種方式:

  1. includes():該方法在給定文本存在於字符串中的任意位置時會返回 true ,不然返回false;
  2. startsWith():該方法在給定文本出如今字符串起始處時返回 true ,不然返回 false;
  3. endsWith():該方法在給定文本出如今字符串結尾處時返回 true ,不然返回 false;

這三個方法都提供了兩個參數:

第一個參數:須要搜索的文本;

第二個參數:includes() 與 startsWith() 方法會從該索引位置(參數)開始嘗試匹配;而endsWith() 方法則從字符串長度減去這個索引值的位置開始嘗試匹配;

下面,咱們看看例子:

var a = "Hello world!";
console.log(a.includes("o")); // true
console.log(a.startsWith("Hello")); // true
console.log(a.endsWith("!")); // true

console.log(a.includes("o", 8)); // false
console.log(a.startsWith("o", 4)); // true
console.log(a.endsWith("o", 5)); // true
複製代碼

這個很是簡單,相信你們也都能看得懂,可是有兩點須要注意:

  1. 雖然這三個方法使得判斷子字符串是否存在變得更容易,也更加純粹,但它們只返回了一個布爾值。若須要找到它們在字符串中的確切位置,則須要使用 indexOf() 和 lastIndexOf() ;
  2. 若是向 startsWith() 、 endsWith() 或 includes() 方法傳入了正則表達式而不是字符串,會拋出錯誤。而對於indexOf()和lastIndexOf()這兩個方法,它們會將正則表達式轉換爲字符串並搜索它

字符串複製

有的時候,一個字符串不止使用一次,須要重複使用,那麼就須要用到字符串複製了,咱們來看:

let a = '*';
console.log(a.repeat(3));
console.log(a);
複製代碼

注意一點:repeat()方法是不改變變量的,也就是並不會改變變量a的值

5.ES6增長的數字操做

在ES5中,有幾個數字操做的全局函數,相信你們都比較熟悉,分別是isNaN(),isFinite(),parseInt(),parseFloat()。這些方法在ES5中都是window對象下的方法,在ES6中已經屬於Number對象下了,如今咱們分別看一下區別:

isNaN()

首先,在ES5中,有兩種寫法:

isNaN(2) //false
window.isNaN(2) //false
複製代碼

以上兩種寫法均可以,由於isNaN自己就是window對象下的一個方法,大部分人都會用第一種。如今來對比一下ES5和ES6下的isNaN()的區別:

ES5:會首先把非數值的參數轉換成數值類型,再進行判斷。

ES6:不會強制將參數轉換成數字,只有在參數是真正的數字類型,且值爲 NaN 的時候纔會返回 true。

這個若是你們不理解不要緊,咱們先看例子:

isNaN('a')                //true 由於a沒法裝換成數字,因此返回true
Number.isNaN('a')         //false a是一個字符串,直接返回false

Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true


// 下面這幾個若是使用全局的 isNaN() 時,會返回 true。
Number.isNaN("NaN");      // false,字符串 "NaN" 不會被隱式轉換成數字 NaN。
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false

// 下面的都返回 false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");
複製代碼

相信經過上面那麼多例子,你們差很少能明白了,那麼爲何ES6要這樣幹呢?由於在 JavaScript 中,NaN 最特殊的地方就是,咱們不能使用相等運算符(== 和 ===)來判斷一個值是不是 NaN,由於 NaN == NaN 和 NaN === NaN 都會返回 false。所以,必需要有一個判斷值是不是 NaN 的方法,那麼Number.isNaN()方法就很幸運的被選中了!

isFinite()

這個方法和isNaN()在ES5和ES6中的區別特別相似:

ES5:會首先把非數值的參數轉換成數值類型,再進行判斷。

ES6:不會強制將一個非數值的參數轉換成數值,這就意味着,只有數值類型的值,且是有窮的(finite),才返回 true。

一樣咱們看個例子:

isFinite('0')                    //true,會先轉換成數字0,0是有窮的,因此返回false
Number.isFinite('0')             //false,'0'是字符串,直接返回false
Number.isFinite(Infinity);       //false,Infinity是window對象下的一個常量,表示一個無窮數
複製代碼

parseInt()

這個方法是函數解析一個字符串參數,並返回一個指定基數的整數,parseInt函數一樣是從window對象下移植到Number對象下,可是它的做用沒有任何變化。

parseInt()方法一共有兩個參數:

第一個參數:要被解析的值。若是參數不是一個字符串,則將其轉換爲字符串(使用ToString抽象操做)。字符串開頭的空白符將會被忽略。

第二個參數:一個介於2和36之間的整數(數學系統的基礎),表示上述字符串的基數。好比參數"10"表示使用咱們一般使用的十進制數值系統。始終指定此參數能夠消除閱讀該代碼時的困惑而且保證轉換結果可預測。當未指定基數時,不一樣的實現會產生不一樣的結果,一般將值默認爲10。

parseInt('12.3abc');         //返回12
Number.parseInt('12.3abc');  //返回12
複製代碼

parseFloat()

和parseInt()同樣,被移植到Number對象下,做用保持不變。

parseFloat()只有一個參數,就是要被解析的字符串。

parseFloat('12.3abc');            //返回12.3
Number.parseFloat('12.3abc');     //返回12.3
複製代碼

上述那些方法都是es5自己就有,從全局函數轉到了Number身上,下面,咱們看看ES6新出的一些方法和新定義的一些常量,首先,咱們來看ES6對Number對象的擴展。

Number.isInteger()

這個函數很簡單,用來判斷一個數是否爲整數,可是有兩點須要注意:

1.只有傳入的自己就是數值,纔會判斷是不是整數,若是傳入的是字符串或者數組等其餘類型,是不會強制轉換的,是直接返回false。

2.在javascript內部對整數和浮點數採用同樣的存儲方式,所以小數點後若是都是0的浮點數,都會被認爲是整數

Number.isInteger(0)          //true
Number.isInteger(-1)         //true
Number.isInteger(2.3)        //false
Number.isInteger(2.0)        //true
Number.isInteger("10");      // false
Number.isInteger(true);      // false
Number.isInteger(false);     // false
Number.isInteger([1]);       // false
複製代碼

除了上述兩點之外,還有一點,不是這個方法產生的問題,可是輸出的結果會讓你很奇怪,咱們來看案例:

Number.isInteger(Number.MAX_SAFE_INTEGER - 1.2)   //true
Number.isInteger(0.09+0.01)                       //false
複製代碼

其實輸出這樣的結果,並非這個方法出現了問題,而是Number.MAX_SAFE_INTEGER - 1.2最後獲得的結果是9007199254740990,而0.09+0.01 獲得的結果是0.09999999999999999。爲何會這樣呢?由於計算機是用二進制來存儲和處理數字,不能精確表示浮點數,而JavaScript中沒有相應的封裝類來處理浮點數運算,直接計算會致使運算精度丟失


Number.MAX_SAFE_INTEGER Number.MIN_SAFE_INTEGER

這兩個因爲是一個系列的,因此一塊兒介紹,在介紹這兩個常量以前,得先解釋一下什麼是安全整數和非安全整數。

因爲JavaScript可以精確表示的整數範圍在-2^53到2^53之間(不包含2^53和-2^53),超過這個範圍,JavaScript就沒法精確的表示,因此這個範圍內的整數被稱爲安全整數,不在這個範圍內的則稱爲非安全整數。

對此,ES6對這個範圍的邊界定義了兩個常量,最大值是:Number.MAX_SAFE_INTEGER,最小值是:Number.MIN_SAFE_INTEGER。 也就是:

Number.MAX_SAFE_INTEGER === Math.pow(2,53)-1      //true
Number.MIN_SAFE_INTEGER === -Math.pow(2,53)+1     //true
複製代碼

Number.isSafeInteger()

Number.isSafeInteger()函數就是用來判斷數值是不是安全整數,只有當傳入的數值是數值而且是安全整數,纔會返回false

Number.isSafeInteger(Number.MAX_SAFE_INTEGER);      //true
Number.isSafeInteger(Number.MIN_SAFE_INTEGER);      //true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1);  //false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1);  //false
Number.isSafeInteger('1');                          //false
複製代碼

Number.EPSILON

這是一個常量,表示極小極小的數,它的值爲:2.220446049250313e-16,這個值的出現是爲了用來判斷浮點數的計算偏差,若是浮點數計算獲得的偏差不超過Number.EPSILON的值,就表示能夠接受這樣的偏差。

Number.EPSILON    //2.220446049250313e-16
複製代碼

ES6不只僅是對Number對象作了擴展,對Math對象也作了擴展,下面咱們來看看:

Math.trunc()

這個方法首先會把傳入的參數自動轉化成數字類型,刪除掉數字的小數部分和小數點,無論它是正數仍是負數。

Math.trunc(13.37)    // 13
Math.trunc(42.84)    // 42
Math.trunc(0.123)    //  0
Math.trunc(-0.123)   // -0
Math.trunc("-1.123") // -1
Math.trunc(NaN)      // NaN
Math.trunc("foo")    // NaN
Math.trunc()         // NaN
複製代碼

Math.sign()

這個方法首先會把傳入的參數自動轉化成數字類型,而後判斷這個數是整數仍是負數仍是零,這個方法一共有五個返回值,分別是:1, -1, 0, -0, NaN

Math.sign(3);     //  1
Math.sign(-3);    // -1
Math.sign("-3");  // -1
Math.sign(0);     //  0
Math.sign(-0);    // -0
Math.sign(NaN);   // NaN
Math.sign("foo"); // NaN
Math.sign();      // NaN
複製代碼

Math.cbrt()

這個方法首先會把傳入的參數自動轉化成數字類型,而後算出這個數的立方根

Math.cbrt(NaN); // NaN
Math.cbrt(-1); // -1
Math.cbrt(-0); // -0
Math.cbrt(-Infinity); // -Infinity
Math.cbrt(0); // 0
Math.cbrt(1); // 1
Math.cbrt(Infinity); // Infinity
Math.cbrt(null); // 0
Math.cbrt(2);  // 1.25992104989487
複製代碼

ES6除了給Math擴展了這三個函數外,還有不少,基本是高中數學課本的一些方法,你們有興趣能夠本身去研究,這裏就不一一列舉了。

對於數字運算,ES6還增長了一種運算符,即指數運算符**

3 ** 2  //9
3 ** 3  //27

let a = 1.5;
a **= 2; //2.25
複製代碼

6.ES6新增的數組知識

ES5的數組已經很是強大了,可是ES6依然增長了一些頗有用的數組方法,咱們挨個看一下。

Array.from()

Array.from()方法能夠經過兩種方式來建立數組:

1.僞數組對象(擁有一個length屬性和若干個索引屬性的對象,例如arguments)

2.可迭代對象(能夠獲取對象中的元素,例如 Map和 Set )

該方法一共有三個參數:

1.想要轉換成數組的僞數組對象或可迭代對象

2.若是指定了該參數,新數組中的每一個元素會執行該回調函數

3.可選參數,執行回調函數 (第二個參數) 時 this 對象

其實,Array.from()方法只是後面有一個可選的回調函數mapFn,讓你能夠在最後生成的數組上再執行一次 map 方法後再返回。也就是說 Array.from(obj, mapFn, thisArg) 就至關於 Array.from(obj).map(mapFn, thisArg)。 咱們來看幾個例子:

1.經過僞數組對象建立新數組

function example() {
    console.log(Array.from(arguments))
}
example('s','d','e')                //["s", "d", "e"]
複製代碼

2.經過可迭代對象建立新數組

let s = new Set(['foo', window]); 
Array.from(s);                      // ["foo", window]

Array.from('1234');                 // ["1", "2", "3", "4"] 
Array.form(1234);                   // []
複製代碼

3.給數組傳入第二個參數

function example() {
    console.log(Array.from(arguments,value => value + 2))
}
example(1,2,3)                      // [3, 4, 5]
複製代碼

Array.of()

Array.of() 方法建立一個具備可變數量參數的新數組實例,而不考慮參數的數量或類型。

Array.of()和Array的構造函數很是類似,而Array.of()的出現也是爲了解決Array的一個困惑(或者說是特性) => 在使用Array的構造函數的時候,當咱們new一個數字的時候,生成的是這個數字長度的數組,每一個位置的值都是undefined,而咱們new一個字符串的時候,又會生成一個該字符串爲元素的數組。

const a = new Array('2')   // ['2']
const b = new Array(2)     // [undefined,undefined]
複製代碼

而對於Array.of(),不管傳入的是字符串仍是數字,都會生成一個該參數爲元素的數組。

const a = Array.of('2')   // ['2']
const b = Array.of(2)     // [2]
const b = Array.of(2,3,4) // [2,3,4]
複製代碼

find()實例方法

數組實例的find方法,用於找出第一個符合條件的數組成員。它的參數是一個回調函數,全部數組成員依次執行該回調函數。

這個回調函數有三個參數,依次爲當前的值、當前的位置和原數組。咱們來看實例:

注意,我看不少書裏都說這個方法會返回符合條件的第一個值,若是找不到符合條件的則返回undefined,可是我寫了幾個demo發現並不會返回,除非本身去手動return,你們能夠一塊兒探討,咱們來看寫的案例。

[1,2,3,4,5].find(val => val > 3)     //4
複製代碼

這個會返回4,緣由是由於箭頭函數的特性,若是隻寫一行而且沒有使用{},則默認增長return,咱們再看下面例子就明白了:

let a = [1,2,3,4,5].find(val => {val > 3})   //undefined
複製代碼

這個時候我增長了{},並無去return,那麼打印出來的就是undefined,說明沒有去自動返回,而是須要主動返回,我猜想find的機制只是將你的數組循環出來,而後回調函數根據循環出來的元素本身去處理需求,當遇到return的時候,數組循環終止,返回這時候循環進去的value值(注意:並不會返回你本身return的值),咱們來看一個例子:

let a = [1,2,3,4,5].find((val,index) => {
    if(val < 3) {
        console.log('val:' + val)
    }else {
        return {
            'val' : val,
            'index' : index
        }
    }
})

console.log(a) //分別打印val:1  val:2  3
複製代碼

從這個例子能夠看出來,find()方法是在程序終止的時候,返回終止的那一刻的value值。

findIndex()實例方法

findIndex()方法和find()方法幾乎同樣,只是返回值變成了元素在數組的下標,就不詳細介紹了,看個例子:

let a = [1,2,3,4,5].findIndex((val,index) => {
    if(val < 3) {
        console.log('val:' + val)
    }else {
        return {
            'val' : val,
            'index' : index
        }
    }
})

console.log(a) //分別打印val:1  val:2  2
複製代碼

fill()實例方法

fill()方法是使用給定值去填充數組,該方法一共有三個參數,依次是:填充的值,起始填充位置,結束填充位置。

[1,2,3,4].fill('a')        // ["a", "a", "a", "a"]
[1,2,3,4].fill('a',1,3)    // [1, "a", "a", 4]
複製代碼

includes()

這個方法與字符串的includes方法特別類似,只是把對字符串的匹配改爲了對數組的匹配,因此就不詳細介紹了,給你們一個例子吧。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true
複製代碼

7.遍歷

因爲遍歷是一個很是重要的知識點,因此單獨抽出來說一講,並不侷限於ES6新增的遍歷方法。

1.for循環

這個是最基礎也是ES5最經常使用的循環,這個太簡單太經常使用了,就不解釋了。

2.for..in 循環

for..in循環是專門爲普通對象定義的循環,雖然能夠用來循環數組,可是強烈建議必定不要用for..in循環去循環數組,必定不要,緣由你們能夠去百度,我這裏就不一一闡述了。

3.forEach 循環

forEach循環是ES5中循環數組的方法,該方法有兩個參數:

第一個參數:必需,數組中每一個元素須要調用的函數,這個函數有三個參數,依次是:遍歷的數組內容,數組的索引,原數組。

第二個參數:可選,傳遞給函數的值通常用 "this" 值。若是這個參數爲空, "undefined" 會傳遞給 "this" 值。

let arr = [1,2,3,4];
arr.forEach((value,index,arr)=>{
    arr[index] = value * 2;
});
console.log(arr); // [2, 4, 6, 8]
複製代碼

注意:使用forEach遍歷數組的話,使用break不能中斷循環,使用return也不能返回到外層函數。

let arr = [1,2,3,4];
arr.forEach((value,index,arr)=>{
    if(value === 3) {
        return;
    }
    arr[index] = value * 2;
});
console.log(arr); // [2, 4, 3, 8] 繼續執行了value = 4的狀況
複製代碼

4.map循環

map循環和forEach循環很是類似,都是用來遍歷數組中的每一項值的,不一樣之處在於:

map的回調函數中支持return返回值;return的是啥,至關於把數組中的這一項變爲啥(並不影響原來的數組,只是至關於把原數組克隆一份,把克隆的這一份的數組中的對應項改變了)

let arr = [1,2,3,4];
let newArr = arr.map((value,index,arr) => {
    return value * 2;
})
console.log(newArr); // [2, 4, 6, 8]
複製代碼

5.filter循環

filter()函數實際上是循環原數組裏的每一個元素,而後篩選出符合條件的元素,造成一個新的數組,參數和forEach()以及map()如出一轍。

let arr = [1,2,3,4];
let newArr = arr.filter((value,index,arr) => {
    return value % 2 == 0;
})
console.log(newArr); // [2,4]
複製代碼

注意:必須return 不然獲得的是空數組。

6.for..of 循環

上面的5個循環都是ES5具備的,而for..of循環是ES6新增的,也是最強大的一個循環,你們只須要明白幾點:

  1. 這是最簡潔、最直接的遍歷數組元素的語法
  2. 這個方法避開了for-in循環的全部缺陷
  3. 與forEach()不一樣的是,它能夠正確響應break、continue和return語句

總的來講,遍歷數組,用for..of就對了!

首先,咱們具體來看看for..of如何遍歷數組的:

let arr = ['yuan','will','js','es6'];
for(let value of arr) {
    console.log(value); // yuan will js es6
}
複製代碼

這個是最簡單的方式,增長break試試:

let arr = ['yuan','will','js','es6'];
for(let value of arr) {
    if(value === 'js') {
        break;
    }
    console.log(value); // yuan will
}
複製代碼

這個結果說明是能夠經過break跳出循環的,不只如此,咱們還能夠經過數組的keys()方法來獲取索引,經過數組的entrie()方法來獲取鍵值對:

循環keys()獲取索引

let arr = ['yuan','will','js','es6'];
for(let key of arr.keys()) {
    console.log(key); // 0 1 2 3
}
複製代碼

循環entries()獲取鍵值對

let arr = ['yuan','will','js','es6'];
for(let [key,value] of arr.entries()) {
    console.log('key值:' + key + '; value值:' + value);
}
複製代碼

打印出來的結果以下:

for-of循環不只支持數組,還支持大多數類數組對象,例如DOM NodeList對象。同時也支持Map對象,Set對象(ES6新增對象,後面會講到),固然也支持字符串的遍歷,這些就不一一列舉了。只須要記住:將來的JS可使用一些新型的集合類,甚至會有更多的類型陸續誕生,而for-of就是爲遍歷全部這些集合特別設計的循環語句。

有一點必定要注意:for-of循環不支持普通對象,但若是你想迭代一個對象的屬性,你能夠用for-in循環(這也是它的本職工做)或內建的Object.keys()方法:

let person = {
    id : 1,
    age : 18,
    name : 'will',
    address : 'shenzhen',
}

for (let key of Object.keys(person)) {
    console.log(key + "是:" + person[key]);
}
複製代碼

打印出來的結果:

8.函數及其擴展

函數參數的默認值

ES5中,咱們並不能給函數的參數設置默認值,這讓咱們一直很痛苦,咱們來看咱們之前是怎麼作的:

function example(name) {
    name = name || 'yuan';
    console.log(name);
}

example('will');      // will
example();            // yuan 
example('');          // yuan =>這個結果並非咱們想要的
example(undefined);   // yuan
複製代碼

這樣寫的缺點相信你們都能看出來也經歷過,就是參數若是咱們賦值了,只是對應的布爾值是false,那麼咱們對應的賦值並不起做用,因此咱們又從新修改了代碼:

function example(name) {
    if (typeof name === 'undefined') {
        name = 'yuan';
    }
    console.log(name);
}

example('will');        // will
example();              // yuan
example('');            // ''
example(undefined);     // yuan
複製代碼

這樣就達到了目的,可是顯得很麻煩,由於咱們的要求很簡單,只是參數默認賦值而已,ES6爲咱們作到了:

function example(name = 'yuan') {
    console.log(name);
}
example('will');        // will
example();              // yuan
example('');            // ''
example(undefined);     // yuan
複製代碼

注意:函數的參數默認賦值並非傳值方式的,而是每次都從新計算默認值表達式的值。也就是說,參數默認值是惰性求值的。

let x = 1;
function example(p = x + 1) {
    console.log(p);
}

example();    // 2

x = 100;
example();    // 101
複製代碼

函數的length屬性

若是一個函數的某個參數具備默認值,那麼函數將計入的length的數量爲這個參數以前的沒有設置默認屬性的參數個數。看起來很難讀懂,其實很簡單,咱們看個例子:

(function(a){}).length         // 1
(function(a=1){}).length       // 0
(function(a,b=1,c){}).length   // 1
複製代碼

第一個函數,並無設置參數默認值,有一個參數,因此返回1;第二個函數,設置了參數默認值且沒有不設置默認值的參數,返回0;第三個,b設置了默認參數,b前面有一個沒有設置默認參數的參數a,返回1(注意,並不會去計算b後面的c)。

函數的name屬性

函數的name屬性,返回該函數的函數名。

function example() {}
console.log(example.name); // example
複製代碼

這個屬性早就被瀏覽器普遍支持,可是直到 ES6,纔將其寫入了標準。

須要注意的是,ES6 對這個屬性的行爲作出了一些修改。若是將一個匿名函數賦值給一個變量,ES5 的name屬性,會返回空字符串,而 ES6 的name屬性會返回實際的函數名。

var example = function () {}

//ES5
console.log(example.name);  // ''

//ES6
console.log(example.name);  // example
複製代碼

若是將一個具名函數賦值給一個變量,則 ES5 和 ES6 的name屬性都返回這個具名函數本來的名字。

var exa = function example () {}

//ES5
console.log(exa.name);   // example

//ES6
console.log(exa.name);   // example
複製代碼

箭頭函數

箭頭函數是ES6更新的很是重要的一個功能,咱們來看一下基本用法:

var example = value => value;
複製代碼

上面的箭頭函數等同於:

var example = function(value) {
  return value;
};
複製代碼

若是箭頭函數不須要參數或須要多個參數,就使用一個圓括號表明參數部分。

var example = () => 5;
// 等同於
var example = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同於
var sum = function(num1, num2) {
  return num1 + num2;
};
複製代碼

若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,而且使用return語句返回。

var sum = (num1, num2) => { return num1 + num2; }
複製代碼

因爲大括號被解釋爲代碼塊,因此若是箭頭函數直接返回一個對象,必須在對象外面加上括號,不然會報錯。

// 報錯
let getTempItem = id => { id: id, name: "Temp" };

// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
複製代碼

若是箭頭函數只有一行語句,且不須要返回值,能夠採用下面的寫法,就不用寫大括號了。

let example = () => 5;
複製代碼

箭頭函數有幾個使用注意點。

(1)函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

(2)不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。

(3)不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替。

(4)不可使用yield命令,所以箭頭函數不能用做 Generator 函數。

Object.is()方法

這個方法是用來進行對象比較的方法,固然啦,也能夠進行其餘值的比較,咱們來對比一下它和==以及===的區別:

==: 等同,比較運算符,兩邊值類型不一樣的時候,先進行類型轉換,再比較;

===: 恆等,嚴格比較運算符,不作類型轉換,類型不一樣就是不等,也就是同值相等;

Object.is(): 比較兩個值是否嚴格相等的方法,只有嚴格同樣才返回true;

let obj1 = {name:'yuan'};
let obj2 = {name:'yuan'};
obj1.name === obj2.name;            // true
Object.is(obj1.name,obj2.name);     // true
-0 === +0;                          // true
NaN === NaN;                        // false
Object.is(-0,+0);                   // false
Object.is(NaN,NaN);                 // true
複製代碼

9.Symbol對象

Symbol類型的基本寫法

ES5 的對象屬性名都是字符串,這容易形成屬性名的衝突。好比,你使用了一個他人提供的對象,但又想爲這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現有方法產生衝突。若是有一種機制,保證每一個屬性的名字都是獨一無二的就行了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入Symbol的緣由。(注:這部份內容不少都是摘抄於阮大神的書)

ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種數據類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)。

Symbol 值經過Symbol函數生成。這就是說,對象的屬性名如今能夠有兩種類型,一種是原來就有的字符串,另外一種就是新增的 Symbol 類型。凡是屬性名屬於 Symbol 類型,就都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。

首先,咱們來看一下最簡單的Symbol類型的聲明:

let s = Symbol();
typeof s;          //symbol
複製代碼

Symbol函數能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。可是注意,Symbol函數的參數只是表示對當前 Symbol 值的描述,所以相同參數的Symbol函數的返回值是不相等的。

let s1 = Symbol('will');
let s2 = Symbol('yuan');
let s3 = Symbol('will');

console.log(s1);
console.log(s2);

console.log(s1.toString());
console.log(s2.toString());

console.log(s1 === s2);
複製代碼

打印出來的結果是:

Symbol做爲屬性名

因爲每個 Symbol 值都是不相等的,這意味着 Symbol 值能夠做爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的狀況很是有用,能防止某一個鍵被不當心改寫或覆蓋。

let mySymbol = Symbol();

// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';

// 第二種寫法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三種寫法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上寫法都獲得一樣結果
a[mySymbol] // "Hello!"
複製代碼

注意,Symbol 值做爲對象屬性名時,不能用點運算符。

const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
複製代碼

上面代碼中,由於點運算符後面老是字符串,因此不會讀取mySymbol做爲標識名所指代的那個值,致使a的屬性名其實是一個字符串,而不是一個 Symbol 值。

屬性名的遍歷

Symbol 做爲屬性名,該屬性不會出如今for...in、for...of循環中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。這樣就很好的保護了Symoble類型的屬性,可是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,能夠獲取指定對象的全部 Symbol 屬性名。

const obj = {};

let s1 = Symbol('will');
let s2 = Symbol('yuan');

obj[s1] = 'hello';
Object.defineProperty(obj,s2,{
    value: 'world'
});

obj.s3 = 'shen';

for(let i in obj) {
    console.log(i);                                 // s3
}

console.log(Object.getOwnPropertyNames(obj));       // ['s3']

console.log(Object.getOwnPropertySymbols(obj));     // [Symbol(will), Symbol(yuan)]
複製代碼

上面這個例子,能夠發現經過for循環並不能循環出來Symbol類型的屬性名,可是Object.getOwnPropertySymbols()方法又不能拿到字符串類型的屬性名,那麼咱們若是要拿到一個對象所有的屬性名怎麼辦呢,能夠用Reflect.ownKeys()方法:

let obj = {
    'age' : 20,
    'address' : 'shenzhen',
    [Symbol('name')] : 'will',
    [Symbol('id')] : 5
}

console.log(Reflect.ownKeys(obj));

for(let i of Reflect.ownKeys(obj)) {
    console.log(obj[i]);
}
複製代碼

打印出來的結果就是:

Symbol.for(),Symbol.keyFor()

有時,咱們但願從新使用同一個 Symbol 值,Symbol.for方法能夠作到這一點。它接受一個字符串做爲參數,而後搜索有沒有以該參數做爲名稱的 Symbol 值。若是有,就返回這個 Symbol 值,不然就新建並返回一個以該字符串爲名稱的 Symbol 值。

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true
複製代碼

注意:Symbol.for()與Symbol()這兩種寫法,都會生成新的 Symbol。它們的區別是,前者會被登記在全局環境中供搜索,後者不會。Symbol.for()不會每次調用就返回一個新的 Symbol 類型的值,而是會先檢查給定的key是否已經存在,若是不存在纔會新建一個值。也就是說,經過Symbol()生成的Symbol並不會被檢索到,咱們看個例子:

let s1 = Symbol('will');
let s2 = Symbol.for('will');
let s3 = Symbol.for('will');

console.log(s1 === s2);      //false
console.log(s2 === s3);      //true
複製代碼

Symbol.keyFor方法返回一個已登記的 Symbol 類型值的key

let s1 = Symbol('will');
let s2 = Symbol.for('will');

console.log(Symbol.keyFor(s1));   // undefined
console.log(Symbol.keyFor(s2));   // 'will'
複製代碼

Symbol類型主要用於函數的屬性,Symbol還有不少的方法,可是用的不是不少,因此就不一一列舉了,你們想了解更加詳細的,能夠去看阮一峯大神的書。

10.Set數據結構

Set是ES6提供的一種新的數據結構,它很是相似於數組,可是它的成員值都是惟一的,就算添加劇復的值,也會自動刪除掉,而且計算Set的size的時候不會計算重複的值。

Set接受一個數組來進行初始化(也能夠不接受任何參數)

const set1 = new Set();

const set2 = new Set([1,2,3,4,5]);

const set3 = new Set([1,2,3,4,5,4,3,2,1]);

console.log (set1);
console.log (set2);
console.log (set3);
複製代碼

打印結果:

因爲Set是沒有重複值的,因此咱們能夠利用這個特性進行數組去重:

let arr = [1,2,3,4,5,6,4,3,4,5,2];

let newArr = [...new Set(arr)];          // 方法一

let newArr1 = Array.from(new Set(arr));  // 方法二
複製代碼

注意:Set內部判斷值是否重複的算法是「Same-value equality」,相似於===,不一樣之處在於NaN對於這種算法來言是相等的。

let set1 = new Set(['1',1,2,NaN,NaN]);

console.log(set1);
複製代碼

打印結果是:

Set和數組同樣,也是能夠增刪查改的,不一樣的是方法不同,如下說明:

1.add(value) : 添加某個值,返回 Set 結構自己

2.delete(value) : 刪除某個值,返回一個布爾值,表示刪除是否成功

3.has(value) : 返回一個布爾值,表示該值是否爲Set的成員

4.clear() : 清除全部成員,沒有返回值

如下是demo:

let set = new Set();

set.add(1).add(2).add(3);

console.log(set);
console.log(set.has(3));

set.delete(3);

console.log(set.has(3));

set.clear();

console.log(set);
複製代碼

打印結果:

和數組同樣,set數據結構也是能夠進行遍歷的,可是注意一點,set是沒有鍵名的(也能夠認爲鍵名和鍵值同樣),因此在遍歷keys和values的時候,返回值同樣。

let set = new Set([1, 2, 3, 4, 5]);

for (let i of set) {
    console.log(i);                         // 1 2 3 4 5
}

for (let i of set.keys()) {
    console.log(i);                         // 1 2 3 4 5
}

for (let i of set.values()) {   
    console.log(i);                         // 1 2 3 4 5
}

for (let [key,value] of set.entries()) {
    console.log(key + ':' + value);         // 1:1 2:2 3:3 4:4 5:5
}
複製代碼

咱們在數組中,常常會存放對象,而WeakSet爲咱們作到了,不過用的不多,能夠稍做了解便可。

WeakSet 是一個構造函數,可使用new命令,建立 WeakSet 數據結構,可是須要注意兩點,一是在new的時候不容許放入值,不然會報錯,二是WeakSet裏面的值也是不容許重複的,這裏的不容許重複指的是不能指向同一內存塊。

let objSet = new WeakSet();

let obj1 = {name:'will',age:18};

let obj2 = {name:'will',age:18}; //因爲obj和obj2是指向不一樣內存塊,因此在WeakSet裏面是都會添加進去的

let obj3 = obj1;                  //因爲obj和obj3是指向不一樣同一內存塊,因此在WeakSet裏面是隻會添加以此

objSet.add(obj1);
objSet.add(obj2);
objSet.add(obj3);

console.log(objSet);  //WeakSet {{…}, {…}}
複製代碼

11.Map數據結構

Map是ES6提供的一種新的數據結構,相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。也就是說,Object 結構提供了「字符串—值」的對應,Map 結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。若是你須要「鍵值對」的數據結構,Map 比 Object 更合適。

Map構造函數接受一個數組做爲參數(固然也能夠不傳參數)

let map1 = new Map();

let map2 = new Map([['name','yuan'],['age',18]]);

console.log(map1);
console.log(map2);
複製代碼

打印結果:

Map能夠將對象做爲鍵值:

let map = new Map();

let obj = {name:'yuan','age':18};

map.set(obj,'will');

console.log(map);
複製代碼

打印結果:

Map一樣有增刪查改方法,如下說明:

  1. set(key,value) : 設置鍵名key對應的鍵值爲value,而後返回整個 Map 結構。若是key已經有值,則鍵值會被更新,不然就新生成該鍵
  2. get(key) : 取key對應的鍵值,若是找不到key,返回undefined
  3. has(key) : 返回一個布爾值,表示某個鍵是否在當前 Map 對象之中
  4. delete(key) : 刪除某個鍵,返回true。若是刪除失敗,返回false
  5. clear() : 清除全部成員,沒有返回值
  6. size : 這個是屬性,不是方法,返回 Map 結構的成員總數

如下是demo:

let map1 = new Map();

map1.set('string', true);           // 鍵是字符串

map1.set({name:'yuan'},false);      // 鍵是對象

map1.set(1,true);                   // 鍵是數字

map1.set(undefined,false);          // 鍵是undefined

console.log(map1);

console.log(map1.get(1));           // true
console.log(map1.get(undefined));   // false

console.log(map1.has(undefined));   // true
console.log(map1.has('string'));    // true
console.log(map1.has('1'));         // false

map1.delete(1);

console.log(map1.size);             // 3

map1.clear();

console.log(map1)
複製代碼

打印結果:

有一點須要注意:Map 的鍵其實是跟內存地址綁定的,只要內存地址不同,就視爲兩個鍵。若是 Map 的鍵是一個簡單類型的值(數字、字符串、布爾值),則只要兩個值嚴格相等,Map 將其視爲一個鍵,好比0和-0就是一個鍵,布爾值true和字符串true則是兩個不一樣的鍵。另外,undefined和null也是兩個不一樣的鍵。雖然NaN不嚴格相等於自身,但 Map 將其視爲同一個鍵。

let map = new Map();

let obj1 = {name:'yuan'};
let obj2 = {name:'yuan'};

map.set(obj1,1);
map.set(obj2,2);

console.log(map.get(obj1));     // 1
console.log(map.get(obj2));     // 2

map.set(-0,true);
console.log(map.get(+0));       // true

map.set(true,5);
console.log(map.get('true'));   // undefined

map.set(undefined,6);
map.set(null,7);
console.log(map.get(undefined));// 6

map.set(NaN,8);
console.log(map.get(NaN));      // 8
複製代碼

ES6的基本知識差不都就這些了,相似promise,class等沒有作講述,只是講述了基礎的知識,上述內容有部分摘抄於阮一峯大神的ES6書籍,也有部分借鑑了網上各位大神的資料,我只是作了部分的概括和總結,有不少不足之處或者不完善的地方,但願多多包容!

相關文章
相關標籤/搜索