【學習筆記】ES6標准入門


這里簡要記錄一下對自己感觸比較深的幾個知識點,將核心的應用投放於實際的項目之中,提供代碼的可維護性。

 

一、let和const

{
    // let聲明的變量只在let命令所在的代碼塊內有效
    let a = 1;
    var b = 2;
}

console.log(a);     // 報錯: ReferenceError: a is not defined
console.log(b);
// for循環的技術器就很適合let命令
for (let i = 0; i < 3; i++) {
    console.log(i);
}

console.log(i); // ReferenceError: i is not defined
// 這里的i是var聲明的,在全局范圍內有效,素偶一每一次循環,新的i值都會覆蓋舊值,導致最后輸出的是最后一輪的i值
for (var i = 0; i < 10; i++) {
    a[i] = function() {
        console.log(i);
    }
}

a[6](); // 10

var b = [];

// 使用let聲明的變量盡在塊級作用域內有效,所以每一次循環的j其實都是一個新的變量,於是最后輸出6
for (let j = 0; j < 10; j++) {
    a[j] = function() {
        console.log(j);
    }
}

b[6]();
// let不像var那樣會發生"變量"提升的現象
// 但是經過babel轉換器轉換之后,還是存在變量提升的現象
// ES6明確規定,如果區塊中存在let和const命令,則這個區塊中對這些命令聲明的變量從一開始就形成封閉作用域.只要在聲明這些變量之前就使用這些變量,就會報錯
{
    console.log(foo);   // ReferenceError
    let foo = 2;
}

// 塊級作用域
{
    // 塊級作用域的出現使得獲得廣泛應用的立即執行匿名函數(IIFE)不再必要了
    // IIFE寫法
    (function() {
        var tmp = 'a';
        // ...
    })();

    // 塊級作用域寫法
    {
        let tmp = 'a';
        // ...
    }

    // 因此,我們可以使用塊級作用域來划分業務模塊,以及避免全局變量

}

{
    let a = 'secret';

    function f() {
        return a;
    }
}

f();    // 報錯
// const聲明的常量不得改變值
// const一旦聲明常量,就必須立即初始化,不能留到以后賦值
// const的作用域與let命令相同:只在聲明所在的塊級作用域內有效
// const命令聲明的變量也不提升,只能聲明后使用
const foo = 'AAA';
foo = 'BBB';    // 編譯不通過
{
    // 跨模塊常量
    // constants.js
    //export const A = 1;
    //export const B = 2;
    //export const C = 3;

    // test1.js模塊
    //import * as constants from 'constants';
    
}
// 全局對象的屬性
var a = 1;
console.log(window.a);  // 1

let b = 2;
console.log(window.b);  // undefined

 

二、字符串

{
    // 使用for of循環字符串
    for (let c of 'foo') {
        console.log(c);
    }

    let s = 'Hello world!';
    
    // 使用字符串的startsWidth/endsWidth/includes方法
    console.log(s.startsWith('Hello')); // true
    console.log(s.endsWith('!'));   // true
    console.log(s.includes('e'));   // true

    // 這三個方法都支持第二個參數,表示開始搜索的位置
    s.startsWith('world', 6);   // true

    let person = {
        name: 'king',
        age: 20
    };
    
    // 模板字符串
    // 所有的空格和縮進都會被保留在輸出中
    let str = (`
        The name is ${person.name}.
        The age is ${person.age}.
    `);

    console.log(str);

}

 

三、函數

// 函數參數的默認值
function log(x, y = 'world') {
    console.log(x, y);
}

log('hello');   // 可以省略尾部參數的默認值

function f(x = 1, y) {
    return [x, y];
}

f();    // [1, undefined]
f(2);   // [2, undefined]
f(, 1); // 報錯, 編譯無法通過


// rest參數
function add(...values) {
    let sum = 0;

    for (let val of values) {
        sum += val;
    }

    return sum;
}

console.log(add(2, 5, 3));   // 10


const sortNumbers = function() {
    let arr = Array.prototype.slice.call(arguments);
    return arr.sort();
};

const sortNumbers = function (...numbers) {
    return numbers.sort();
};

sortNumbers(3, 1, 2);

// rest參數必須是參數列表中的最后一個
const push = function(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
    });
};

let a = [];

console.log(push(a, 3, 1, 2));

 

四、對象

// Object.assign方法用來將源對象的所有可枚舉屬性復制到目標對象
let target = {
    a: 1
};

// 后邊的屬性值,覆蓋前面的屬性值
Object.assign(target, {
    b: 2,
    c: 3
}, {
    a: 4
});

console.log(target);

// 用處1 - 為對象添加屬性
class Point {
    constructor(x, y) {
        Object.assign(this, {x, y});
    }
}

//let p = new Point(1, 2);
//
//console.log(p); // Point {x: 1, y: 2}

// 用處2 - 為對象添加方法
Object.assign(Point.prototype, {
    getX() {
        return this.x;
    },
    setX(x) {
        this.x = x;
    }
});

let p = new Point(1, 2);

console.log(p.getX());  // 1

// 用處3 - 克隆對象
function clone(origin) {
    return Object.assign({}, origin);
}

 

五、Set和Map

  // Set里面的成員的值都是唯一的,沒有重復的值,Set加入值時不會發生類型轉換,所以5和"5"是兩個不同的值.
    let s = new Set();

    [2, 3, 5, 4, 5, 2, 2].map(function(x) {
        s.add(x);
    });

    //for (let i of s) {
    //    console.log(i);
    //}

    console.log([...s]);

    console.log(s.size);


    // 數組去重
    function dedupe(array) {
        return Array.from(new Set(array));
    }

    console.log(dedupe([1, 2, 2, 3]));  // 1, 2, 3
{

    // Map類似於對象,也是鍵值對的集合,但是"鍵"的范圍不限於字符串,各種類型的值(包括對象)都可以當做鍵.
    // 也就是說,Object結構提供了"字符串--值"的對應,Map的結構提供了"值——值"的對象,是一種更完善的Hash結構實現.

    var m = new Map();

    var o = {
        p: 'Hello World'
    };

    m.set(o, 'content');
    m.get(o);   // content

    m.has(o);    // true
    m.delete(o);    // true
    m.has(o);   // false

    m.set(o, 'my content').set(true, 7).set('foo', 8);

    console.log(m);

    // Map/數組/對象 三者之間的相互轉換
    console.log([...m]);

}

 

六、Iterator和Generator

{
    // 是一種接口,為各種不同的數據結構提供統一的訪問機制.任何數據結構,只要不輸Iterator接口,就可以完成遍歷操作.
    // 可供for...of循環消費

    const arr = ['red', 'green', 'blue'];

    let iterator = arr[Symbol.iterator]();

    for (let v of arr) {
        console.log(v); // red green blue
    }

    for (let i of iterator) {
        console.log(i);
    }

    // for of 循環可以代替數組對象的forEach方法, 同樣可以替代對象的for in循環

}
{
    function * foo() {
        yield 1;
        yield 2;
        yield 3;
        yield 4;
        yield 5;
        return 6;
    }

    for (let v of foo()) {
        console.log(v);
    }

}

 

七、Promise和async

{
    let getJSON = function (path, param) {

        return new Promise(function(resolve, reject) {
            let async = typeof param.async == 'undefined' ? true : param.async;
            //let deferred = $.Deferred();

            param = param || {};
            param.data.auth_token = lib.getToken();

            window.loading();

            $.ajax({
                url: path,
                data: param.data,
                type: 'POST',
                dataType: 'json',
                async: async,
                timeout: 15000,
                success: function (data) {
                    window.unloading();
                    if (data.code == 0) {
                        resolve.apply(this, [data]);
                    } else {
                        reject.apply(this, [data]);
                        lib.alert(data.msg, '我知道了');
                    }
                },
                error: function (xhr, type) {
                    window.unloading();
                    reject.apply(this, ['網絡異常, 請稍候再試']);
                    lib.alert('網絡異常, 請稍候再試');
                }
            });
        });

    };

    getJSON('/xxx.json').then(function(rep) {

    }).catch(function(rep) {

    });

}
{

    function timeout(ms) {
        return new Promise(function(resolve) {
            setTimeout(resolve, ms);
        });
    }

    async function asyncPrint(value, ms) {
        let promise = await timeout(ms);
        console.log(value);
    }

    asyncPrint('Hello world !', 1000);

}

 

八、class

{
    class Point {

        static classMethod() {
            return 'classMethod...';
        }

        // constructor方法是類的默認方法,通過new命令生成對象實例時自動調用該方法.
        // 一個類必須有constructor方法,如果沒有顯示定義,一個空的constructor方法會被默認添加
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }

        toString() {
            return '(' + this.x + ', ' + this.y + ')';
        }

        get prop() {
            return 'getter';
        }

        set prop(value) {
            console.log('setter:' + value);
        }

    }

    // 靜態屬性的處理,只能用下面這種方式
    Point.foo = 1;
    console.log(Point.foo); // 1

    // 繼承
    class ColorPoint extends Point {

        constructor(x, y, color) {
            // super方法必須被調用, 否則編譯不通過
            // 如果super在賦值屬性this.xx = xx,后邊調用,會報錯'this' is not allowed before super()
            super(x, y);
            this.color = color;
        }

        toString() {
            return 'The color is ' + this.color + ' and the point is ' + super.toString();
        }

    }

    var p = new ColorPoint(1, 2, 'red');
    console.log(p.toString());
    p.prop = 1;
    p.prop;

    console.log(Point.classMethod());

    // 父類的靜態方法可以被子類繼承
    console.log('ColorPoint.classMethod(): ' + ColorPoint.classMethod());


}

 

 九、Module

{
    // module
    /**
     * 優勢:
     *  1. ES6可以在編譯時就完成模塊編譯,效率要比commonJs模塊的加載方式高
     *  2. 不再需要UMD模塊格式,將來服務器端和瀏覽器都會支持ES6模塊格式.目前,通過各種工具庫其實已經做到了這一點
     *  3. 將來瀏覽器的新API可以用模塊格式提供,不再需要做成全局變量或者navigator對象的屬性
     *  4. 不再需要對象作為命名空間(比如Math對象),未來這些功能可以通過模塊提供
     */

}
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};


// export命令除了輸出變量,還可以輸出函數或類(class)。
export function multiply (x, y) {
    return x * y;
};

// export輸出的變量就是本來的名字,但是可以使用as關鍵字重命名。

function v1() {
    //...
}
function v2() {
    //...
}

export {
    v1 as streamV1,
    v2 as streamV2,
    v2 as streamLatestVersion
};



// import
// main.js

import {firstName, lastName, year} from './profile';

// 重命名
import { lastName as surname } from './profile';

// import命令具有提升效果,會提升到整個模塊的頭部,首先執行。
foo();
import { foo } from 'my_module';


// 僅僅執行lodash模塊,但是不輸入任何值。
import 'lodash';




// export default
// 為了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,為模塊指定默認輸出。

// export-default.js
export default function () {
    console.log('foo');
}

// import-default.js
// 需要注意,這時import命令后面,不使用大括號。
import customName from './export-default';
customName(); // 'foo'


// export default命令用在非匿名函數前,也是可以的。
// export-default.js
export default function foo() {
    console.log('foo');
}

// 或者寫成

function foo() {
    console.log('foo');
}

export default foo;

 

十、編程風格
// 1. let取代var



// 2. 全局常量 // 在let和const之間,建議優先使用const,尤其是在全局環境,不應該設置變量,只應設置常量。 // const聲明常量還有兩個好處,一是閱讀代碼的人立刻會意識到不應該修改這個值,二是防止了無意間修改變量值所導致的錯誤。 // 所有的函數都應該設置為常量。 // bad var a = 1, b = 2, c = 3; // good const a = 1; const b = 2; const c = 3; // best const [a, b, c] = [1, 2, 3];


// 3. 字符串 // 靜態字符串一律使用單引號或反引號,不使用雙引號。動態字符串使用反引號。


// 4. 對象 //對象盡量靜態化,一旦定義,就不得隨意添加新的屬性。如果添加屬性不可避免,要使用Object.assign方法。 // bad const a = {}; a.x = 3; // if reshape unavoidable const a = {}; Object.assign(a, { x: 3 }); // good const a = { x: null }; a.x = 3; // 對象的屬性和方法,盡量采用簡潔表達法,這樣易於描述和書寫。 var ref = 'some value'; // bad const atom = { ref: ref, value: 1, addValue: function (value) { return atom.value + value; }, }; // good const atom = { ref, value: 1, addValue(value) { return atom.value + value; }, };


// 5. 數組 //使用擴展運算符(...)拷貝數組。 // bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items]; //使用Array.from方法,將類似數組的對象轉為數組。 const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);



// 6. 函數 //不要在函數體內使用arguments變量,使用rest運算符(...)代替。因為rest運算符顯式表明你想要獲取參數,而且arguments是一個類似數組的對象,而rest運算符可以提供一個真正的數組。 // bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); } //使用默認值語法設置函數參數的默認值。 // bad function handleThings(opts) { opts = opts || {}; } // good function handleThings(opts = {}) { // ... }



// 7. 模塊 //首先,Module語法是JavaScript模塊的標准寫法,堅持使用這種寫法。使用import取代require。 // bad const moduleA = require('moduleA'); const func1 = moduleA.func1; const func2 = moduleA.func2; // good import { func1, func2 } from 'moduleA'; //使用export取代module.exports。 // commonJS的寫法 var React = require('react'); var Breadcrumbs = React.createClass({ render() { return <nav />; } }); module.exports = Breadcrumbs; // ES6的寫法 import React from 'react'; const Breadcrumbs = React.createClass({ render() { return <nav />; } }); export default Breadcrumbs //如果模塊只有一個輸出值,就使用export default,如果模塊有多個輸出值,就不使用export default,不要export default與普通的export同時使用。 //不要在模塊輸入中使用通配符。因為這樣可以確保你的模塊之中,有一個默認輸出(export default)。 // bad import * as myObject './importModule'; // good import myObject from './importModule'; //如果模塊默認輸出一個函數,函數名的首字母應該小寫。 function makeStyleGuide() { } export default makeStyleGuide; //如果模塊默認輸出一個對象,對象名的首字母應該大寫。 const StyleGuide = { es6: { } }; export default StyleGuide;

 

希望我所記錄的,正是你所想要的。 

最后,將這本書的封面放在這里——

 



 


免責聲明!

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



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