170+道钉钉前端扫盲知识点(收藏学习)哈喽比特

主存包括随机存储器RAM和只读存储器ROM,其中ROM又可以分为MROM(一次性)、PROM、EPROM、EEPROM。ROM中存储的程序(例如启动程序、固化程序)和数据(例如常量数据)在断电后不会丢失。RAM主要分为静态RAM(SRAM)和动态RAM(DRAM)两种类型(DRAM种类很多,包括SDRAM、RDRAM、CDRAM等),断电后数据会丢失,主要用于存储临时程序或者临时变量数据。DRAM一般访问速度相对较慢。由于现代CPU读取速度要求相对较高,因此在CPU内核中都会设计L1、L2以及L3级别的多级高速缓存,这些缓存基本是由SRAM构成,一般访问速度较快。

上图右侧主存中的指令是CPU可以支持的处理命令,一般包含算术指令(加和减)、逻辑指令(与、或和非)、数据指令(移动、输入、删除、加载和存储)、流程控制指令以及程序结束指令等,由于CPU只能识别二进制码,因此指令是由二进制码组成。除此之外,指令的集合称为指令集(例如汇编语言就是指令集的一种表现形式),常见的指令集有精简指令集(ARM)和复杂指令集(InterX86)。一般指令集决定了CPU处理器的硬件架构,规定了处理器的相应操作。

温馨提示:感兴趣的同学可以了解一下ARM芯片的程序运行原理,包括使用IDE进行程序的编译(IDE内置编译器,主流编译器包含ARMCC、IAR以及GCCFORARM等,其中一些编译器仅仅随着IDE进行捆绑发布,不提供独立使用的能力,而一些编译器则随着IDE进行发布的同时,还提供命令行接口的独立使用方式)、通过串口进行程序下载(下载到芯片的代码区初始启动地址映射的存储空间地址)、启动的存储空间地址映射(包括系统存储器、闪存FLASH、内置SRAM等)、芯片的程序启动模式引脚BOOT的设置(例如调试代码时常常选择内置SRAM、真正程序运行的时候选择闪存FLASH)等。

如果某种高级语言或者应用语言(例如用于人工智能的计算机设计语言)转换的目标语言不是特定计算机的汇编语言,而是面向另一种高级程序语言(很多研究性的编译器将C作为目标语言),那么还需要将目标高级程序语言再进行一次额外的编译才能得到最终的目标程序,这种编译器可称为源到源的转换器。

除此之外,有些程序设计语言将编译的过程和最终转换成目标程序进行执行的过程混合在一起,这种语言转换程序通常被称为解释器,主要作用是将某种语言编写的源程序作为输入,将该源程序执行的结果作为输出,解释器的作用如下图所示:

解释器和编译器有很多相似之处,都需要对源程序进行分析,并转换成目标机器可识别的机器语言进行执行。只是解释器是在转换源程序的同时立马执行对应的机器语言(转换和执行的过程不分离),而编译器得先把源程序全部转换成机器语言并产生目标文件,然后将目标文件写入相应的程序存储器进行执行(转换和执行的过程分离)。例如Perl、Scheme、APL使用解释器进行转换,C、C++则使用编译器进行转换,而Java和JavaScript的转换既包含了编译过程,也包含了解释过程。

JavaScript中的数组存储大致需要分为两种情况:

温馨提示:可以想象一下连续的内存空间只需要根据索引(指针)直接计算存储位置即可。如果是哈希映射那么首先需要计算索引值,然后如果索引值有冲突的场景下还需要进行二次查找(需要知道哈希的存储方式)。

编译器的设计是一个非常庞大和复杂的软件系统设计,在真正设计的时候需要解决两个相对重要的问题:

阅读链接:基于Vue实现一个简易MVVM[5]-观察者模式和发布/订阅模式

编程范式(Programmingparadigm)是指计算机编程的基本风格或者典型模式,可以简单理解为编程学科中实践出来的具有哲学和理论依据的一些经典原型。常见的编程范式有:

阅读链接::如果你对于编程范式的定义相对模糊,可以继续阅读Whatistheprecisedefinitionofprogrammingparadigm\[6]了解更多。

不同的语言可以支持多种不同的编程范式,例如C语言支持POP范式,C++和Java语言支持OOP范式,Swift语言则可以支持FP范式,而Web前端中的JavaScript可以支持上述列出的所有编程范式。

顾名思义,函数式编程是使用函数来进行高效处理数据或数据流的一种编程方式。在数学中,函数的三要素是定义域、值域和**对应关系。假设A、B是非空数集,对于集合A中的任意一个数x,在集合B中都有唯一确定的数f(x)和它对应,那么可以将f称为从A到B的一个函数,记作:y=f(x)。在函数式编程中函数的概念和数学函数的概念类似,主要是描述形参x和返回值y之间的对应关系,**如下图所示:

温馨提示:图片来自于简明JavaScript函数式编程——入门篇[7]。

简单示例

尽管你对函数式编程的概念有所了解,但是你仍然不知道函数式编程到底有什么特点。这里我们仍然拿OOP编程范式来举例,假设希望通过OOP编程来解决数学的加减乘除问题:

classMathObject{constructor(privatevalue:number){}publicadd(num:number):MathObject{this.value+=num;returnthis;}publicmultiply(num:number):MathObject{this.value*=num;returnthis;}publicgetValue():number{returnthis.value;}}consta=newMathObject(1);a.add(1).multiply(2).add(a.multiply(2).getValue());复制代码我们希望通过上述程序来解决(1+2)*2+1*2的问题,但实际上计算出来的结果是24,因为在代码内部有一个this.value的状态值需要跟踪,这会使得结果不符合预期。接下来我们采用函数式编程的方式:

functionadd(a:number,b:number):number{returna+b;}functionmultiply(a:number,b:number):number{returna*b;}consta:number=1;constb:number=2;add(multiply(add(a,b),b),multiply(a,b));复制代码以上程序计算的结果是8,完全符合预期。我们知道了add和multiply两个函数的实际对应关系,通过将对应关系进行有效的组合和传递,达到了最终的计算结果。除此之外,这两个函数还可以根据数学定律得出更优雅的组合方式:

add(multiply(add(a,b),b),multiply(a,b));//根据数学定律分配律:a*b+a*c=a*(b+c),得出://(a+b)*b+a*b=(2a+b)*b//简化上述函数的组合方式multiply(add(add(a,a),b),b);复制代码我们完全不需要追踪类似于OOP编程范式中可能存在的内部状态数据,事实上对于数学定律中的结合律、交换律、同一律以及分配律,上述的函数式编程代码足可以胜任。

原则

通过上述简单的例子可以发现,要实现高可复用的函数**(对应关系)**,一定要遵循某些特定的原则,否则在使用的时候可能无法进行高效的传递和组合,例如

如果你之前经常进行无原则性的代码设计,那么在设计过程中可能会出现各种出乎意料的问题(这是为什么新手老是出现一些稀奇古怪问题的主要原因)。函数式编程可以有效的通过一些原则性的约束使你设计出更加健壮和优雅的代码,并且在不断的实践过程中进行经验式叠加,从而提高开发效率。

特点

一等公民

在JavaScript中,函数的使用非常灵活,例如可以对函数进行以下操作:

纯函数

纯函数是是指在相同的参数调用下,函数的返回值唯一不变。这跟数学中函数的映射关系类似,同样的x不可能映射多个不同的y。使用函数式编程会使得函数的调用非常稳定,从而降低Bug产生的机率。当然要实现纯函数的这种特性,需要函数不能包含以下一些副作用:

从以上常见的一些副作用可以看出,纯函数的实现需要遵循最小意外原则,为了确保函数的稳定唯一的输入和输出,尽量应该避免与函数外部的环境进行任何交互行为,从而防止外部环境对函数内部产生无法预料的影响。纯函数的实现应该自给自足,举几个例子:

可缓存性和可测试性基于纯函数输入输出唯一不变的特性,可移植性则主要基于纯函数不依赖外部环境的特性。这里举一个可缓存的例子:

interfaceICache{[arg:string]:T;}interfaceISquare{(x:T):T;}//简单的缓存函数(忽略通用性和健壮性)functionmemoize(fn:ISquare):ISquare{constcache:ICache={};returnfunction(x:T){constarg:string=JSON.stringify(x);cache[arg]=cache[arg]||fn.call(fn,x);returncache[arg];};}//纯函数functionsquare(x:number):number{returnx*x;}constmemoSquare=memoize(square);memoSquare(4);//不会再次调用纯函数square,而是直接从缓存中获取值//由于输入和输出的唯一性,获取缓存结果可靠稳定//提升代码的运行效率memoSquare(4);复制代码无状态和数据不可变

在函数式编程的简单示例中已经可以清晰的感受到函数式编程绝对不能依赖内部状态,而在纯函数中则说明了函数式编程不能依赖外部的环境或状态,因为一旦依赖的状态变化,不能保证函数根据对应关系所计算的返回值因为状态的变化仍然保持不变。

这里单独讲解一下数据不可变,在JavaScript中有很多数组操作的方法,举个例子:

constarr=[1,2,3];console.log(arr.slice(0,2));//[1,2]console.log(arr);//[1,2,3]console.log(arr.slice(0,2));//[1,2]console.log(arr);//[1,2,3]console.log(arr.splice(0,1));//[1]console.log(arr);//[2,3]console.log(arr.splice(0,1));//[2]console.log(arr);//[3]复制代码这里的slice方法多次调用都不会改变原有数组,且会产生相同的输出。而splice每次调用都在修改原数组,且产生的输出也不相同。在函数式编程中,这种会改变原有数据的函数已经不再是纯函数,应该尽量避免使用。

阅读链接:如果想要了解更深入的函数式编程知识点,可以额外阅读函数式编程指北[8]。

ThedirectionCSSpropertysetsthedirectionoftext,tablecolumns,andhorizontaloverflow.Usertlforlanguageswrittenfromrighttoleft(likeHebreworArabic),andltrforthosewrittenfromlefttoright(likeEnglishandmostotherlanguages).

具体查看:developer.mozilla.org/en-US/docs/…[9]

Theconsoleobjectprovidesaccesstothebrowser'sdebuggingconsole(e.g.theWebconsole[10]inFirefox).Thespecificsofhowitworksvariesfrombrowsertobrowser,butthereisadefactosetoffeaturesthataretypicallyprovided.

这里列出一些我常用的API:

具体查看:developer.mozilla.org/en-US/docs/…[11]

在JavaScript中利用事件循环机制[12](EventLoop)可以在单线程中实现非阻塞式、异步的操作。例如

我们重点来看一下常用的几种编程方式(Callback、Promise、Generator、Async)在语法糖上带来的优劣对比。

Callback

Callback(回调函数)是在Web前端开发中经常会使用的编程方式。这里举一个常用的定时器示例:

exportinterfaceIObj{value:string;deferExec():void;deferExecAnonymous():void;console():void;}exportconstobj:IObj={value:'hello',deferExecBind(){//使用箭头函数可达到一样的效果setTimeout(this.console.bind(this),1000);},deferExec(){setTimeout(this.console,1000);},console(){console.log(this.value);},};obj.deferExecBind();//helloobj.deferExec();//undefined复制代码回调函数经常会因为调用环境的变化而导致this的指向性变化。除此之外,使用回调函数来处理多个继发的异步任务时容易导致回调地狱(CallbackHell):

fs.readFile(fileA,'utf-8',function(err,data){fs.readFile(fileB,'utf-8',function(err,data){fs.readFile(fileC,'utf-8',function(err,data){fs.readFile(fileD,'utf-8',function(err,data){//假设在业务中fileD的读写依次依赖fileA、fileB和fileC//或者经常也可以在业务中看到多个HTTP请求的操作有前后依赖(继发HTTP请求)//这些异步任务之间纵向嵌套强耦合,无法进行横向复用//如果某个异步发生变化,那它的所有上层或下层回调可能都需要跟着变化(比如fileA和fileB的依赖关系倒置)//因此称这种现象为回调地狱//....});});});});复制代码回调函数不能通过return返回数据,比如我们希望调用带有回调参数的函数并返回异步执行的结果时,只能通过再次回调的方式进行参数传递:

//希望延迟3s后执行并拿到结果functiongetAsyncResult(result:number){setTimeout(()=>{returnresult*3;},1000);}//尽管这是常规的编程思维方式constresult=getAsyncResult(3000);//但是打印undefinedconsole.log('result:',result);functiongetAsyncResultWithCb(result:number,cb:(result:number)=>void){setTimeout(()=>{cb(result*3);},1000);}//通过回调的形式获取结果getAsyncResultWithCb(3000,(result)=>{console.log('result:',result);//9000});复制代码对于JavaScript中标准的异步API可能无法通过在外部进行try...catch...的方式进行错误捕获:

try{setTimeout(()=>{//下述是异常代码//你可以在回调函数的内部进行try...catch...console.log(a.b.c)},1000)}catch(err){//这里不会执行//进程会被终止console.error(err)}复制代码上述示例讲述的都是JavaScript中标准的异步API,如果使用一些三方的异步API并且提供了回调能力时,这些API可能是非受信的,在真正使用的时候会因为执行反转(回调函数的执行权在三方库中)导致以下一些问题:

举个简单的例子:

interfaceILib{params:T;emit(params:T):void;on(callback:(params:T)=>void):void;}//假设以下是一个三方库,并发布成了npm包exportconstlib:ILib={params:'',emit(params){this.params=params;},on(callback){try{//callback回调执行权在lib上//lib库可以决定回调执行多次callback(this.params);callback(this.params);callback(this.params);//lib库甚至可以决定回调延迟执行//异步执行回调函数setTimeout(()=>{callback(this.params);},3000);}catch(err){//假设lib库的捕获没有抛出任何异常信息}},};//开发者引入lib库开始使用lib.emit('hello');lib.on((value)=>{//使用者希望on里的回调只执行一次//这里的回调函数的执行时机是由三方库lib决定//实际上打印四次,并且其中一次是异步执行console.log(value);});lib.on((value)=>{//下述是异常代码//但是执行下述代码不会抛出任何异常信息//开发者无法感知自己的代码设计错误console.log(value.a.b.c)});复制代码Promise

Callback的异步操作形式除了会造成回调地狱,还会造成难以测试的问题。ES6中的Promise(基于PromiseA+[21]规范的异步编程解决方案)利用有限状态机[22]的原理来解决异步的处理问题,Promise对象提供了统一的异步编程API,它的特点如下:

温馨提示:有限状态机提供了一种优雅的解决方式,异步的处理本身可以通过异步状态的变化来触发相应的操作,这会比回调函数在逻辑上的处理更加合理,也可以降低代码的复杂度。

Promise对象的执行状态不可变示例如下:

constpromise=newPromise((resolve,reject)=>{//状态变更为fulfilled并返回结果1后不会再变更状态resolve(1);//不会变更状态reject(4);});promise.then((result)=>{//在ES6中Promise的then回调执行是异步执行(微任务)//在当前then被调用的那轮事件循环(EventLoop)的末尾执行console.log('result:',result);}).catch((error)=>{//不执行console.error('error:',error);});复制代码假设要实现两个继发的HTTP请求,第一个请求接口返回的数据是第二个请求接口的参数,使用回调函数的实现方式如下所示(这里使用setTimeout来指代异步请求):

//回调地狱constdoubble=(result:number,callback:(finallResult:number)=>void)=>{//Mock第一个异步请求setTimeout(()=>{//Mock第二个异步请求(假设第二个请求的参数依赖第一个请求的返回结果)setTimeout(()=>{callback(result*2);},2000);},1000);};doubble(1000,(result)=>{console.log('result:',result);});复制代码温馨提示:继发请求的依赖关系非常常见,例如人员基本信息管理系统的开发中,经常需要先展示组织树结构,并默认加载第一个组织下的人员列表信息。

如果采用Promise的处理方式则可以规避上述常见的回调地狱问题:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求//将resolve改成reject会被catch捕获setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求//将resolve改成reject会被catch捕获setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{returnnextPromise(result);}).then((result)=>{//2s后打印2000console.log('result:',result);})//任何一个Promise到达rejected状态都能被catch捕获.catch((err)=>{console.error('err:',err);});复制代码Promise的错误回调可以同时捕获firstPromise和nextPromise两个函数的rejected状态。接下来考虑以下调用场景:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{nextPromise(result).then((result)=>{//后打印console.log('nextPromiseresult:',result);});}).then((result)=>{//先打印//由于上一个then没有返回值,这里打印undefinedconsole.log('firstPromiseresult:',result);}).catch((err)=>{console.error('err:',err);});复制代码首先Promise可以注册多个then(放在一个执行队列里),并且这些then会根据上一次返回值的结果依次执行。除此之外,各个Promise的then执行互不干扰。我们将示例进行简单的变换:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{//返回了nextPromise的then执行后的结果returnnextPromise(result).then((result)=>{returnresult;});})//接着nextPromise的then执行的返回结果继续执行.then((result)=>{//2s后打印2000console.log('nextPromiseresult:',result);}).catch((err)=>{console.error('err:',err);});复制代码上述例子中的执行结果是因为then的执行会返回一个新的Promise对象,并且如果then执行后返回的仍然是Promise对象,那么下一个then的链式调用会等待该Promise对象的状态发生变化后才会调用(能得到这个Promise处理的结果)。接下来重点看下Promise的错误处理:

constpromise=newPromise((resolve,reject)=>{//下述是异常代码console.log(a.b.c);resolve('hello');});promise.then((result)=>{console.log('result:',result);})//去掉catch仍然会抛出错误,但不会退出进程终止脚本执行.catch((err)=>{//执行//ReferenceError:aisnotdefinedconsole.error(err);});setTimeout(()=>{//继续执行console.log('helloworld!');},2000);复制代码从上述示例可以看出Promise的错误不会影响其他代码的执行,只会影响Promise内部的代码本身,因为Promise会在内部对错误进行异常捕获,从而保证整体代码执行的稳定性。Promise还提供了其他的一些API方便多任务的执行,包括

Promise相对于Callback对于异步的处理更加优雅,并且能力也更加强大,但是也存在一些自身的缺点:

温馨提示:手写Promise是面试官非常喜欢的一道笔试题,本质是希望面试者能够通过底层的设计正确了解Promise的使用方式,如果你对Promise的设计原理不熟悉,可以深入了解一下或者手动设计一个。

Generator

Promise解决了Callback的回调地狱问题,但也造成了代码冗余,如果一些异步任务不支持Promise语法,就需要进行一层Promise封装。Generator将JavaScript的异步编程带入了一个全新的阶段,它使得异步代码的设计和执行看起来和同步代码一致。Generator使用的简单示例如下:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};//在Generator函数里执行的异步代码看起来和同步代码一致function*gen(result:number):Generator,Promise,number>{//异步代码constfirstResult=yieldfirstPromise(result)console.log('firstResult:',firstResult)//2//异步代码constnextResult=yieldnextPromise(firstResult)console.log('nextResult:',nextResult)//6returnnextPromise(firstResult)}constg=gen(1)//手动执行Generator函数g.next().value.then((res:number)=>{//将firstPromise的返回值传递给第一个yield表单式对应的firstResultreturng.next(res).value}).then((res:number)=>{//将nextPromise的返回值传递给第二个yield表单式对应的nextResultreturng.next(res).value})复制代码通过上述代码,可以看出Generator相对于Promise具有以下优势:

next可以不停的改变状态使得yield得以继续执行的代码可以变得非常有规律,例如从上述的手动执行Generator函数可以看出,完全可以将其封装成一个自动执行的执行器,具体如下所示:

需要注意的是Generator函数的返回值是一个Iterator遍历器对象,具体如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};typeGen=Generator>;function*gen():Gen{yieldfirstPromise(1);yieldnextPromise(2);}//注意使用next是继发执行,而这里是并发执行Promise.all([...gen()]).then((res)=>{console.log('res:',res);});for(constpromiseofgen()){promise.then((res)=>{console.log('res:',res);});}复制代码Generator函数的错误处理相对复杂一些,极端情况下需要对执行和Generator函数进行双重错误捕获,具体如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//需要注意这里的reject没有被捕获setTimeout(()=>reject(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};typeGen=Generator>;function*gen():Gen{try{yieldfirstPromise(1);yieldnextPromise(2);}catch(err){console.error('Generator函数错误捕获:',err);}}try{constg=gen();g.next();//返回Promise后还需要通过Promise.prototype.catch进行错误捕获g.next();//Generator函数错误捕获g.throw('err');//执行器错误捕获g.throw('err');}catch(err){console.error('执行错误捕获:',err);}复制代码在使用g.throw的时候还需要注意以下一些事项:

Async

Async是Generator函数的语法糖,相对于Generator而言Async的特性如下:

举个简单的示例:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){constfirstResult=awaitfirstPromise(1);//1s后打印2console.log('firstResult:',firstResult);//等待firstPromise的状态发生变化后执行constnextResult=awaitnextPromise(firstResult);//2s后打印6console.log('nextResult:',nextResult);returnnextResult;}co();co().then((res)=>{console.log('res:',res);//6});复制代码通过上述示例可以看出,async函数的特性如下:

上述代码是阻塞式执行,nextPromise需要等待firstPromise执行完成后才能继续执行,如果希望两者能够并发执行,则可以进行下述设计:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){returnawaitPromise.all([firstPromise(1),nextPromise(1)]);}co().then((res)=>{console.log('res:',res);//[2,3]});复制代码除了使用Promise自带的并发执行API,也可以通过让所有的Promise提前并发执行来处理:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{console.log('firstPromise');setTimeout(()=>resolve(result*2),10000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{console.log('nextPromise');setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){//执行firstPromiseconstfirst=firstPromise(1);//和firstPromise同时执行nextPromiseconstnext=nextPromise(1);//等待firstPromise结果回来constfirstResult=awaitfirst;console.log('firstResult:',firstResult);//等待nextPromise结果回来constnextResult=awaitnext;console.log('nextResult:',nextResult);returnnextResult;}co().then((res)=>{console.log('res:',res);//3});复制代码Async的错误处理相对于Generator会更加简单,具体示例如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Promise决断错误setTimeout(()=>reject(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){constfirstResult=awaitfirstPromise(1);console.log('firstResult:',firstResult);constnextResult=awaitnextPromise(1);console.log('nextResult:',nextResult);returnnextResult;}co().then((res)=>{console.log('res:',res);}).catch((err)=>{console.error('err:',err);//err:2});复制代码async函数内部抛出的错误,会导致函数返回的Promise对象变为rejected状态,从而可以通过catch捕获,上述代码只是一个粗粒度的容错处理,如果希望firstPromise错误后可以继续执行nextPromise,则可以通过try...catch...在async函数里进行局部错误捕获:

业务思考更多的是结合基础知识的广度和深度进行的具体业务实践,主要包含以下几个方面:

笔试更多的是考验应聘者的逻辑思维能力和代码书写风格,主要包含以下几个方面:

THE END
1.UI界面设计专业:解锁数字世界的视觉艺术基础技能学习在学习UI界面设计之前,需要掌握一定的基础技能,如色彩理论、构图原则、设计软件操作等。这些基础技能将为后续的专业学习打下坚实的基础。 专业知识学习在掌握基础技能后,需要深入学习UI界面设计的专业知识,包括交互设计原理、用户体验设计、界面设计规范等。通过系统学习,可以掌握UI设计的核心方法和技巧。 https://www.pbids.com/aboutUs/pbidsNews/1861306730463465472
2.UI设计就业与薪资在数字化浪潮汹涌澎湃的当今时代,UI 设计宛如一颗璀璨的明星,闪耀于科技与创意的浩瀚苍穹之中。它不仅是构建精美用户界面的艺术魔法,更是连接用户与产品、服务的情感桥梁。随着各类应用和网站如雨后春笋般不断涌现,UI 设计的重要性愈发凸显,其就业市场也成为众多求职者和行业观察者瞩目的焦点。那么,在这充满机遇与挑战https://blog.csdn.net/2403_89603523/article/details/144407257
3.深入分析App原生混合与纯Web开发模式的优缺点比较原生应用特别适合游戏、电子杂志、管理软件、物联网等这类不常需要更新程序架构的APP。在游戏界,尤其是大型3D游戏,它们需要充分利用手机的图形处理和操作感应等硬件功能,原生应用便能很好地满足这些需求。以日本的一些游戏开发企业为例,他们为了确保游戏在手机上的最佳性能体验,通常会选择开发原生应用。 https://www.7claw.com/2802947.html
4.UI设计是指对软件的人机交互操作逻辑界面美观的整体设计由于目前绝大部分高校都未开设UI设计方面的专业,所以社会上诞生了很多专门培养UI设计人才的培训机构,即使是设计类专业毕业生大都也要进行培训方可。 该怎么选UI设计培训机构?下面是总结的一些经验,大家可以作为参考。 一、确定自己的需求和学习方式: 要想选择一个合适的UI设计培训机构,要先明确自己学习UI设计的目标,以https://www.163.com/dy/article/JJ08NAC40556B224.html
5.第三波!2022年5月精选实用设计干货合集优设网五月第三波设计干货合集来了!这次的合集当中,很多工具和素材是相辅相承的,比如图片压缩、格式转换、去除图片背景、移除图片内容的几款工具可以搭配在一起使用,给 Figma 的开源图标合集、Web UI 设计系统和框架以及高素质的 Lottie 动画素材库也可以搭配到一起使用。当然,前2期合集里也有宝藏: https://www.uisdc.com/2022-5-design-resources-vol3/
6.app开发工程师和web前端开发工程师有什么区别职位职责: 1.负责互联网产品前端设计开发; 2.参与设计体验、流程的制定和规范,组织设计评审; 3.负责与设计师和后台程序配合,高效率高质量地完成页面的实现工作。 Web前端开发工程师 来自东升 更新于 2024-12-13 查看更多岗位职责 工资待遇区别 岗位名称 平均工资 较上年 app开发工程师 ¥15.4K +8% https://www.jobui.com/gangwei/pk/appkaifagongchengshi-webqianduankaifagongchengshi/
7.进击大数据系列(九)Hadoop实时计算流计算引擎Flink根据到来的事件流触发计算、状态更新或其他外部动作,主要应用实例有反欺诈、异常检测、基于规则的报警、业务流程监控、(社交网络)Web应用等。 传统应用和事件驱动型应用架构的区别如图: 数据分析 从原始数据中提取有价值的信息和指标,这些信息和指标数据可以写入外部数据库系统或以内部状态的形式维护,主要应用实例有电信网https://cloud.tencent.com/developer/article/2315251
8.2022软件测试面试题大全(整理版)1000+面试题附答案详解,最全面进大厂是大部分程序员的梦想,而进大厂的门槛也是比较高的,所以这里整理了一份阿里、美团、滴滴、头条等大厂面试大全,其中概括的知识点有:软件测试基础,MySQL,liunx,接口测试,APP测试,性能测试,selenium,Python,lordrunner,计算机网络,组成原理,数据结构与算法,逻辑题,人力资源等共有1000+道面试题。 https://maimai.cn/article/detail?fid=1700715497&efid=tA7eC1eCPIrzCZI7LISXLQ
9.UI设计师岗位职责(精选45篇)在不断进步的社会中,岗位职责在生活中的使用越来越广泛,制定岗位职责能够有效的地防止因为职位分配不合理而导致部门之间或是员工之间出现工作推脱、责任推卸等现象发生。我敢肯定,大部分人都对制定岗位职责很是头疼的,下面是小编精心整理的UI设计师岗位职责,供大家参考借鉴,希望可以帮助到有需要的朋友。 https://www.ruiwen.com/gangweizhize/6147816.html
10.FrozenUI·专注于移动web的UI框架,基于腾讯手机QQ规范.FrozenUI 是一套基于移动端的UI库 轻量、精美、遵从手机 QQ 设计规范 开始使用立即下载 当前版本 v2.0.0 简单易用,轻量快捷 适用于使用手Q规范设计的Web页面,而针对非手Q规范的页面,可通过修改变量定制界面主题。 可以按需选择需要的组件,也可以采用cdn和combo的方式按需加载。 http://frozenui.github.io/
11.ASP.NETCore适用于.NET的开源Web框架用于使用 .NET 和 C# 构建 Web 应用和服务的框架。 开始 Windows、Linux 和 macOS 上均支持 使用一个堆栈快速生成 Web 应用 ASP.NET Core 附带生成 Web 应用所需的一切内容,其中包括 Web UI 框架 Blazor,可用于快速创建在任何位置运行的快速、安全且可靠的 Web 应用。 https://asp.net/
12.设计圈正在热议的18大UI设计网站,火爆!无论你是经验丰富的UI设计师还是刚刚起步的UI设计萌新,拥有一些优质可靠的UI设计网站灵感库可以加快你的设计工作流程。借助灵感资源的案例,你可以更快、更有效地开启项目。同时,优秀的UI设计网站还可以帮助你探索新的设计解决方案,跟上设计潮流趋势并推动你的创造力。本https://pixso.cn/designskills/18-ui-design-websites/
13.WebUI之PO设计模式与UnittestPO模式就是将将页面元素的定位与操作方法与各个页面上的元素分离,提高代码的复用率,降低代码的编写量。同时抽离业务逻辑,与数据文件,降低代码的耦合性。核心还是面向对象编程,通过子类继承父类的特性,达到使界面代码简洁的目的。 基本页面写常用的元素定位与元素操作,其他页面通过继承基本页面类,再添加该页面独特的元素https://www.jianshu.com/p/14f759f8613b
14.ui设计如何和web前端结合?Worktile社区静态页面的制作:Web前端工程师将UI设计师输出的静态页面进行切割,根据设计需求进行代码实现,实现页面的动态效果和响应式布局。 页面的交互效果:Web前端工程师根据UI设计的需求实现页面的交互效果,包括按钮的点击事件、表单的验证等。 响应式设计的实现:UI设计师和Web前端工程师共同关注页面的响应式设计,确保页面在不同设https://worktile.com/kb/ask/810616.html
15.第6页个人简历范文简历填写技巧求职面试指南基本信息,姓名:简历本,意向:web前端,工作经历,工作描述:负责公司旗下直销人网(www.zhixiaoren.com)及哪里玩(www.naliwan.com)旅游网站UI设计及前端开发工作。主要负责工作如下:1、直销人网、哪里玩旅游网pc端的ui设计及前端开发2、直销人网、哪里玩旅游网(部分)手机端ui设计与前端开发3、公司直销通app、直销帮https://www.jianliben.com/article/page_6
16.邢台工程职业技术学校冀中工程技师学院(北校区)就业方向:Web前端工程师、全栈工程师、项目经理、小程序开发工程师、创业(Web前端因为编写速度快、快平台,是创业技术实现!) 薪资:5000+ 2.UI设计 UI设计:ui设计的本义是用户界面,是指从事对软件的人机交互、操作逻辑、界面美观的整体设计工作的人,从工作内容上来分,一般分为图形设计、交互设计和用户测试这三分类。http://www.hebjxw.com/ShowInfo_News.asp?id=32136
17.基于Mongoose在STM32单片机上实现WebUI和MQTT客户端例如在设备上实现Web UI 界面、RESTful API服务、遥测数据交换、产品远程控制、远程软件更新、远程监控等,Mongoose已部署到全球生产环境中的数亿台设备中。所有非商业项目都可以在 GPLv2 许可下使用Mongoos开源代码。只需添加mongoose.c和mongoose.h文件,在现有应用中添加几行代码即可轻松集成Mongoose库。 https://www.eet-china.com/mp/a156698.html
18.开发人员必须知道的免费服务与资源设计和UI 域名解析 Docker相关 电子邮件 IaaS IDE和代码编辑 国际手机号码验证API和SDK 问题跟踪与项目管理 日志管理 管理系统 杂 监控方式 其他免费资源 PaaS 包构建系统 付款和账单整合 搜索 安全和PKI 源代码回购 存储和媒体处理 STUN,WebRTC,Web套接字服务器和其他路由器 https://news.51cto.com/art/201911/606582.htm