關於ES模塊你必需要知道的一些禁忌(一)

背景

ES ModuleJavaScriptES2015版本開始提供的語言標準級別的模塊化方案,在此以前JavaScript一直沒有語言級別的模塊化體系。沒有模塊化的支持,使用JavaScript開發大型應用將舉步維艱,因此通過大量的實踐,社區制定了一些模塊加載方案,最主要的有運行於瀏覽器的AMD方案和運行於以Nodejs爲表明的服務端的CommonJS方案。javascript

因爲WebpackBabel等打包、轉義工具的出現,開發者已經能夠在開發中使用ES ModuleAMD已經是明日黃花,使用的人愈來愈少,不太值得去關注。但CommonJS方案因爲Nodejs在前端構建工具和服務端中的普及度,在Nodejs全面支持ES Module、老版本Nodejs消亡以前,咱們仍是要關注CommonJS方案以及它與ES Module之間的區別,以避免搞混、記憶混淆,釀成bug。爲了爲後面的禁止點作鋪墊,先讓咱們來了解或回顧兩個API:Object.preventExtensionsObject.freeze前端

Object.preventExtensions

Object.preventExtensions()將對象標記爲再也不可擴展,所以它將永遠不會具備超出它被標記爲不可擴展的屬性。注意,通常來講,不可擴展對象的屬性可能仍然可被刪除。嘗試將新屬性添加到不可擴展對象將靜默失敗或拋出TypeError(在strict mode下) 當咱們在嚴格模式下,嘗試對不可拓展的對象進行屬性添加時,就會拋出異常,具體代碼以下:java

"use strict"
var obj = {
    age: 23,
    name: 'rioli',
    city: ['sz', 'jy']
};
Object.preventExtensions(obj);
obj.province = 'GD';
複製代碼

運行結果:瀏覽器

Uncaught TypeError: Cannot add property 'province', object is not extensible 
複製代碼

Object.freeze

Object.freeze() 方法能夠凍結一個對象,凍結指的是不能向這個對象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對象已有屬性的可枚舉性、可配置性、可寫性。該方法返回被凍結的對象。 當咱們在嚴格模式下,嘗試對已凍結的對象進行屬性修改時,就會拋出異常,具體代碼以下:bash

"use strict"
var obj = {
    age: 23,
    name: 'rioli',
    city: ['sz', 'jy']
};
Object.freeze(obj);
obj.age = 26;
複製代碼

運行結果:前端構建

Uncaught TypeError: Cannot assign to read only property 'age' of object '#<Object>'
複製代碼

構建不可拓展的凍結對象

咱們能夠利用Object.preventExtensionsObject.freeze這兩個API來組合構建一個不可拓展的凍結對象,即:不能對對象的頂級屬性對象增、刪、改等操做。模塊化

"use strict"
var obj = {
    age: 23,
    name: 'rioli',
    city: ['sz', 'jy']
};
Object.freeze(obj);
Object.preventExtensions(obj);
// obj.age = 26; 修改頂級屬性將引起報錯
// obj.province = 'GD'; 新增頂級屬性將引起報錯
// delete obj.name; 刪除頂級屬性將引起報錯
// 但仍然能夠修改對象的子對象的屬性,由於修改對象的子對象的屬性並不會修改子對象的引用,對於引用類型來講等於沒有發生值的改變
obj.city[0] = 'zq';
obj.city.push('st');
複製代碼

ES Module禁忌之不能夠修改總體導入模塊對象的直接屬性

爲何會列出這個禁忌,這是由於在CommonJS方案中,你是能夠修改總體導入模塊對象的直接屬性的,長期在CommonJSES Module中交叉使用,不免會形成沒必要要的記憶混淆。可是在 ES Module中,總體導入的模塊對象是一個不可拓展的凍結的常量對象,對其直接屬性的修改和新增將引起報錯。工具

假定咱們正處於CommonJs環境下,例如NodeJS中,咱們導入等個模塊,而後嘗試對模塊對象的屬性進行修改和新增,具體代碼以下:ui

lib.jsspa

exports.time = Date.now();
exports.getCurrrentYear = function () {
    return new Date().getFullYear();
}
exports.people = {
    age: 26,
    name: 'rioli',
    cities: ['jieyang', 'shenzhen']
};
複製代碼

main.js

const lib = require('./lib');
const people = lib.people;

const print = (data) => {
    console.log("===================================");
    console.log(data);
};

print(people);
print(lib);

people.age = 999;
print(people);

people.father = 'baba';
print(people);

lib.people = 23;
print(lib);

lib.people.age = 666;
print(lib);

lib.provices = ['GD', 'FJ'];
print(lib);
複製代碼

運行結果經過,控制檯輸出結果以下:

===================================
{ age: 26, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] }
===================================
{ time: 1545274516494,
  getCurrrentYear: [Function],
  people: { age: 26, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] } }
===================================
{ age: 999, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] }
===================================
{ age: 999,
  name: 'rioli',
  cities: [ 'jieyang', 'shenzhen' ],
  father: 'baba' }
===================================
{ time: 1545274516494, getCurrrentYear: [Function], people: 23 }
===================================
{ time: 1545274516494, getCurrrentYear: [Function], people: 23 }
===================================
{ time: 1545274516494,
  getCurrrentYear: [Function],
  people: 23,
  provices: [ 'GD', 'FJ' ] }
複製代碼

ES Module環境下,總體導入的模塊對象,但咱們對其進行修改和新增屬性的時候,其表現和上述利用Object.preventExtensionsObject.freeze這兩個API來組合構建一個不可拓展的凍結對象的表現同樣,即:這個模塊對象是一個不可拓展的凍結的常量對象。演示代碼以下:

lib.mjs

export const time = Date.now();

export function getCurrrentYear() {
    return new Date().getFullYear();
}

export const people = {
    age: 26,
    name: 'rioli',
    cities: ['jieyang', 'shenzhen']
};
複製代碼

main.mjs

import { people } from './lib.mjs';
import * as lib from './lib.mjs';

// 首先定一個基調,其實但咱們用 import * as xx from 'yy' 將一個模塊作總體導入時,此時xx會做爲做爲模塊命名空間

const print = (data) => {
    console.log("===================================");
    console.log(data);
};

print(people);
print(lib);

// 容許修改
people.age = 999;
print(people);

// 容許修改
people.father = 'baba';
print(people);

// 不容許修改,由於做爲總體導入時,模塊對象的直接一級屬性是隻讀屬性,修改將引起報錯
try {
    lib.people = 23;
    print(lib);
} catch (e) {
    print(e);
    print("不容許修改lib.people,由於做爲總體導入時,模塊對象的直接一級屬性是隻讀屬性,修改將引起報錯");
}

// 容許修改,由於做爲總體導入時,模塊對象的直接一級屬性是隻讀屬性,但一級
// 屬性引用的對象不是隻讀對象,能夠修改其屬性(只要沒有被設置爲不可寫)
lib.people.age = 666;
print(lib);

// 總體導入時,不容許給模塊對象新增屬性,由於總體導入時的模塊對象是一個不可拓展的對象,不能夠給模塊對象新增任何屬性
try {
    lib.provices = ['GD', 'FJ'];
    print(lib);
} catch (e) {
    print(e);
    print("不容許對lib對象新增屬性,總體導入時,不容許給模塊對象新增屬性,由於總體導入時的模塊對象是一個不可拓展的對象,不能夠給模塊對象新增任何屬性");
}
複製代碼

具體運行結果:

===================================
{ age: 26, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] }
===================================
{ getCurrrentYear: [Function: getCurrrentYear],
  people: { age: 26, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] },
  time: 1545275257126 }
===================================
{ age: 999, name: 'rioli', cities: [ 'jieyang', 'shenzhen' ] }
===================================
{ age: 999,
  name: 'rioli',
  cities: [ 'jieyang', 'shenzhen' ],
  father: 'baba' }
===================================
TypeError: Cannot assign to read only property 'people' of object '[object Module]'
    at ModuleJob.run (internal/modules/esm/ModuleJob.js:106:14)
    at <anonymous>
===================================
不容許修改lib.people,由於做爲總體導入時,模塊對象的直接一級屬性是隻讀屬性,修改將引起報錯
===================================
{ getCurrrentYear: [Function: getCurrrentYear],
  people:
   { age: 666,
     name: 'rioli',
     cities: [ 'jieyang', 'shenzhen' ],
     father: 'baba' },
  time: 1545275257126 }
===================================
TypeError: Cannot add property provices, object is not extensible
    at ModuleJob.run (internal/modules/esm/ModuleJob.js:106:14)
    at <anonymous>
===================================
不容許對lib對象新增屬性,總體導入時,不容許給模塊對象新增屬性,由於總體導入時的模塊對象是一個不可拓展的對象,不能夠給模塊對象新增任何屬性
複製代碼
相關文章
相關標籤/搜索