数据结构论坛

首页 » 分类 » 分类 » 花了一天总结一下ES6Module模块化
TUhjnbcbe - 2024/8/12 16:15:00

Modul介绍

模块,(Modul),是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。

两个基本的特征:外部特征和内部特征

外部特征是指模块跟外部环境联系的接口(即其他模块或程序调用该模块的方式,包括有输入输出参数、引用的全局变量)和模块的功能内部特征是指模块的内部环境具有的特点(即该模块的局部数据和程序代码)

ES6模块Modul概念

ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

3.ES6(静态加载)对比CommonJS(运行时加载):

CommonJS模块就是对象,输入时必须查找对象属性。//CommonJS模块lt{stat,xists,adfil}=qui(fs);//等同于lt_fs=qui(fs);ltstat=_fs.stat;ltxists=_fs.xists;ltadfil=_fs.adfil;

上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取3个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6模块不是对象,而是通过xport命令显式指定输出的代码,再通过import命令输入。//ES6模块import{stat,xists,adFil}fromfs;

上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即ES6可以在编译时就完成模块加载,效率要比CommonJS模块的加载方式高。

为什么需要模块化

代码抽象代码封装代码复用依赖管理

如果没有模块化,我们代码会怎样?

变量和方法不容易维护,容易污染全局作用域加载资源的方式通过script标签从上到下。依赖的环境主观逻辑偏重,代码较多就会比较复杂。大型项目资源难以维护,特别是多人合作的情况下,资源的引入会让人奔溃

因此,需要一种将JavaScript程序模块化的机制,如

CommonJs(典型代表:nod.js早期)AMD(典型代表:qui.js)CMD(典型代表:sa.js)

AMD

AsynchronousModulDfinition(AMD),异步模块定义,采用异步方式加载模块。所有依赖模块的语句,都定义在一个回调函数中,等到模块加载完成之后,这个回调函数才会运行

代表库为qui.js

/**main.js入口文件/主模块**///首先用config()指定各模块路径和引用名qui.config({basUrl:"js/lib",paths:{"jqury":"jqury.min",//实际路径为js/lib/jqury.min.js"undrsco":"undrsco.min",}});//执行基本操作qui(["jqury","undrsco"],function($,_){//somcodh});

CommonJs

CommonJS是一套Javascript模块规范,用于服务端

//a.jsmodul.xports={foo,bar}//b.jsconst{foo,bar}=qui(./a.js)

其有如下特点:

所有代码都运行在模块作用域,不会污染全局作用域模块是同步加载的,即只有加载完成,才能执行后面的操作模块在首次执行后就会缓存,再次加载只返回缓存结果,如果想要再次执行,可清除缓存qui返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值

既然存在了AMD以及CommonJs机制,ES6的Modul又有什么不一样?

ES6在语言标准的层面上,实现了Modul,即模块功能,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案

CommonJS和AMD模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性

//CommonJS模块lt{stat,xists,adfil}=qui(fs);//等同于lt_fs=qui(fs);ltstat=_fs.stat;ltxists=_fs.xists;ltadfil=_fs.adfil;

ES6设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量

//ES6模块import{stat,xists,adFil}fromfs;

上述代码,只加载3个方法,其他方法不加载,即ES6可以在编译时就完成模块加载

由于编译加载,使得静态分析成为可能。包括现在流行的typScript也是依靠静态分析实现功能

Modul使用

ES6模块内部自动采用了严格模式,这里就不展开严格模式的限制,毕竟这是ES5之前就已经规定好

模块功能主要由两个命令构成:

xport:用于规定模块的对外接口import:用于输入其他模块提供的功能

Modulxport

一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用xport关键字输出该变量

//profil.jsxportvarfirstNam=Michal;xportvarlastNam=Jackson;xportvaryar=;或//建议使用下面写法,这样能瞬间确定输出了哪些变量varfirstNam=Michal;varlastNam=Jackson;varyar=;xport{firstNam,lastNam,yar};

输出函数或类

xportfunctionmultiply(x,y){turnx*y;};

通过as可以进行输出变量的重命名

functionv1(){...}functionv2(){...}xport{v1asstamV1,v2asstamV2,v2asstamLatstVrsion};

importModul

使用xport命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块

//main.jsimport{firstNam,lastNam,yar}from./profil.js;functionstNam(lmnt){lmnt.txtContnt=firstNam++lastNam;}

同样如果想要输入变量起别名,通过as关键字

import{lastNamassurnam}from./profil.js;

当加载整个模块的时候,需要用到星号*

//circl.jsxportfunctionaa(radius){turnMath.PI*radius*radius;}xportfunctioncircumfnc(radius){turn2*Math.PI*radius;}//main.jsimport*ascirclfrom./circl;consol.log(circl)//{aa:aa,circumfnc:circumfnc}

输入的变量都是只读的,不允许修改,但是如果是对象,允许修改属性

import{a}from./xxx.jsa.foo=hllo;//合法操作a={};//SyntaxError:aisad-only;

不过建议即使能修改,但我们不建议。因为修改之后,我们很难差错

import后面我们常接着from关键字,from指定模块文件的位置,可以是相对路径,也可以是绝对路径

import{a}from./a;

如果只有一个模块名,需要有配置文件,告诉引擎模块的位置

import{myMthod}fromutil;

在编译阶段,import会提升到整个模块的头部,首先执行

foo();import{foo}frommy_modul;

多次重复执行同样的导入,只会执行一次

importlodash;importlodash;

上面的情况,大家都能看到用户在导入模块的时候,需要知道加载的变量名和函数,否则无法加载

如果不需要知道变量名或函数就完成加载,就要用到xportdfault命令,为模块指定默认输出

//xport-dfault.jsxportdfaultfunction(){consol.log(foo);}

加载该模块的时候,import命令可以为该函数指定任意名字

//import-dfault.jsimportcustomNamfrom./xport-dfault;customNam();//foo

Modul动态加载

允许您仅在需要时动态加载模块,而不必预先加载所有模块,这存在明显的性能优势

这个新功能允许您将import()作为函数调用,将其作为参数传递给模块的路径。它返回一个promis,它用一个模块对象来实现,让你可以访问该对象的导出

import(/moduls/myModul.mjs).thn((modul)={//Dosomthingwiththmodul.});

Modul复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与xport语句写在一起

xport{foo,bar}frommy_modul;//可以简单理解为import{foo,bar}frommy_modul;xport{foo,bar};

同理能够搭配as、*搭配使用

Modul使用场景

如今,ES6模块化已经深入我们日常项目开发中,像vu、act项目搭建项目,组件化开发处处可见,其也是依赖模块化实现

vu组件

tmplatdivclass="App"组件化开发----模块化/div/tmplatscriptxportdfault{nam:HlloWorld,props:{msg:String}}/script

act组件

functionApp(){turn(divclassNam="App"    组件化开发----模块化/div);}xportdfaultApp;

包括完成一些复杂应用的时候,我们也可以拆分成各个模块

ES6模块的优势

由于ES6模块是编译时加载,使得静态分析成为可能。

静态加载优点:按需加载,编译阶段完成,效率提升。能进一步拓宽JavaScript的语法,比如引入宏(macro)和类型检验(typsystm)这些只能靠静态分析实现的功能。除了静态加载带来的各种好处,ES6模块还有以下好处。不再需要UMD模块格式了,通过各种工具库,服务器和浏览器都会支持ES6模块格式。将来浏览器的新API就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。

Dcorator介绍

Dcorator,即装饰器,从名字上很容易让我们联想到装饰者模式

简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。

ES6中Dcorator功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法

装饰器(Dcorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。许多面向对象的语言都有这项功能,目前有一个提案将其引入了ECMAScript。

装饰器是一种函数,写成

+函数名。它可以放在类和类方法的定义前面。

这里定义一个士兵,这时候他什么装备都没有

classsoldir{}

定义一个得到AK装备的函数,即装饰器

functionstrong(targt){targt.AK=tru}

使用该装饰器对士兵进行增强

strongclasssoldir{}

这时候士兵就有武器了

soldir.AK//tru

上述代码虽然简单,但也能够清晰看到了使用Dcorator两大优点:

代码可读性变强了,装饰器命名相当于一个注释在不改变原有代码情况下,对原来功能进行扩展

Dcorator用法

Docorator修饰对象为下面两种:

类的装饰类属性的装饰

Dcorator类的装饰

当对类本身进行装饰的时候,能够接受一个参数,即类本身

将装饰器行为进行分解,大家能够有个更深入的了解

dcoratorclassA{}//等同于classA{}A=dcorator(A)

A;

下面

tstabl就是一个装饰器,targt就是传入的类,即MyTstablClass,实现了为类添加静态属性

tstablclassMyTstablClass{//...}functiontstabl(targt){targt.isTstabl=tru;}MyTstablClass.isTstabl//tru

如果想要传递参数,可以在装饰器外层再封装一层函数

functiontstabl(isTstabl){turnfunction(targt){targt.isTstabl=isTstabl;}}

tstabl(tru)classMyTstablClass{}MyTstablClass.isTstabl//tru

tstabl(fals)classMyClass{}MyClass.isTstabl//fals

Dcorator类属性的装饰

当对类属性进行装饰的时候,能够接受三个参数:

类的原型对象需要装饰的属性名装饰属性名的描述对象

首先定义一个adonly装饰器

functionadonly(targt,nam,dscriptor){dscriptor.writabl=fals;//将可写属性设为falsturndscriptor;}

使用adonly装饰类的nam方法

classPrson{

adonlynam(){turn`${this.first}${this.last}`}}

相当于以下调用

adonly(Prson.prototyp,nam,dscriptor);

如果一个方法有多个装饰器,就像洋葱一样,先从外到内进入,再由内到外执行

functiondc(id){consol.log(valuatd,id);turn(targt,proprty,dscriptor)=consol.log(xcutd,id);}classExampl{

dc(1)

dc(2)mthod(){}}//valuatd1//valuatd2//xcutd2//xcutd1

外层装饰器

dc(1)先进入,但是内层装饰器

dc(2)先执行

Dcorator注意

装饰器不能用于修饰函数,因为函数存在变量声明情况

varcountr=0;varadd=function(){countr++;};

addfunctionfoo(){}

编译阶段,变成下面

varcountr;varadd;

addfunctionfoo(){}countr=0;add=function(){countr++;};

意图是执行后countr等于1,但是实际上结果是countr等于0

Mixin

在装饰器的基础上,可以实现Mixin模式。所谓Mixin模式,就是对象继承的一种替代方案,中文译为“混入”(mixin),意为在一个对象之中混入另外一个对象的方法。

请看下面的例子。

constFoo={foo(){consol.log(foo)}};classMyClass{}Objct.assign(MyClass.prototyp,Foo);ltobj=nwMyClass();obj.foo()//foo

上面代码之中,对象Foo有一个foo方法,通过Objct.assign方法,可以将foo方法“混入”MyClass类,导致MyClass的实例obj对象都具有foo方法。这就是“混入”模式的一个简单实现。

下面,我们部署一个通用脚本mixins.js,将Mixin写成一个装饰器。

xportfunctionmixins(...list){turnfunction(targt){Objct.assign(targt.prototyp,...list);};}

然后,就可以使用上面这个装饰器,为类“混入”各种方法。

import{mixins}from./mixins;constFoo={foo(){consol.log(foo)}};

mixins(Foo)classMyClass{}ltobj=nwMyClass();obj.foo()//"foo"

通过mixins这个装饰器,实现了在MyClass类上面“混入”Foo对象的foo方法。

Trait

Trait也是一种装饰器,效果与Mixin类似,但是提供更多功能,比如防止同名方法的冲突、排除混入某些方法、为混入的方法起别名等等。

下面采用traits-dcorator这个第三方模块作为例子。这个模块提供的traits装饰器,不仅可以接受对象,还可以接受ES6类作为参数。

import{traits}fromtraits-dcorator;classTFoo{foo(){consol.log(foo)}}constTBar={bar(){consol.log(bar)}};

traits(TFoo,TBar)classMyClass{}ltobj=nwMyClass();obj.foo()//fooobj.bar()//bar

上面代码中,通过traits装饰器,在MyClass类上面“混入”了TFoo类的foo方法和TBar对象的bar方法。

Trait不允许“混入”同名方法。

import{traits}fromtraits-dcorator;classTFoo{foo(){consol.log(foo)}}constTBar={bar(){consol.log(bar)},foo(){consol.log(foo)}};

traits(TFoo,TBar)classMyClass{}//报错//thrownwError(Mthodnamd:+mthodNam+isdfindtwic.);//^//Error:Mthodnamd:fooisdfindtwic.

上面代码中,TFoo和TBar都有foo方法,结果traits装饰器报错。

一种解决方法是排除TBar的foo方法。

import{traits,xcluds}fromtraits-dcorator;classTFoo{foo(){consol.log(foo)}}constTBar={bar(){consol.log(bar)},foo(){consol.log(foo)}};

traits(TFoo,TBar::xcluds(foo))classMyClass{}ltobj=nwMyClass();obj.foo()//fooobj.bar()//bar

上面代码使用绑定运算符(::)在TBar上排除foo方法,混入时就不会报错了。

Dcorator使用场景

基于Dcorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种:

使用act-dux的时候,如果写成下面这种形式,既不雅观也很麻烦

classMyRactComponntxtndsRact.Componnt{}xportdfaultconnct(mapStatToProps,mapDispatchToProps)(MyRactComponnt);

通过装饰器就变得简洁多了

connct(mapStatToProps,mapDispatchToProps)xportdfaultclassMyRactComponntxtndsRact.Componnt{}

将mixins,也可以写成装饰器,让使用更为简洁了

functionmixins(...list){turnfunction(targt){Objct.assign(targt.prototyp,...list);};}//使用constFoo={foo(){consol.log(foo)}};

mixins(Foo)classMyClass{}ltobj=nwMyClass();obj.foo()//"foo"

下面再讲讲co-dcorators.js几个常见的装饰器

Dcorator

antobind

autobind装饰器使得方法中的this对象,绑定原始对象

import{autobind}fromco-dcorators;classPrson{

autobindgtPrson(){turnthis;}}ltprson=nwPrson();ltgtPrson=prson.gtPrson;gtPrson()===prson;//tru

Dcorator

adonly

adonly装饰器使得属性或方法不可写

import{adonly}fromco-dcorators;classMal{

adonlynt=stak;}vardinnr=nwMal();dinnr.nt=salmon;//Cannotassigntoadonlyproprtyntof[objctObjct]

Dcorator

dpcat

dpcat或dpcatd装饰器在控制台显示一条警告,表示该方法将废除

import{dpcat}fromco-dcorators;classPrson{

dpcatfacpalm(){}

dpcat(功能废除了)facpalmHard(){}}ltprson=nwPrson();prson.facpalm();//DEPRECATIONPrson#facpalm:Thisfunctionwillbmovdinfutuvrsions.prson.facpalmHard();//DEPRECATIONPrson#facpalmHard:功能废除了

给大家分享我收集整理的各种学习资料,前端小白交流、学习交流,也可以直接问我,我会组织大家一起做项目练习,帮助大家匹配一位学习伙伴互相监督学习-下面是学习资料参考。

前端学习交流、自学、学习资料等推荐-知乎

1
查看完整版本: 花了一天总结一下ES6Module模块化