当前位置: 首页 > 后端技术 > Node.js

JS语言特点(下)

时间:2023-04-03 16:56:05 Node.js

包引用其他项目或文件。之所以需要把这个模块单独拿出来,是因为一个语言能否成为气候的关键点之一就是能否模块化;一个项目能否形成可观的体量,也离不开模块化。简单的说就是不同的文件或者工程之间是否可以互相调用。es5和es6都有不同风格的引用方法。开发时要注意自己的开发环境和语法格式。es5共有三种引用方式:AMD、CMD、CommonJS。AsynchronousModuleDefinition),CMD(CommonModuleDefinition),是为解决前端同步引用高延迟导致的“假死”状态而诞生的异步引用方式。就我个人而言,前端开发已经基本完全使用了es6格式的语法,AMD和CMD格式中的引用几乎没有用到。我不会在这里详细解释。有关详细信息,请检查链接。我只讲解后端常用的CommonJS。CommonJS的发展史在这里CommonJS中有exporting和module.exports两种方式,这两种方式是完全等价的,但是大多数时候为了区别于es6export,只使用module.exports来调用流程CommonJS调用是同步调用,在调用时,执行调用文件中的所有可执行部分;同步引用可以忽略后端的传输延迟,但是前端同步+高延迟意味着浏览器要等到接收到文件再进行处理,方便用上面提到的异步加载方式AMD和CMD//fileA.jsexportconsole.log("haoye1")functiona(){console.log("haoye2")}a();module.exports={a}//b.jsimportconstfileA=require("fileA")//控制台输出://haoye1//haoye2的基本用法代码如下//fileA.jsexportfunctiona(){console.log("haoye")}constb=()=>{console.log("haoyeB")}constc=0;module.exports={//本质上是将需要导出的值包装成一个json变量赋值给modules.exportsa:a,b,//json格式,如果变量key和value的名字相同,可以直接简写c}//b.js引入constfileA=require("fileA")fileA.a();//haoyefileA.b();//haoyeB//orconst{a,b}=require("fileA")a();//haoyeB();//haoyeB值调用引用值,以变量形式引入(let,const,var),所以引用的变量是否可以修改取决于变量的定义。一般推荐使用const格式引用;不关联;但是在调用对象类型参数时,是浅拷贝,模块中的变量与参考值同步;//b.jsletcount=1//numberclassletobj={//objectclasscount:1}letplusCount=()=>{count++}letplusCount2=()=>{obj.count++}setTimeout(()=>{console.log(count)//(after1s)2console.log(obj.count)//(after1s)2},1000)module.exports={obj,count,plusCount,plusCount2}//a.jsletmod=require('./b.js')console.log(mod.count)//1console.log(mod.obj.count)//1mod.plusCount()mod.plusCount2()console.log(mod.count)//1console.log(mod.obj.count)//2setTimeout(()=>{mod.count=3mod.obj.count=3console.log(mod.count)//(之后2s)3console.log(mod.obj.count)//(after2s)3},2000)es6es6的导出是一个导出调用过程与CommonJS一样,是同步调用。调用时首先执行调用文件中的所有可执行部分。基本用法多行导出、统一导出、默认导出三种格式//fileA.js//多行导出exportfunctiona(){console.log("haoye")}exportconstb=()=>{console.log("haoyeB")}//fileB.js//统一导出函数a(){console.log("haoye")}constb=()=>{console.log("haoyeB")}export{a,b}//fileC.js//默认导出functiona(){console.log("haoye")}constb=()=>{console.log("haoyeB")}exportdefault{//这个导出方法只能全部调用,不能只调用其中一个functiona,b}//fileD.jsimportdemoimport{a,b}from'fileA'//orfileBa();//haoyeb();//haoyeB//fileE.jsimportdemoimportDfrom'fileD'import{a,b}from'fileD'//报错D.a();//haoyeD.b();//haoyeB值调用默认情况下,所有值都以const变量的形式导入(即,不能修改),但是也可以用来修改object类型变量中的数据,对原模块还是有影响的//fileA.jsexportletcounter={count:1}//fileB.jsimport{counter}from'fileA'counter.count++;console.log(counter.count)//2counter={}//错误:“counter”是只读的异步回调为了保证一个函数的正常执行,它是需要在它执行完成后判断它的结果,判断这个过程执行的函数就是回调函数。例如,一个简单的a+b函数。通常,我们可以这样写:=a+bif(c===(Number(a)+Number(b))){//防止a或b为字符串等其他类型,这里验证onSuccess(c)}else{onFailed(c)}}consta=1,b=2;aPlusB(a,b);//haoye3但是当我想把这个函数独立成一个模块的时候,可以随时随地处理它的结构结果,我们可能会这样写:consta=1,b=2;functionaPlusB(a,b){constc=a+breturnc}constc=aPlusB(a,b);if(c===(Number(a)+Number(b))){onSuccess(c)}else{onFailed(c)}随意处理结果的代价是必须返回结果,判断语句必须写在外面;但是也许你可以这样写functionaPlusB(a,b){constc=a+bif(c===(Number(a)+Number(b))){//防止a或b为其他类型如strings,在这里验证return{c,succes:true}}else{return{c,succes:false}}}consta=1,b=2;constres=aPlusB(a,b);if(res.succes){onSuccess(res.c)}else{onFailed(res.c)}降低了程序肉眼可见的可读性,也需要等待函数执行完毕,否则下一个可能的b+c,c+不能执行对于d等函数,如果接下来要执行的函数根本不使用上一步计算的结果,那么等待在js中就是一个完全没有意义的回调。好在js可以通过参数的形式声明一个函数,这样就不用等回调执行完了再执行下一个函数,于是就有了如下写法aPlusB(a,b,onSuccess,onFailed){constc=a+bif(c===(Number(a)+Number(b))){//防止a或b为字符串等其他类型,这里验证onSuccess(c)}else{onFailed(c)}}functiononSuccess(result){console.log("haoye",result)}functiononFailed(error){console.log("huaiye",error)}consta=1,b=2;aPlusB(a,b,onSuccess,onFailed)可以简化为es6箭头函数形式aPlusB(a,b,(result)=>{console.log("haoye",result)},(error)=>{console.log("huaiye",error)})这样,不影响接下来的b+c,c+d,回调函数的定义更加灵活;但是现在还有一个问题,如果我需要在接下来的b+c中使用这一步的结果,比如我需要a+b+c,那么我可能要写constc=3aPlusB(a,b,(result)=>{aPlusB(result,c,(result2)=>{console.log("haoye",result2)},(error)=>{console.log("huaiye",error)})},(error)=>{console.log("huaiye",error)})这是只需要+c的情况,但是如果我需要求a+b+c+d+e+f...?粗略显示代码constc=3,d=4,e=5aPlusB(a,b,(res)=>{aPlusB(res,c,(res2)=>{aPlusB(res2,d,(res3)=>{aPlusB(res2,d,(res3)=>{{aPlusB(res4,e,(res4)=>{aPlusB(res4,f,res5=>{console.log("好爷",res5)},err=>{console.log("怀爷",err)})},err=>{console.log("怀夜",err)})},err=>{console.log("怀夜",err)})},(err)=>{console.log("Huaiye",err)})},(error)=>{console.log("huaiye",err)})这样就陷入了一种“回调地狱”,最直观的做法就是降低代码的可读性和可维护性使代码臃肿低效,比如err,其实只要任意一步抛出错误,那么后面所有的错误捕获函数都是没有意义的,但是为了程序的稳定性,这些函数是必须要定义的,所以就有了aPromiseines6,翻译成中文是promise,意思是承诺在未来的某个时间点给你返回数据,promise的详细介绍就到这里了,只是对promise的使用做一个简单的说明。Promise有pending/reslove/reject三种状态,pending就是pending,resolve可以理解为成功,reject可以理解为拒绝,那么Promise常用的几个方法代表异步执行成功后数据状态的变化对于reslove,catch的意思异步失败后执行的数据状态变为reject,all表示将多个不相关的Promise封装到一个Promise对象中,然后使用then返回一个数据数组,finally表示无论成功还是失败,所有执行完成。还是上面的a+b函数,这次使用promise实现函数aPlusB(a,b){returnnewPromise((onSuccess,onFailed)=>{constc=a+bif(c===(Number(a)+Number(b))){//防止a或b为字符串等其他类型,这里验证onSuccess(c)}else{onFailed(c)}})}consta=1,b=2;aPlusB(a,b).then(res=>{//成功后执行的函数console.log("haoye",res)},err1=>{//失败后执行的函数,可能没有定义,catch至少需要定义一个,否则抛出的错误是捕获不到的无法预料的问题console.log("huaiye",err2)})那么这个表单将如何处理a+b+c+d+e+f...?consta=1,b=2,c=3,d=4,e=5aPlusB(a,b).then(res=>{//成功后执行的函数returnaPlusB(res,b)//如果return是一般变量,则下一个then的res为变化量;若return为promise变量,则下一个res为promise最终return的变量}).then(res=>{returnaPlusB(res,c)}).然后(res=>{returnaPlusB(res,d)}).then(res=>{returnaPlusB(res,e)}).then(res=>{returnaPlusB(res,f)}).then(res=>{console.log("haoye",res)}).catch(err=>{console.log("huaiye",err)})优点很明显,但也存在问题。当抛出错误的时候,我们可能不知道错误在哪一步,但是我们可以在可能出错但返回newPromise((r,e)=>{})的这个方法中加入onRejected回调函数定义可能不会那么优雅,所以在即将到来的es7标准中(目前es6中有这个功能),可以使用async和await这两个新的语法糖,用普通函数的形式返回复杂的promise类,上面functions可以简写为asyncfunctionaPlusB(a,b){constc=a+bif(c===(Number(a)+Number(b))){returnc//如果正确则返回结果}else{thrownewError(c)//出错就抛出错误}}如果想让这个函数同步运行,需要await关键字constc=awaitaPlusB(a,b)但是只能用await在异步函数中,对于异步函数只能使用单线程js具有高效的运行速度,但它是一种单线程语言。造成了很大的麻烦,因为服务器后台对多线程处理有刚需所以,nodejs在node10之后有一个稳定的worker解决方案来解决多线程的需求,但是worker并没有为js生成第二个线程,但是启动了一个新的进程被创建,进程间通信使用的是json格式数据