重學前端之JavaScript(六)屬性描述符、ES6中的module、箭頭函數

  這篇文章主要介紹JavaScript中的屬性描述符、ES6中的module、箭頭函數。javascript

JavaScript的屬性描述符

1、對象的屬性
  屬性描述符: 對象裏目前存在的屬性描述符有兩種主要形式:數據描述符和存取描述符。數據描述符是一個具備值的屬性,該值多是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一,不能同時是二者。
  數據描述符(數據屬性)和存取描述符(訪問器屬性)均具備的可選鍵值:
    configurable: 當且僅當該屬性的 configurable 爲 true 時,該屬性能從對應的對象上被刪除,以及除value和writable特性外的其餘特性是否能夠被修改。默認爲 false。
    enumerable: 當且僅當該屬性的enumerable爲true時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。
  數據描述符(數據屬性)的可選鍵值:
    value: 該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。
    writable: 當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。默認爲 false。
  存取描述符(訪問器屬性)的可選鍵值:
    get: 一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this對象(因爲繼承關係,這裏的this並不必定是定義該屬性的對象)。默認爲 undefined。
    set: 一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。默認爲 undefined。
  注: 若是一個描述符不具備value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。若是一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。html

2、設置、修改對象單個屬性的描述
  Object.defineProperty(): 該方法會直接在對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回這個對象。IE8部分支持。
  Object.defineProperty()的語法:
    Object.defineProperty(obj, prop, descriptor)
      obj: 要在其上定義屬性的對象。
      prop: 要定義或修改的屬性的名稱。
      descriptor: 將被定義或修改的屬性描述符。java

Object.defineProperty(obj, 'val', {
        value: 1,
        writable: false,
        enumerable: true,
        configurable: true
    })
    obj.val = 2;
    console.log(obj.val); // 1

    Object.defineProperty(obj, 'val', {
        value: 1,
        writable: true,
        enumerable: true,
        configurable: true
    })

    obj.val = 3;
    console.log(obj.val); // 3

    Object.defineProperty(obj1, 'val', {
        get() {
            num += 1;
            return num;
        },
        set(newVal) {
            num = newVal;
        },
        enumerable: true,
        configurable: true
    })
    console.log(obj1.val); // 2
    console.log(obj1.val); // 3

屬性的賦值器(setter)和取值器(getter): 利用ES6屬性的簡潔表示法,使用get和set關鍵字來建立訪問器屬性。es6

const obj = {
        num: 1,
        get num1() {
            return this.num;
        },
        set num1(newVal) {
            this.num += 1;
        }
    }

3、設置、修改對象多個屬性的描述
  Object.defineProperties(): 直接在一個對象上定義新的屬性或修改現有屬性,並返回該對象。
  Object.defineProperties()語法: Object.defineProperties(obj, props) / Object.defineProperties(obj, {property: descriptor, property: descriptor})
    obj: 在其上定義或修改屬性的對象。
    property: 要定義或修改的屬性的名稱。
    descriptor: 將被定義或修改的屬性描述符。瀏覽器

var obj = {};
    Object.defineProperties(obj, {
        'property1': {
            value: true,
            writable: true
        },
        'property2': {
            value: 'Hello',
            writable: false
        }
    });

4、獲取對象屬性描述
  Object.getOwnPropertyDescriptor(): 返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不須要從原型鏈上進行查找的屬性)。IE8支持。
  Object.getOwnPropertyDescriptor()的語法:
    Object.getOwnPropertyDescriptor(obj, prop)
      obj: 須要查找的目標對象
      prop: 目標對象內屬性名稱服務器

let obj = {},
        obj1 = {};
    Object.defineProperty(obj, 'num', {
        value: 1
    })
    obj1.num = 1;
    console.log(Object.getOwnPropertyDescriptor(obj, 'num'));  // {value: 1, writable: false, enumerable: false, configurable: false}
    console.log(Object.getOwnPropertyDescriptor(obj, 'num'));  // {value: 1, writable: true, enumerable: true, configurable: true}

參考來自:
  MDN--Object.defineProperty()
  MDN--Object.defineProperties()
  MDN--Object.getOwnPropertyDescriptor()app

ES6module

1、module的定義
  模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其餘模塊提供的功能。異步

2、export命令
  1) 定義: 一個模塊就是一個獨立的文件,改文件內部的全部變量,外部沒法獲取,只有經過export關鍵字輸出該變量、函數或類,外部纔可以讀取該變量、函數或類。
  2) 注意點:
    a、export命令規定的是對外的接口,必須與模塊內部的變量創建一一對應的關係,因此不能經過export直接輸出一個變量或值
    b、export命令必須處於模塊頂層,不能處於塊級做用域內,import也是如此。這是由於處於條件代碼塊之中,就沒有辦法作靜態優化了,違背了ES6模塊的設計初衷
使用方法:async

// 輸出一
    export var a = 1;

    // 輸出二
    var b = 1;
    var c = 2;
    export { b, c };



    // 錯誤寫法,由於沒有提供對外的接口
    var d = 3;
    export d;
    export 1;

3、export default命令
  1) 定義: 爲模塊指定默認輸出,在輸入時無需知道原模塊輸出的變量名,能夠用任意名稱來指向該模塊輸出的內容
  2) 注意點:
    a、export命令用於指定模塊的默認輸出,一個模塊只能有一個默認輸出,所以export default命令只能使用一次,否則會報錯
    b、本質上export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字。它後面不能跟變量聲明語句
使用方法:函數

// 輸出一個變量
    var a = 1;
    export defualt a;

    // 輸出一個值
    export default 1;

    // 輸出一個方法
    export default funciton () {

    }

    // 輸出一個方法
    function add() {}
    export default add;

    // 輸出一個對象
    export default {

    }

4、import命令
  1) 定義: 使用export命令定義了模塊的對外接口之後,其餘 JS 文件就能夠經過import命令加載這個模塊
  2) 注意點:
    a、加載export輸出的變量、函數、類時,須要使用import命令接受一對大括號,裏面指定要從其餘模塊導入的變量名,大括號裏的變量名,必須與被導入模塊對外接口的名稱同樣(import {variable} from './index.js');加載export default輸出的變量、函數、類時,可使用任意名稱指向輸出的方法,import後面不使用大括號(import allFn from '/index.js' )
    b、加載模塊全部的輸出值,利用星號(*)指定一個對象,全部輸出值都加載在這個對象上面,包括export default
    c、import命令輸入的變量都是隻讀的,不容許在加載模塊的裏面改寫接口。只有當輸出的是一個對象時,能夠改寫其屬性
    d、import命令具備提高效果,會提高到整個模塊的頭部,首先執行。import命令是編譯階段執行的,在代碼運行以前
    e、若是屢次重複執行同義句import語句,那麼只會執行一次,而不會執行屢次。當屢次使用import引入同一個module的不一樣方法時,引入的方法對應的都是一個module,import語句是單例模式
    f、import是靜態執行的,因此不能使用表達式、變量、條件語句、代碼塊
    g、import輸入時,語法要求不帶as的默認值(export default輸出的)永遠在最前
使用方法:

// 執行所加載的模塊,引用它的模塊沒法獲取它的任何信息
    import 'lodash';

    // 加載export輸出的變量
    import { firstName } from './index.js';

    // 加載export default輸出的變量
    import allFn from './index.js';

    // 在一條import語句中同時輸入默認方法和其餘接口
    import _, { each } from './lodash';

    // 加載模塊全部的輸出值,利用星號(*)指定一個對象,全部輸出值都加載在這個對象上面
    import * as circle from './index.js';

    // 單例的表現
        // test1.js
        var a = 1;
        export function add() {
            a++;
            console.log(a);
        }
        // test2.js
        export { add } from './text1.js';
        // index.html
        import { add } from './test.js';
        import { add as getCount } from './test1.js';
        add();  // 2
        getCount(); // 3

5、as關鍵字:
  1) 定義: 使用as關鍵字能夠爲輸出或輸入的變量從新命名
  2) 注意點: as關鍵字的前面表示原本的名字,後面的是新名字(oldName as newName)
使用方法:

// 輸出時重命名
    var a = 1,
        b = 2;
    export {
        a as num1,
        b as num2
    }
    // 輸入時重命名
    import * as numModule from './index.js';

    // 輸入時重命名
    import {x as num1} from './index.js'

6、export和import的複合用法
  1) 定義: 在一個模塊中,先輸入後輸出同一個模塊,import語句能夠和export語句寫在一塊兒
  2) 注意點:
    a. export和import寫成一行時,接口其實沒有被導入當前模塊,只是至關於對外轉發了這個接口,當前模塊不能直接使用該接口
    b. 在export和import複合使用時,* 表明的是所有的其餘接口(export輸出的),不包含默認接口(export default輸出的)
使用方法:

// 使用一
        // 先輸入後輸出其餘接口(export輸出的),該模塊中沒法使用foo、bar
        export { foo, bar } from './index.js';
        // 別的模塊中使用
        import { foo, bar } from './util.js';
    // 使用二
        // 總體輸出其餘接口(export輸出的)
        export * from './index.js';
        // 別的模塊中使用
        import * as all from './util.js';
    // 使用三
        // 先輸入後輸出默認接口(export default輸出的),該模塊中沒法使用該方法
        export { default } from './index.js';
        // 別的模塊中使用
        import allFn from './util.js';

7、跨模塊使用
   定義: 是一個模塊裏的接口跨多個文件使用
使用方法:

// test1.js
    export const A = 1;

    // test2.js
    export const B = 2;

    // 合併全部的接口在index.js中
    export { A } from './test1.js';
    export { B } from './test2.js';

8、ES6模塊的好處
  1) ES6模塊能夠按需加載。在加載時,只加載import命令輸入的方法和變量,模塊中其餘方法和變量不加載。這種加載稱爲"編譯時加載"或者靜態加載,即ES6能夠在編譯時就完成模塊加載。而CommonJS和AMD模塊,只能在運行時肯定模塊的的依賴關係、輸入輸出變量,是"運行時加載",由於只有運行時才能獲得這個對象。好比CommonJS模塊加載時,實質上是總體加載,生成一個對象,再從對象上讀取加載的方法。
  2) 再也不須要UMD模塊格式了,未來服務器和瀏覽器都會支持 ES6 模塊格式。目前,經過各類工具庫,其實已經作到了這一點。
  3) 未來瀏覽器的新 API 就能用模塊格式提供,再也不必須作成全局變量或者navigator對象的屬性。
  ) 再也不須要對象做爲命名空間(好比Math對象),將來這些功能能夠經過模塊提供。

9、ES6模塊注意點
  ES6的模塊自動採用嚴格模式,關於嚴格模塊一般須要注意的就兩點,一是變量必須聲明後再使用,二是頂層的this指向undefined。

10、腳本和模塊的區別
  腳本的特色:
     1) 是JavaScript源文件的一種
     2) 經過<script type="application/javascript">標籤的src屬性,引入外部js文件
     3) 在沒有src屬性時能夠直接在<script type="application/javascript">標籤內部寫代碼。因爲瀏覽器腳本的默認語言是 JavaScript,所以type="application/javascript"能夠省略
     4) 兼容性好,沒法在其中使用export和import
     5) 腳本默認是同步加載的,能夠在<script>上添加async(腳本一旦下完,渲染引擎就會中斷渲染,執行這個腳本以後,再渲染)、defer(該腳本要等到整個頁面在內存中正常渲染結束,也就是DOM結構徹底生成,以及其餘腳本執行完成後纔會執行)屬性來讓其異步加載
  模塊的特色:
     1) 是JavaScript源文件的一種
     2) 瀏覽器對於type="module"的<script>都是異步加載的,不會形成阻塞瀏覽器,即等到整個頁面渲染完,再執行模塊腳本,等同於打開了<script>標籤的defer屬性,若是有多個<script type="module">,會按照在頁面出現的順序依次執行。也能夠本身在<script>設置async屬性,這時只要加載完成,渲染引擎就會中斷渲染當即執行,執行完成後再恢復渲染。一旦使用了async屬性,<script type="module">就不會按照在頁面出現的順序執行,而是隻要該模塊加載完成,就執行該模塊
     3) 經過<script type="module">標籤的src屬性引入外部模塊,執行所加載的模塊,引用它的模塊沒法獲取它的任何信息
     4) 在沒有src屬性時,在<script type="module"></script>中間能夠內嵌到網頁中,語法行爲與加載外部腳本徹底一致。能夠在其中使用import、export、export default命令
    5) 兼容性很差,IE不支持,Edge16支持
參考:
  ES6阮一峯--Module 的語法
  ES6阮一峯--Module 的加載實現

箭頭函數的特色

1、箭頭函數的特色
  1) 函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。this指向的固定化,並非由於箭頭函數內部有綁定this的機制,實際緣由是箭頭函數根本沒有本身的this,致使內部的this就是外層代碼塊的this(也就是this的指向向上提一級)。正是由於它沒有this,因此也就不能用做構造函數。
  2) 不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
  3) 不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替。
  4) 不可使用yield命令,所以箭頭函數不能用做 Generator 函數。
  5) 箭頭函數沒有本身的this,因此不能使用call()、apply()、bind()方法去改變this的指向
  6) 箭頭函數中沒有arguments、super、new.target,它們指向外層函數的對應變量。

2、箭頭函數轉換成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)
    }

  來源: 阮一峯ES6入門--箭頭函數

以上內容若有不對,但願你們指出,謝謝。

相關文章
相關標籤/搜索