最新最热

Node.js 写 bash 脚本终极方案!

加载中...
前言最近在学习ah脚本语法,但是如果对ah语法不是熟手的话,感觉非常容易出错,比如说:显示未定义的变量hell中变量没有定义,仍然是可以使用的,但是它的结果可能不是你所预期的。举个例子:#!/i/ah#这里是判断变量var是否等于字符串ac,但是var这个变量并没有声明if[quot$varquot=quotacquot]the#如果if判断里是true就在控制台打印“otac”echoquototacquotele#如果if判断里是fale就在控制台打印“ac”echoquotacquotfi结果是打印了ac,但问题是,这个脚本应该报错啊,变量并没有赋值算是错误吧。为了弥补这些错误,我们学会在脚本开头加入:et-u这句命令的意思是脚本在头部加上它,遇到不存在的变量就会报错,并停止执行。再次运行就会提示:tet.h:3:tet.h:um:arameterotet再想象一下,你本来想删除:rm-rf$dir/*然后dir是空的时候,变成了什么?rm-rf是删除命令,$dir是空的话,相当于执行rm-rf/*,这是删除所有文件和文件夹。。。然后,你的系统就没了,这就是传说中的删库跑路吗~~~~如果是ode或者浏览器环境,我们直接var==='ac'肯定是会报错的,也就是说很多javacrit编程经验无法复用到ah来,如果能复用的话,该多好啊。后来就开始探索,如果用ode脚本代替ah该多好啊,经过一天折腾逐渐发现一个神器,Google旗下的zx库,先别着急,我先不介绍这个库,我们先看看目前主流用ode如何编写ah脚本,就知道为啥它是神器了。ode执行ah脚本:勉强解决方案:child_roceAPI例如child_roce的API里面exec命令cot{exec}=require(quotchild_rocequot)exec(quotl-laquot,(error,tdout,tderr)=gt{if(error){coole.log(`error:${error.meage}`)retur}if(tderr){coole.log(`tderr:${tderr}`)retur}coole.log(`tdout:${tdout}`)})这里需要注意的是,首先exec是异步的,但是我们ah脚本命令很多都是同步的。而且注意:error对象不同于tderr.error当child_roce模块无法执行命令时,该对象不为空。例如,查找一个文件找不到该文件,则error对象不为空。但是,如果命令成功运行并将消息写入标准错误流,则该tderr对象不会为空。当然我们可以使用同步的exec命令,execSyc//引入exec命令fromchild_roce模块cot{execSyc}=require(quotchild_rocequot)//同步创建了一个hello的文件夹execSyc(quotmkdirhelloquot)再简单介绍一下child_roce的其它能够执行ah命令的aiaw:启动一个子进程来执行命令exec:启动一个子进程来执行命令,与aw不同的是,它有一个回调函数能知道子进程的情况execFile:启动一子进程来执行可执行文件fork:与aw类似,不同点是它需要指定子进程需要需执行的javacrit文件exec跟ececFile不同的是,exec适合执行命令,eexecFile适合执行文件。ode执行ah脚本:进阶方案helljcothell=require('hellj')#删除文件命令hell.rm('-rf','out/Releae')//拷贝文件命令hell.c('-R','tuff/','out/Releae')#切换到li目录,并且列出目录下到.j结尾到文件,并替换文件内容(ed-i是替换文字命令)hell.cd('li')hell.l('*.j').forEach(fuctio(file){hell.ed('-i','BUILD_VERSION','v0.1.2',file)hell.ed('-i',/^.*REMOVE_THIS_LINE.*$/,'',file)hell.ed('-i',/.*REPLACE_LINE_WITH_MACRO.*\/,hell.cat('macro.j'),file)})hell.cd('..')#除非另有说明,否则同步执行给定的命令。在同步模式下,这将返回一个ShellStrig#(与ShellJSv0.6.x兼容,它返回一个形式为{code:...,tdout:...,tderr:...}的对象)。#否则,这将返回子进程对象,并且回调接收参数(代码、标准输出、标准错误)。if(hell.exec('gitcommit-amquotAuto-commitquot').code!==0){hell.echo('Error:Gitcommitfailed')hell.exit(1)}从上面代码上看来,hellj真的已经算是非常棒的odej写ah脚本的方案了,如果你们那边的ode环境不能随便升级,我觉得hellj确实够用了。接着我们看看今天的主角zx,tart已经17.4k了。zx库官方网址:www.mj.com/ackage/zx我们先看看怎么用#!/ur/i/evzxawait$`catackage.jo|greame`letrach=await$`gitrach--how-curret`await$`dedeloy--rach=${rach}`awaitPromie.all([$`lee1echo1`,$`lee2echo2`,$`lee3echo3`,])letame='fooar'await$`mkdir/tm/${ame}各位看官觉得咋样,是不是就是在写liux命令而已,ah语法可以忽略很多,直接上j就行,而且它的优点还不止这些,有一些特点挺有意思的:1、支持t,自动编译.t为.mj文件,.mj文件是ode高版本自带的支持e6module的文件结尾,也就是这个文件直接imort模块就行,不用其它工具转义2、自带支持管道操作ie方法3、自带fetch库,可以进行网络请求,自带chalk库,可以打印有颜色的字体,自带错误处理othrow方法,如果ah命令出错,可以包裹在这个方法里忽略错误完整中文文档(在下翻译水平一般,请见谅)#!/ur/i/evzxawait$`catackage.jo|greame`letrach=await$`gitrach--how-curret`await$`dedeloy--rach=${rach}`awaitPromie.all([$`lee1echo1`,$`lee2echo2`,$`lee3echo3`,])letame='fooar'await$`mkdir/tm/${ame}Bah很棒,但是在编写脚本时,人们通常会选择更方便的编程语言。JavaScrit是一个完美的选择,但标准的Node.j库在使用之前需要额外的做一些事情。zx基于child_roce,转义参数并提供合理的默认值。安装mi-gzx需要的环境Node.jgt=14.8.0将脚本写入扩展名为.mj的文件中,以便能够在顶层使用await。将以下heag添加到zx脚本的开头:#!/ur/i/evzx现在您将能够像这样运行您的脚本:chmod+x./crit.mj./crit.mj或者通过zx可执行文件:zx./crit.mj所有函数($、cd、fetch等)都可以直接使用,无需任何导入。$commad使用child_roce包中的aw函数执行给定的字符串,并返回ProcePromie.letcout=areIt(await$`l-1|wc-l`)coole.log(`Filecout:${cout}`)例如,要并行上传文件:如果执行的程序返回非零退出代码,ProceOutut将被抛出try{await$`exit1`}catch(){coole.log(`Exitcode:${.exitCode}`)coole.log(`Error:${.tderr}`)}ProcePromie,以下是romietyecrit的接口定义claProcePromieltTgtextedPromieltTgt{readolytdi:Writalereadolytdout:Readalereadolytderr:ReadalereadolyexitCode:Promieltumergtie(det):ProcePromieltTgt}阅读更多的关于管道的信息githu.com/google/zx/…ProceOutut的tyecrit接口定义claProceOutut{readolytdout:trigreadolytderr:trigreadolyexitCode:umertoStrig():trig}函数:cd()更改当前工作目录cd('/tm')await$`wd`//outut/tmcd('/tm')await$`wd`//outut/tmfetch()ode-fetch包。letre=awaitfetch('htt://wttr.i')if(re.ok){coole.log(awaitre.text())}quetio()readlie包letear=awaitquetio('Whatkidofeariet?')lettoke=awaitquetio('Chooeevvariale:',{choice:Oject.key(roce.ev)})在第二个参数中,可以指定选项卡自动完成的选项数组以下是接口定义fuctioquetio(query?:trig,otio?:QuetioOtio):PromielttriggttyeQuetioOtio={choice:trig[]}lee()基于etTimeout函数awaitlee(1000)othrow()将$的行为更改,如果退出码不是0,不跑出异常.t接口定义fuctioothrowltPgt(:P):Pawaitothrow($`greomethigfrom-file`)//在管道内:await$`fid./examle-tyef-rit0`.ie(othrow($`xarg-0greomethig`)).ie($`wc-l`)以下的包,无需导入,直接使用chalkawaitothrow($`greomethigfrom-file`)//在管道内:await$`fid./examle-tyef-rit0`.ie(othrow($`xarg-0greomethig`)).ie($`wc-l`)f类似于如下的使用方式imort{romieaf}from'f'letcotet=awaitf.readFile('./ackage.jo')oawait$`cd${o.homedir()}amammkdirexamle`配置:$.hell指定要用的ah.$.hell='/ur/i/ah'$.quote指定用于在命令替换期间转义特殊字符的函数默认用的是hq包.注意:__fileameamam__dirame这两个变量是在commoj中的。我们用的是.mj结尾的e6模块。在ESM模块中,Node.j不提供__fileame和__dirame全局变量。由于此类全局变量在脚本中非常方便,因此zx提供了这些以在.mj文件中使用(当使用zx可执行文件时)require也是commoj中的导入模块方法,在ESM模块中,没有定义require()函数。zx提供了require()函数,因此它可以与.mj文件中的导入一起使用(当使用zx可执行文件时)传递环境变量roce.ev.FOO='ar'await$`echo$FOO`传递数组如果值数组作为参数传递给$,数组的项目将被单独转义并通过空格连接Examle:letfile=[1,2,3]await$`tarcz${file}`可以通过显式导入来使用$和其他函数#!/ur/i/evodeimort{$}from'zx'await$`date`zx可以将.t脚本编译为.mj并执行它们zxexamle/tyecrit.t

2021年上半年值得关注的 web 前端开源 UI 组件库

加载中...
盘点2021年上半年推荐过的weui组件库,都很优秀,很值得去尝试。1:NutUI3.0官网:htt://utui.jd.com/3x/京东出品的轻量免费开源Vue组件库迎来了全新版本更新,NutUI是京东样式风格的Vue移动端组件库,开发和服务于移动We界面的企业级产品。京东出品的Vue移动端UI组件库NutUI最近迎来了大版本更新,还特意做了全新的官网,主色调从原来的蓝色变成了红色,这也意味着,不仅升级到了如今火热的Vue3.x,整套UI视觉也进行了全新设计,看着更接近京东A。2:Quaar官网:quaarch.com/性能顶级的多平台weUI组件开源框架,性能顶级,支持一套代码同时发布多个平台的国外优秀开发框架。Quaar是一个基于Vue.j开发的weui框架,性能顶级,能够用于快速开发we桌面产品或A项目,编写一次代码,同时发布为网站、移动应用或Electro桌面应用。3:ElemetPlu官网:htt://elemet-lu.gitee.io/饿了么团队基于Vue3.0更新发布的优秀开源桌面UI组件库,这是ElemetUI的升级版本,体验同样优秀,ai更加友好,文档同样详尽,很香。4.MueUI官网:htt://mue-ui.org/优雅的MaterialDeig风格前端开源UI组件库,一款基于Vue2.0的UI组件库,按MaterialDeig设计规范实现,体验优秀,使用简单。MueUI是一款基于Vue.j的优雅UI组件库,所有内置组件基本实现MaterialDeig的设计规范,另外还开发了许多新的功能特性,是一款受欢迎的优质VueUI组件库,不要错过。5.c.gg官网:htt://c.gg/一套漂亮的纯CSS实现的免费开源图标库,700多个时尚漂亮的c图标库,特别适合UI设计师和前端开发者下载使用。c.gg是一套纯c实现的轻量美观的开源图标库,特点是所有的图形都通过c语法实现,是技术和设计的完美结合。6.uViewUI官网:htt://www.uviewui.com/支持APP/H5/各小程序平台多端发布的通用UI框架,知名多端开发框架ui-a生态里优秀的UI框架,一次编写,多端发布。uViewUI是一个用于ui-a多端开发的优质UI组件库,由第三方爱好者的团队编写。介绍uViewUI之前,先简单介绍一下ui-a。7.NaiveUI官网:htt://www.aiveui.com/zh-CN/o-theme一个Vue3组件库,组件完整,主题可调,使用TyeScri编写尤大推荐的Vue组件库

git使用的基本流程

加载中...
Git是一个版本控制系统,是任何软件开发项目中的主要内容。通常有两个主要用途:代码备份和代码版本控制。你可以逐步处理代码,在需要回滚到备份副本的过程中保存每一步的进度!常见的问题是Git很难使用。有时版本和分支不同步,你会花很长时间试图推送代码!更糟糕的是,不知道某些命令的确切工作方式很容易导致意外删除或覆盖部分代码!这就是我写本文的原因,从而学习到如何正确使用Git,以便在开发中共同进行编码!安装和配置Git安装首先,我们必须安装Git才能使用它!这里分Liux和Widow来演示:在Liux上安装Git我们可以使用yum轻松快速地做到这一点:udoyumitallgit在Widow上安装Git直接在htt://git-cm.com/dowload里面,下载最新版的Git,默认安装就可以了。安装完成后,在开始菜单里找到Git-gtGitBah,点击后出现一个类似命令行窗口的东西,就说明Git安装成功。Git配置可以保存Git用户名和电子邮件,这样就不必在以后的Git命令中再次输入它们。在命令行中配置本地仓库的账号和邮箱:$gitcofig--gloaluer.amequottetquot$gitcofig--gloaluer.emailquottet@qq.comquot好多人都不知道的小技巧是,你可以为Git启用一些额外的颜色,这样就可以更容易地阅读命令的输出!gitcofig--gloalcolor.uitrueGit基本版本控制初始化Git现在,我们可以开始对项目进行版本控制。使用cd命令导航到要在终端中设置版本控制的目录,现在你可以像这样初始化Git存储库:gitiit这将创建一个名为.git的新子目录(Widow下该目录为隐藏的),其中包含所有必需的存储库文件(Git存储库框架)。至此,你的项目中尚未跟踪任何内容。添加并提交要开始对现有文件进行版本控制,你应该先跟踪这些文件并进行初始提交。要做到这一点,你首先需要将文件添加到Git中,并将它们附加到Git项目中。gitaddltfilegtgitcommit-m'firtcommit'远程备份很棒!你现在已经开始在本地对项目进行版本控制。如果你想远程保存和备份项目,则需要在GitHu上创建一个远程存储库(它是免费的!)。因此,首先转到htt://githu.com并创建一个存储库。然后,使用存储库的链接将其添加为本地git项目的来源,即该代码的存储位置。#示例gitremoteaddorigihtt://githu.com/wueixua/reo.git#以我的一个仓库为例gitremoteaddorigihtt://githu.com/wueixua/JDKSourceCode1.8.git然后,你可以继续将代码推送到GitHu!哇,你已经成功备份了你的代码!gituhorigimater状态检查gittatu命令用于确定哪些文件处于哪种状态,它使你可以查看哪些文件已提交,哪些文件尚未提交。如果在所有文件都已提交并推送后运行此命令,则应该看到类似以下内容:$gittatu#Orachmaterothigtocommit(workigdirectoryclea)如果你将新文件添加到项目中,而该文件之前不存在,则在运行gittatu时,你应该看到未跟踪的文件,如下所示:$gittatu#Orachmater#Utrackedfile:#(uequotgitaddltfilegt...quottoicludeiwhatwillecommitted)##READMEothigaddedtocommitututrackedfilereet(uequotgitaddquottotrack)使用gittatu对于快速检查你已经备份的内容和你仅在本地拥有的内容非常有用。高级文件添加还有一些更高级的方法可以将文件添加到Git中,从而使你的工作流程更高效。我们可以执行以下操作,而不是试图查找所有有更改的文件并逐个添加它们:#逐个添加文件gitaddfileame#添加当前目录中的所有文件gitadd-A#添加当前目录中的所有文件更改gitadd.#选择要添加的更改(你可以Y或N完成所有更改)gitadd-高级提交我们可以使用gitcommit-m'提交信息'来将文件提交到Git。对于提交简短消息来说,这一切都很好,但是如果你想做一些更精细的事情,你需要来学习更多的操作:###提交暂存文件,通常用于较短的提交消息gitcommit-m'commitmeage'###添加文件并提交一次gitcommitfileame-m'commitmeage'###添加文件并提交暂存文件gitcommit-am'iertcommitmeage'###更改你的最新提交消息gitcommit--amed'ewcommitmeage'#将一系列提交合并为一个提交,你可能会用它来组织混乱的提交历史记录gitreae-i###这将为你提供核心编辑器上的界面:#Commad:#,ick=uecommit#r,reword=uecommit,uteditthecommitmeage#e,edit=uecommit,uttoforamedig#,quah=uecommit,utmelditorevioucommit#f,fixu=likequotquahquot,utdicardthicommit'logmeage#x,exec=rucommad(theretofthelie)uighell分支与合并GitHu存储库的mater分支应始终包含有效且稳定的代码。但是,你可能还希望备份一些当前正在处理的代码,但这些代码并不完全稳定。也许你要添加一个新功能,你正在尝试和破坏很多代码,但是你仍然希望保留备份以保存进度!分支使你可以在不影响mater分支的情况下处理代码的单独副本。首次创建分支时,将以新名称创建mater分支的完整克隆。然后,你可以独立地在此新分支中修改代码,包括提交文件等。一旦你的新功能已完全集成并且代码稳定,就可以将其合并到mater分支中!分支这是你在分支上创建和工作所需的所有东西:###创建一个本地分支gitcheckout-rachame###在2个分支之间切换gitcheckoutrc/dev-wuxgitcheckoutmater###将新的本地分支作为备份gituh-uorigirach_2###删除本地分支,这不会让你删除尚未合并的分支gitrach-drach_2###删除本地分支,即使尚未合并,这也会删除该分支!gitrach-Drach_2###Viewigallcurretracheforthereoitory,icludigoth###localadremoterache.Greattoeeifyoualreadyhavea###rachforaarticularfeatureadditio,eeciallyoigger###roject###查看存储库的所有当前分支,包括本地和远程分支。gitrach-a###查看已合并到您当前分支中的所有分支,包括本地和远程。非常适合查看所有代码的来源!gitrach-a--merged###查看尚未合并到当前分支中的所有分支,包括本地和远程gitrach-a--o-merged###查看所有本地分支gitrach###查看所有远程分支gitrach-r#将主分支重新设置为本地分支$gitreaeorigi/mater#将分支推送到远程存储库源并对其进行跟踪$gituhorigirachame合并很棒!现在,你已经学习了如何创建分支并开始敲代码!将新功能添加到分支中之后,你需要将其合并回mater分支,以便您的mater具有所有最新的代码功能。方法如下:###首先确保你正在查看mater分支gitcheckoutmater###现在将你的分支合并到matergitmergedev你可能必须修复分支与主服务器之间的任何代码冲突,但是Git将向你展示在键入该merge命令后如何执行所有这些操作。修复错误和回溯发生错误......它们经常在编码中发生!重要的是我们能够修复它们。不要慌!Git提供了你所需的一切,以防你在所推送的代码中犯错,改写某些内容或者只是想对所推送的内容进行更正。###切换到最新提交的代码版本gitreetHEADgitreetHEAD--fileame#foraecificfile###切换到最新提交之前的代码版本gitreetHEAD^--fileamegitreetHEAD^--fileame#foraecificfile###切换回3或5次提交gitreetHEAD~3--fileamegitreetHEAD~3--fileame#foraecificfilegitreetHEAD~5--fileamegitreetHEAD~5--fileame#foraecificfile###切换回特定的提交,其中0766c053为提交IDgitreet0766c053--fileamegitreet0766c053--fileame#foraecificfile###先前的命令是所谓的软重置。你的代码已重置,但是git仍会保留其他代码的副本,以备你需要时使用。另一方面,--hard标志告诉Git覆盖工作目录中的所有更改。gitreet--hard0766c053对Git有用的提示和技巧我们已经完成了所有细节部分!以下是一些Git提示和技巧,你可能会发现它们对改善工作流程非常有用!###搜索###搜索目录中的字符串部分gitgre'roject'###在目录中搜索部分字符串,-打印出git找到匹配项的行号gitgre-'roject'###gitgre-Clt行数gt'omethig'搜索带有某些上下文的字符串部分(某些行在我们正在寻找的字符串之前和之后)gitgre-Cltumerofliegt'roject'###搜索字符串的一部分,并在字符串之前显示行gitgre-Bltumerofliegt'roject'###搜索字符串的一部分,并在字符串之后显示行gitgre-Altumerofliegt'omethig'看谁写了什么###显示带有作者姓名的文件的更改历史记录gitlame'fileame'###显示带有作者姓名和gitcommitID的文件的更改历史记录gitlame'fileame'-l日志###显示存储库中所有提交的列表该命令显示有关提交的所有信息,例如提交ID,作者,日期和提交消息gitlog###提交列表仅显示提交消息和更改gitlog-###包含您要查找的特定字符串的提交列表gitlog-S'roject'###作者提交的清单gitlog--author'wux'###显示存储库中提交列表的摘要。显示提交ID和提交消息的较短版本。gitlog--oelie###显示昨天以来仓库中的提交列表gitlog--ice=yeterday###显示作者日志,并在提交消息中搜索特定术语gitlog--grequotrojectquot--authorquotuerquot

让你30分钟快速掌握vue 3

加载中...
经过了漫长的迭代,Vue3.0终于在上2020-09-18发布了,带了翻天覆地的变化,使用了Tyecrit进行了大规模的重构,带来了ComoitioAPIRFC版本,类似ReactHook一样的写Vue,可以自定义自己的hook,让使用者更加的灵活,接下来总结一下vue3.0带来的部分新特性。etu()ref()reactive()iRef()toRef()comuted()watch()LifeCycleHook(新的生命周期)TemlaterefgloalProertieueeVue2与Vue3的对比对TyeScrit支持不友好(所有属性都放在了thi对象上,难以推倒组件的数据类型)大量的API挂载在Vue对象的原型上,难以实现TreeShakig。架构层面对跨平台dom渲染开发支持不友好ComoitioAPI。受ReactHook启发更方便的支持了jxVue3的Temlate支持多个根标签,Vue2不支持对虚拟DOM进行了重写、对模板的编译进行了优化操作...一、etu函数etu()函数是vue3中,专门为组件提供的新属性。它为我们使用vue3的ComoitioAPI新特性提供了统一的入口,etu函数会在eforeCreate之后、created之前执行,vue3也是取消了这两个钩子,统一用etu代替,该函数相当于一个生命周期函数,vue中过去的data,method,watch等全部都用对应的新增ai写在etu()函数中etu(ro,cotext){cotext.attrcotext.lotcotext.aretcotext.rootcotext.emitcotext.refretur{}}ro:用来接收ro数据cotext用来定义上下文,上下文对象中包含了一些有用的属性,这些属性在vue2.x中需要通过thi才能访问到,在etu()函数中无法访问到thi,是个udefied返回值:retur{},返回响应式数据,模版中需要使用的函数二、reactive函数reactive()函数接收一个普通对象,返回一个响应式的数据对象,想要使用创建的响应式数据也很简单,创建出来之后,在etu中retur出去,直接在temlate中调用即可lttemlategt{{ame}}//tetlttemlategtltcritlag=quottquotgtimort{defieComoet,reactive,ref,toRef}from'vue'exortdefaultdefieComoet({etu(ro,cotext){lettate=reactive({ame:'tet'})returtate}})lt/critgt三、ref()函数ref()函数用来根据给定的值创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象上只包含一个value属性,只在etu函数内部访问ref函数需要加.valuelttemlategtltdivcla=quotmiequotgt{{cout}}//10lt/divgtlt/temlategtltcritlag=quottquotgtimort{defieComoet,ref}from'vue'exortdefaultdefieComoet({etu(){cotcout=refltumergt(10)//在j中获取ref中定义的值,需要通过value属性coole.log(cout.value)retur{cout}}})lt/critgt在reactive对象中访问ref创建的响应式数据lttemlategtltdivcla=quotmiequotgt{{cout}}-{{t}}//10-100lt/divgtlt/temlategtltcritlag=quottquotgtimort{defieComoet,reactive,ref,toRef}from'vue'exortdefaultdefieComoet({etu(){cotcout=refltumergt(10)cotoj=reactive({t:100,cout})//通过reactive来获取ref的值时,不需要使用.value属性coole.log(oj.cout)retur{...toRef(oj)}}})lt/critgt四、iRef()函数iRef()用来判断某个值是否为ref()创建出来的对象ltcritlag=quottquotgtimort{defieComoet,iRef,ref}from'vue'exortdefaultdefieComoet({etu(ro,cotext){cotame:trig='vue'cotage=refltumergt(18)coole.log(iRef(age))//truecoole.log(iRef(ame))//faleretur{age,ame}}})lt/critgt五、toRef()函数toRef()函数可以将reactive()创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是ref()类型的响应式数据lttemlategtltdivcla=quotmiequotgt{{ame}}//tet{{age}}//18lt/divgtlt/temlategtltcritlag=quottquotgtimort{defieComoet,reactive,ref,toRef}from'vue'exortdefaultdefieComoet({etu(ro,cotext){lettate=reactive({ame:'tet'})cotage=ref(18)retur{...toRef(tate),age}}})lt/critgt六、comuted()该函数用来创造计算属性,和过去一样,它返回的值是一个ref对象。里面可以传方法,或者一个对象,对象中包含et()、get()方法6.1创建只读的计算属性imort{comuted,defieComoet,ref}from'vue'exortdefaultdefieComoet({etu(ro,cotext){cotage=ref(18)//根据age的值,创建一个响应式的计算属性readOlyAge,它会根据依赖的ref自动计算并返回一个新的refcotreadOlyAge=comuted(()=gtage.value++)//19retur{age,readOlyAge}}})lt/critgt6.2通过et()、get()方法创建一个可读可写的计算属性ltcritlag=quottquotgtimort{comuted,defieComoet,ref}from'vue'exortdefaultdefieComoet({etu(ro,cotext){cotage=refltumergt(18)cotcomutedAge=comuted({get:()=gtage.value+1,et:value=gtage.value+value})//为计算属性赋值的操作,会触发et函数,触发et函数后,age的值会被更新age.value=100retur{age,comutedAge}}})lt/critgt七、watch()函数watch函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源数据变更时才执行回调。7.1监听用reactive声明的数据源ltcritlag=quottquotgtimort{comuted,defieComoet,reactive,toRef,watch}from'vue'iterfacePero{ame:trig,age:umer}exortdefaultdefieComoet({etu(ro,cotext){cottate=reactiveltPerogt({ame:'vue',age:10})watch(()=gttate.age,(age,reAge)=gt{coole.log(age)//100coole.log(reAge)//10})//修改age时会触发watch的回调,打印变更前后的值tate.age=100retur{...toRef(tate)}}})lt/critgt7.2监听用ref声明的数据源ltcritlag=quottquotgtimort{defieComoet,ref,watch}from'vue'iterfacePero{ame:trig,age:umer}exortdefaultdefieComoet({etu(ro,cotext){cotage=refltumergt(10)watch(age,()=gtcoole.log(age.value))//100//修改age时会触发watch的回调,打印变更后的值age.value=100retur{age}}})lt/critgt7.3同时监听多个值ltcritlag=quottquotgtimort{comuted,defieComoet,reactive,toRef,watch}from'vue'iterfacePero{ame:trig,age:umer}exortdefaultdefieComoet({etu(ro,cotext){cottate=reactiveltPerogt({ame:'vue',age:10})watch([()=gttate.age,()=gttate.ame],([ewName,ewAge],[oldName,oldAge])=gt{coole.log(ewName)coole.log(ewAge)coole.log(oldName)coole.log(oldAge)})//修改age时会触发watch的回调,打印变更前后的值,此时需要注意,更改其中一个值,都会执行watch的回调tate.age=100tate.ame='vue3'retur{...toRef(tate)}}})lt/critgt7.4to停止监听在etu()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用watch()函数的返回值即可,语法如下:ltcritlag=quottquotgtimort{et}from'lodah'imort{comuted,defieComoet,reactive,toRef,watch}from'vue'iterfacePero{ame:trig,age:umer}exortdefaultdefieComoet({etu(ro,cotext){cottate=reactiveltPerogt({ame:'vue',age:10})cotto=watch([()=gttate.age,()=gttate.ame],([ewName,ewAge],[oldName,oldAge])=gt{coole.log(ewName)coole.log(ewAge)coole.log(oldName)coole.log(oldAge)})//修改age时会触发watch的回调,打印变更前后的值,此时需要注意,更改其中一个值,都会执行watch的回调tate.age=100tate.ame='vue3'etTimeout(()=gt{to()//此时修改时,不会触发watch回调tate.age=1000tate.ame='vue3-'},1000)//1秒之后讲取消watch的监听retur{...toRef(tate)}}})lt/critgt八、LifeCycleHook(新的生命后期)新版的生命周期函数,可以按需导入到组件中,且只能在etu()函数中使用,但是也可以在etu外定义,在etu中使用ltcritlag=quottquotgtimort{et}from'lodah'imort{defieComoet,oBeforeMout,oBeforeUmout,oBeforeUdate,oErrorCatured,oMouted,oUmouted,oUdated}from'vue'exortdefaultdefieComoet({etu(ro,cotext){oBeforeMout(()=gt{coole.log('eformouted!')})oMouted(()=gt{coole.log('mouted!')})oBeforeUdate(()=gt{coole.log('eforudated!')})oUdated(()=gt{coole.log('udated!')})oBeforeUmout(()=gt{coole.log('eforumouted!')})oUmouted(()=gt{coole.log('umouted!')})oErrorCatured(()=gt{coole.log('errorCatured!')})retur{}}})lt/critgt九、Temlateref通过ref来回去真实dom元素,这个和react的用法一样,为了获得对模板内元素或组件实例的引用,我们可以像往常一样在etu()中声明一个ref并返回它还是跟往常一样,在html中写入ref的名称在teu中定义一个refteu中返回ref的实例oMouted中可以得到ref的RefIml的对象,通过.value获取真实domltdivcla=quothighlightquotgtlttemlategtlt!--第一步:还是跟往常一样,在html中写入ref的名称--gtltdivcla=quotmiequotref=quotelmRefquotgtltagt1111lt/agtlt/divgtlt/temlategtltcritlag=quottquotgtimort{et}from'lodah'imort{defieComoet,oMouted,ref}from'vue'exortdefaultdefieComoet({etu(ro,cotext){//获取真实domcotelmRef=refltull|HTMLElemetgt(ull)oMouted(()=gt{coole.log(elmRef.value)//得到一个RefIml的对象,通过.value访问到数据})retur{elmRef}}})lt/critgt十、vue的全局配置通过vue实例上cofig来配置,包含Vue应用程序全局配置的对象。您可以在挂载应用程序之前修改下面列出的属性:cota=Vue.createA({})a.cofig={...}为组件渲染功能和观察程序期间的未捕获错误分配处理程序。错误和应用程序实例将调用处理程序a.cofig.errorHadler=(err,vm,ifo)=gt{}可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。这可以代替Vue2.xVue.rototye扩展:cota=Vue.createA({})a.cofig.gloalProertie.$htt='xxxxxxxx'可以在组件用通过getCurretItace()来获取全局gloalProertie中配置的信息,getCurretItace方法获取当前组件的实例,然后通过ctx属性获得当前上下文,这样我们就能在etu中使用router和vuex,通过这个属性我们就可以操作变量、全局属性、组件属性等等etu(){cot{ctx}=getCurretItace()ctx.$htt}十一、Suee组件在开始介绍Vue的Suee组件之前,我们有必要先了解一下React的Suee组件,因为他们的功能类似。React.lazy接受一个函数,这个函数需要动态调用imort()。它必须返回一个Promie,该Promie需要reolve一个defaultexort的React组件。imortReact,{Suee}from'react'cotmyComoet=React.lazy(()=gtimort('./Comoet'))fuctioMyComoet(){retur(ltdivgtltSueefallack={ltdivgtLoadig...lt/divgt}gtltmyComoet/gtlt/Sueegtlt/divgt)}Vue3也新增了React.lazy类似功能的defieAycComoet函数,处理动态引入(的组件)。defieAycComoet可以接受返回承诺的工厂函数。当您从服务器检索到组件定义时,应该调用Promie的解析回调。您还可以调用reject(reao)来指示负载已经失败imort{defieAycComoet}from'vue'cotAycCom=defieAycComoet(()=gtimort('./comoet/AycComoet.vue'))a.comoet('ayc-comoet',AycCom)Vue3也新增了Suee组件:lttemlategtltSueegtlttemlate#defaultgtltmy-comoet/gtlt/temlategtlttemlate#fallackgtLoadig...lt/temlategtlt/Sueegtlt/temlategtltcritlag='t'gtimort{defieComoet,defieAycComoet}fromquotvuequotcotMyComoet=defieAycComoet(()=gtimort('./Comoet'))exortdefaultdefieComoet({comoet:{MyComoet},etu(){retur{}}})十二、vue3.x完整组件模版结构一个完成的vue3.x完整组件模版结构包含了:组件名称、ro、comoet、etu(hook、comuted、watch、method等)作者:Haru前端链接:htt://zhuala.zhihu.com//268705817来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。lttemlategtltdivcla=quotmiequotref=quotelmRefquotgtltagt{{ame}}lt/agtltrgtltagt{{cout}}lt/agtltdivgtltutto@click=quothadleClickquotgt测试按钮lt/uttogtlt/divgtltulgtltliv-for=quotitemilitquot:key=quotitem.idquotgt{{item.ame}}lt/ligtlt/ulgtlt/divgtlt/temlategtltcritlag=quottquotgtimort{comuted,defieComoet,getCurretItace,oMouted,ProTye,reactive,ref,toRef}from'vue'iterfaceIState{cout:0,ame:trig,lit:Arrayltojectgt}exortdefaultdefieComoet({ame:'demo',//父组件传子组件参数ro:{ame:{tye:StrigaProTyeltull|''gt,default:'vue3.x'},lit:{tye:ArrayaProTyeltoject[]gt,default:()=gt[]}},comoet:{///TODO组件注册},emit:[quotemit-amequot],//为了提示作用etu(ro,cotext){coole.log(ro.ame)coole.log(ro.lit)cottate=reactiveltIStategt({ame:'vue3.0组件',cout:0,lit:[{ame:'vue',id:1},{ame:'vuex',id:2}]})cota=comuted(()=gttate.ame)oMouted(()=gt{})fuctiohadleClick(){tate.cout++//调用父组件的方法cotext.emit('emit-ame',tate.cout)}retur{...toRef(tate),hadleClick}}})lt/critgtvue3的生态官网源码vite构建器脚手架:vue-router-extvuex4.0UI组件库vat2.xAtDeigofVue2.xelemet-lu

20个 Javascript 技巧,提高我们的摸鱼时间!

使用方便有用的方法,以减少代码行数,提高我们的工作效率,增加我们的摸鱼时间。在我们的日常任务中,我们需要编写函数,如排序、搜索、寻找惟一值、传递参数、交换值等,所以在这里分享一下我工作多年珍藏的几个常用技巧和方法,以让大家增加摸鱼的时间。这些方法肯定会帮助你:减少代码行CodigCometitio增加摸鱼的时间1.声明和初始化数组我们可以使用特定的大小来初始化数组,也可以通过指定值来初始化数组内容,大家可能用的是一组数组,其实二维数组也可以这样做,如下所示:cotarray=Array(5).fill('')//输出(5)[quotquot,quotquot,quotquot,quotquot,quotquot]cotmatrix=Array(5).fill(0).ma(()=gtArray(5).fill(0))//输出(5)[Array(5),Array(5),Array(5),Array(5),Array(5)]0:(5)[0,0,0,0,0]1:(5)[0,0,0,0,0]2:(5)[0,0,0,0,0]3:(5)[0,0,0,0,0]4:(5)[0,0,0,0,0]legth:52.求和,最小值和最大值我们应该利用reduce方法快速找到基本的数学运算。cotarray=[5,4,7,8,9,2]求和array.reduce((a,)=gta+)//输出:35最大值array.reduce((a,)=gtagt?a:)//输出:9最小值array.reduce((a,)=gtalt?a:)//输出:23.排序字符串,数字或对象等数组我们有内置的方法ort()和revere()来排序字符串,但是如果是数字或对象数组呢字符串数组排序cottrigArr=[quotJoequot,quotKailquot,quotStevequot,quotMukquot]trigArr.ort()//输出(4)[quotJoequot,quotKailquot,quotMukquot,quotStevequot]trigArr.revere()//输出(4)[quotStevequot,quotMukquot,quotKailquot,quotJoequot]数字数组排序cotarray=[40,100,1,5,25,10]array.ort((a,)=gta-)//输出(6)[1,5,10,25,40,100]array.ort((a,)=gt-a)//输出(6)[100,40,25,10,5,1]对象数组排序cotojectArr=[{firt_ame:'Lazlo',lat_ame:'Jamf'},{firt_ame:'Pig',lat_ame:'Bodie'},{firt_ame:'Pirate',lat_ame:'Pretice'}]ojectArr.ort((a,)=gta.lat_ame.localeComare(.lat_ame))//输出(3)[{…},{…},{…}]0:{firt_ame:quotPigquot,lat_ame:quotBodiequot}1:{firt_ame:quotLazloquot,lat_ame:quotJamfquot}2:{firt_ame:quotPiratequot,lat_ame:quotPreticequot}legth:34.从数组中过滤到虚值像0,udefied,ull,fale,quotquot,''这样的假值可以通过下面的技巧轻易地过滤掉。cotarray=[3,0,6,7,'',fale]array.filter(Boolea)//输出(3)[3,6,7]使用逻辑运算符处理需要条件判断的情况fuctiodoSomethig(arg1){arg1=arg1||10//如果arg1没有值,则取默认值10}letfoo=10foo===10amamdoSomethig()//如果foo等于10,刚执行doSomethig()//输出:10foo===5||doSomethig()//itheamethigaif(foo!=5)thedoSomethig()//Outut:10去除重复值cotarray=[5,4,7,8,9,2,7,5]array.filter((item,idx,arr)=gtarr.idexOf(item)===idx)//orcotoUique=[...ewSet(array)]//Outut:[5,4,7,8,9,2]创建一个计数器对象或Ma大多数情况下,可以通过创建一个对象或者Ma来计数某些特殊词出现的频率。lettrig='kailaliak'cottale={}for(letcharoftrig){tale[char]=tale[char]+1||1}//输出{k:2,a:3,:2,i:2,l:2}或者cotcoutMa=ewMa()for(leti=0ilttrig.legthi++){if(coutMa.ha(trig[i])){coutMa.et(trig[i],coutMa.get(trig[i])+1)}ele{coutMa.et(trig[i],1)}}//输出Ma(5){quotkquot=gt2,quotaquot=gt3,quotquot=gt2,quotiquot=gt2,quotlquot=gt2}三元运算符很酷fuctioFever(tem){returtemgt97?'ViitDoctor!':temlt97?'GoOutadPlay!!':tem===97?'TakeSomeRet!'}//输出Fever(97):quotTakeSomeRet!quotFever(100):quotViitDoctor!quot循环方法的比较for和for..i默认获取索引,但你可以使用arr[idex]。for..i也接受非数字,所以要避免使用。forEach,for...of直接得到元素。forEach也可以得到索引,但for...of不行。合并两个对象cotuer={ame:'KailRaghuwahi',geder:'Male'}cotcollege={rimary:'MaiPrimarySchool',ecodary:'LaSecodarySchool'}cotkill={rogrammig:'Extreme',wimmig:'Average',leeig:'Pro'}cotummary={...uer,...college,...kill}//合并多个对象geder:quotMalequotame:quotKailRaghuwahiquotrimary:quotMaiPrimarySchoolquotrogrammig:quotExtremequotecodary:quotLaSecodarySchoolquotleeig:quotProquotwimmig:quotAveragequot箭头函数箭头函数表达式是传统函数表达式的一种替代方式,但受到限制,不能在所有情况下使用。因为它们有词法作用域(父作用域),并且没有自己的thi和argumet,因此它们引用定义它们的环境。cotero={ame:'Kail',ayName(){returthi.ame}}ero.ayName()//输出quotKailquot但是这样:cotero={ame:'Kail',ayName:()=gt{returthi.ame}}ero.ayName()//Oututquot可选的链cotuer={emloyee:{ame:quotKailquot}}uer.emloyee?.ame//Outut:quotKailquotuer.emloy?.ame//Outut:udefieduer.emloy.ame//输出:VM21616:1UcaughtTyeError:Caotreadroerty'ame'ofudefied14.洗牌一个数组利用内置的Math.radom()方法。cotlit=[1,2,3,4,5,6,7,8,9]lit.ort(()=gt{returMath.radom()-0.5})//输出(9)[2,5,1,6,9,8,4,3,7]//输出(9)[4,1,7,5,3,8,2,9,6]14.双问号语法cotfoo=ull??'mychool'//输出:quotmychoolquotcotaz=0??42//输出:015.剩余和展开语法fuctiomyFu(a,,...mayMoreArg){returargumet.legth}myFu(quotoequot,quottwoquot,quotthreequot,quotfourquot,quotfivequot,quotixquot)//输出:6和cotart=['houlder','kee']cotlyric=['head',...art,'ad','toe']lyric//输出:(5)[quotheadquot,quothoulderquot,quotkeequot,quotadquot,quottoequot]16.默认参数cotearch=(arr,low=0,high=arr.legth-1)=gt{returhigh}earch([1,2,3,4,5])//输出:4将十进制转换为二进制或十六进制cotum=10um.toStrig(2)//输出:quot1010quotum.toStrig(16)//输出:quotaquotum.toStrig(8)//输出:quot12quot使用解构来交换两个数leta=5let=8[a,]=[,a][a,]//输出(2)[8,5]单行的回文数检查fuctiocheckPalidrome(tr){returtr==tr.lit('').revere().joi('')}checkPalidrome('ama')//输出:true20.将Oject属性转换为属性数组cotoj={a:1,:2,c:3}Oject.etrie(oj)//Outut(3)[Array(2),Array(2),Array(2)]0:(2)[quotaquot,1]1:(2)[quotquot,2]2:(2)[quotcquot,3]legth:3Oject.key(oj)(3)[quotaquot,quotquot,quotcquot]Oject.value(oj)(3)[1,2,3]

JavaScript 复杂判断的更优雅写法

前提我们编写j代码时经常遇到复杂逻辑判断的情况,通常大家可以用if/ele或者witch来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增加,代码中的if/ele/witch会变得越来越臃肿,越来越看不懂,那么如何更优雅的写判断逻辑,本文带你试一下。举个例子先看一段代码/***按钮点击事件*@aram{umer}tatu活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消*/cotoButtoClick=(tatu)=gt{if(tatu==1){edLog('roceig')jumTo('IdexPage')}eleif(tatu==2){edLog('fail')jumTo('FailPage')}eleif(tatu==3){edLog('fail')jumTo('FailPage')}eleif(tatu==4){edLog('ucce')jumTo('SuccePage')}eleif(tatu==5){edLog('cacel')jumTo('CacelPage')}ele{edLog('other')jumTo('Idex')}}复制代码通过代码可以看到这个按钮的点击逻辑:根据不同活动状态做两件事情,发送日志埋点和跳转到对应页面,大家可以很轻易的提出这段代码的改写方案,witch出场:/***按钮点击事件*@aram{umer}tatu活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消*/cotoButtoClick=(tatu)=gt{witch(tatu){cae1:edLog('roceig')jumTo('IdexPage')reakcae2:cae3:edLog('fail')jumTo('FailPage')reakcae4:edLog('ucce')jumTo('SuccePage')reakcae5:edLog('cacel')jumTo('CacelPage')reakdefault:edLog('other')jumTo('Idex')reak}}复制代码嗯,这样看起来比if/ele清晰多了,细心的同学也发现了小技巧,cae2和cae3逻辑一样的时候,可以省去执行语句和reak,则cae2的情况自动执行cae3的逻辑。这时有同学会说,还有更简单的写法:cotactio={'1':['roceig','IdexPage'],'2':['fail','FailPage'],'3':['fail','FailPage'],'4':['ucce','SuccePage'],'5':['cacel','CacelPage'],'default':['other','Idex'],}/***按钮点击事件*@aram{umer}tatu活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消*/cotoButtoClick=(tatu)=gt{letactio=actio[tatu]||actio['default'],logName=actio[0],ageName=actio[1]edLog(logName)jumTo(ageName)}复制代码上面代码确实看起来更清爽了,这种方法的聪明之处在于:将判断条件作为对象的属性名,将处理逻辑作为对象的属性值,在按钮点击的时候,通过对象属性查找的方式来进行逻辑判断,这种写法特别适合一元条件判断的情况。是不是还有其他写法呢?有的:cotactio=ewMa([[1,['roceig','IdexPage']],[2,['fail','FailPage']],[3,['fail','FailPage']],[4,['ucce','SuccePage']],[5,['cacel','CacelPage']],['default',['other','Idex']]])/***按钮点击事件*@aram{umer}tatu活动状态:1开团进行中2开团失败3商品售罄4开团成功5系统取消*/cotoButtoClick=(tatu)=gt{letactio=actio.get(tatu)||actio.get('default')edLog(actio[0])jumTo(actio[1])}复制代码这样写用到了e6里的Ma对象,是不是更爽了?Ma对象和Oject对象有什么区别呢?一个对象通常都有自己的原型,所以一个对象总有一个quotrototyequot键。一个对象的键只能是字符串或者Symol,但一个Ma的键可以是任意值。你可以通过ize属性很容易地得到一个Ma的键值对个数,而对象的键值对个数只能手动确认。我们需要把问题升级一下,以前按钮点击时候只需要判断tatu,现在还需要判断用户的身份:/***按钮点击事件*@aram{umer}tatu活动状态:1开团进行中2开团失败3开团成功4商品售罄5有库存未开团*@aram{trig}idetity身份标识:guet客态mater主态*/cotoButtoClick=(tatu,idetity)=gt{if(idetity=='guet'){if(tatu==1){//doth}eleif(tatu==2){//doth}eleif(tatu==3){//doth}eleif(tatu==4){//doth}eleif(tatu==5){//doth}ele{//doth}}eleif(idetity=='mater'){if(tatu==1){//doth}eleif(tatu==2){//doth}eleif(tatu==3){//doth}eleif(tatu==4){//doth}eleif(tatu==5){//doth}ele{//doth}}}复制代码原谅我不写每个判断里的具体逻辑了,因为代码太冗长了。原谅我又用了if/ele,因为我看到很多人依然在用if/ele写这种大段的逻辑判断。从上面的例子我们可以看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的代码量也会加倍,这时怎么写更清爽呢?cotactio=ewMa([['guet_1',()=gt{/*doth*/}],['guet_2',()=gt{/*doth*/}],['guet_3',()=gt{/*doth*/}],['guet_4',()=gt{/*doth*/}],['guet_5',()=gt{/*doth*/}],['mater_1',()=gt{/*doth*/}],['mater_2',()=gt{/*doth*/}],['mater_3',()=gt{/*doth*/}],['mater_4',()=gt{/*doth*/}],['mater_5',()=gt{/*doth*/}],['default',()=gt{/*doth*/}],])/***按钮点击事件*@aram{trig}idetity身份标识:guet客态mater主态*@aram{umer}tatu活动状态:1开团进行中2开团失败3开团成功4商品售罄5有库存未开团*/cotoButtoClick=(idetity,tatu)=gt{letactio=actio.get(`${idetity}_${tatu}`)||actio.get('default')actio.call(thi)}复制代码上述代码核心逻辑是:把两个条件拼接成字符串,并通过以条件拼接字符串作为键,以处理函数作为值的Ma对象进行查找并执行,这种写法在多元条件判断时候尤其好用。当然上述代码如果用Oject对象来实现也是类似的:cotactio={'guet_1':()=gt{/*doth*/},'guet_2':()=gt{/*doth*/},//....}cotoButtoClick=(idetity,tatu)=gt{letactio=actio[`${idetity}_${tatu}`]||actio['default']actio.call(thi)}复制代码如果有些同学觉得把查询条件拼成字符串有点别扭,那还有一种方案,就是用Ma对象,以Oject对象作为key:cotactio=ewMa([[{idetity:'guet',tatu:1},()=gt{/*doth*/}],[{idetity:'guet',tatu:2},()=gt{/*doth*/}],//...])cotoButtoClick=(idetity,tatu)=gt{letactio=[...actio].filter(([key,value])=gt(key.idetity==idetityamamkey.tatu==tatu))actio.forEach(([key,value])=gtvalue.call(thi))}复制代码是不是又高级了一点点?这里也看出来Ma与Oject的区别,Ma可以用任何类型的数据作为key。我们现在再将难度升级一点点,假如guet情况下,tatu1-4的处理逻辑都一样怎么办,最差的情况是这样:cotactio=ewMa([[{idetity:'guet',tatu:1},()=gt{/*fuctioA*/}],[{idetity:'guet',tatu:2},()=gt{/*fuctioA*/}],[{idetity:'guet',tatu:3},()=gt{/*fuctioA*/}],[{idetity:'guet',tatu:4},()=gt{/*fuctioA*/}],[{idetity:'guet',tatu:5},()=gt{/*fuctioB*/}],//...])复制代码好一点的写法是将处理逻辑函数进行缓存:cotactio=()=gt{cotfuctioA=()=gt{/*doth*/}cotfuctioB=()=gt{/*doth*/}returewMa([[{idetity:'guet',tatu:1},fuctioA],[{idetity:'guet',tatu:2},fuctioA],[{idetity:'guet',tatu:3},fuctioA],[{idetity:'guet',tatu:4},fuctioA],[{idetity:'guet',tatu:5},fuctioB],//...])}cotoButtoClick=(idetity,tatu)=gt{letactio=[...actio()].filter(([key,value])=gt(key.idetity==idetityamamkey.tatu==tatu))actio.forEach(([key,value])=gtvalue.call(thi))}复制代码这样写已经能满足日常需求了,但认真一点讲,上面重写了4次fuctioA还是有点不爽,假如判断条件变得特别复杂,比如idetity有3种状态,tatu有10种状态,那你需要定义30条处理逻辑,而往往这些逻辑里面很多都是相同的,这似乎也是笔者不想接受的,那可以这样实现:cotactio=()=gt{cotfuctioA=()=gt{/*doth*/}cotfuctioB=()=gt{/*doth*/}returewMa([[/^guet_[1-4]$/,fuctioA],[/^guet_5$/,fuctioB],//...])}cotoButtoClick=(idetity,tatu)=gt{letactio=[...actio()].filter(([key,value])=gt(key.tet(`${idetity}_${tatu}`)))actio.forEach(([key,value])=gtvalue.call(thi))}复制代码这里Ma的优势更加凸显,可以用正则类型作为key了,这样就有了无限可能,假如需求变成,凡是guet情况都要发送一个日志埋点,不同tatu情况也需要单独的逻辑处理,那我们可以这样写:cotactio=()=gt{cotfuctioA=()=gt{/*doth*/}cotfuctioB=()=gt{/*doth*/}cotfuctioC=()=gt{/*edlog*/}returewMa([[/^guet_[1-4]$/,fuctioA],[/^guet_5$/,fuctioB],[/^guet_.*$/,fuctioC],//...])}cotoButtoClick=(idetity,tatu)=gt{letactio=[...actio()].filter(([key,value])=gt(key.tet(`${idetity}_${tatu}`)))actio.forEach(([key,value])=gtvalue.call(thi))}复制代码也就是说利用数组循环的特性,符合正则条件的逻辑都会被执行,那就可以同时执行公共逻辑和单独逻辑,因为正则的存在,你可以打开想象力解锁更多的玩法,本文就不赘述了。总结本文已经教你了8种逻辑判断写法,包括:if/elewitch一元判断时:存到Oject里一元判断时:存到Ma里多元判断时:将coditio拼接成字符串存到Oject里多元判断时:将coditio拼接成字符串存到Ma里多元判断时:将coditio存为Oject存到Ma里多元判断时:将coditio写作正则存到Ma里至此,本文也将告一段落,愿你未来的人生里,不只是有if/ele/witch。

Nginx 常用配置清单

加载中...
Ngix是一个高性能的HTTP和反向代理we服务器,同时也提供了IMAP/POP3/SMTP服务,其因丰富的功能集、稳定性、示例配置文件和低系统资源的消耗受到了开发者的欢迎。本文,我们总结了一些常用的Ngix配置代码,希望对大家有所帮助。侦听端口erver{#StadardHTTPProtocollite80#StadardHTTPSProtocollite443l#Forhtt2lite443lhtt2#Liteo80uigIPv6lite[::]:80#LiteolyouigIPv6lite[::]:80iv6oly=o}访问日志erver{#Relativeorfullathtologfileacce_log/ath/to/file.log#Tur'o'or'off'acce_logo}域名erver{#Litetoyourdomai.comerver_ameyourdomai.com#Litetomultiledomaierver_ameyourdomai.comwww.yourdomai.com#Litetoalldomaierver_ame*.yourdomai.com#Litetoallto-leveldomaierver_ameyourdomai.*#LitetouecifiedHotame(LitetoIPaddreitelf)erver_amequotquot}静态资产erver{lite80erver_ameyourdomai.comlocatio/{root/ath/to/weite}}重定向erver{lite80erver_amewww.yourdomai.comretur301htt://yourdomai.com$requet_uri}erver{lite80erver_amewww.yourdomai.comlocatio/redirect-url{retur301htt://otherdomai.com}}反向代理erver{lite80erver_ameyourdomai.comlocatio/{roxy_ahtt://0.0.0.0:3000#where0.0.0.0:3000iyouralicatioerver(Ex:ode.j)oudo0.0.0.0liteigoort3000}}负载均衡utreamode_j{erver0.0.0.0:3000erver0.0.0.0:4000erver123.131.121.122}erver{lite80erver_ameyourdomai.comlocatio/{roxy_ahtt://ode_j}}SSL协议erver{lite443lerver_ameyourdomai.comlol_certificate/ath/to/cert.eml_certificate_key/ath/to/rivatekey.eml_taligol_talig_verifyol_truted_certificate/ath/to/fullchai.eml_rotocolTLSv1TLSv1.1TLSv1.2l_eio_timeout1hl_eio_cachehared:SSL:50madd_headerStrict-Traort-Securitymax-age=15768000}#PermaetRedirectforHTTPtoHTTPServer{lite80erver_ameyourdomai.comretur301htt://$hot$requet_uri}其实可以采用可视化的方式对Ngix进行配置,我在GitHu上发现了一款可以一键生成Ngix配置的神器,相当给力。先来看看它都支持什么功能的配置:反向代理、HTTPS、HTTP/2、IPv6,缓存、WordPre、CDN、Node.j支持、Pytho(Djago)服务器等等。如果你想在线进行配置,只需要打开网站:htt://gixcofig.io/,按照自己的需求进行操作就行了。选择你的场景,填写好参数,系统就会自动生成配置文件。开源地址:githu.com/digitalocea/gixcofig.io网站:digitalocea.com/commuity/tool/gix

【ES6 系列】 90% 的前端都会使用 ES6 来简化代码,你都用过吗?

加载中...
前言(介绍ECMAScrit)最初JavaScrit语言有2份标准:ECMA-262:主标准,由ECMA国际组织(EcmaIteratioal)负责管理(为了让最初的JavaScrit与最初的JScrit能遵循同一套标准发展而诞生的ECMAScrit,正好排到了作为Ecma的262号标准,所以得到ECMA-262编号。)ISO/IEC16262:第二标准,由国际标准化组织ISO(IteratioalStadardOrgaizatio)和国际电子技术委员会IEC(IteratioalElectrotechicalCommiio)负责管理出于商标版权的原因,规范标准中将这门语言称为ECMAScrit,所以原则上JavaScrit与ECMAScrit指的是同一个东西,但有时也会加以区分:JavaScrit:指语言及其实现ECMAScrit:指语言标准及语言版本,比如ES6表示语言(标准)的第6版ECMAScrit发展历史ECMAScrit1(1997年6月):规范第一版ECMAScrit2(1998年6月):为了同步ISO标准,引入了一些小更新ECMAScrit3(1999年12月):增加了正则表达式、字符串处理、控制语句(do-while、witch)、异常处理(try-catch)等众多核心特性ECMAScrit4(2008年7月废除):本来是一次大规模升级(静态类型、模块、命名空间等),但跨度过大,出现了分歧,最终没能推广使用ECMAScrit5(2009年12月):变化不大,加了一些标准库特性和严格模式ECMAScrit-5.1(2011年6月):又一次小更新,为了同步ISO标准ECMAScrit6(2015年6月):一大波更新,实现了当年ES4的许多设想,并正式改为按年份命名规范版本ECMAScrit2016(2016年6月):第一个年度版本,与ES6相比,发布周期较短,新特性也相对少些ECMAScrit2017(2017年6月):第二个年度版本...以后的ECMAScrit版本(ES2018、ES2019、ES2020等)都在6月正式获准生效开始(聚焦ES6)这里引用阮一峰老师的ES6标准入门一书中的总结:ES6既是一个历史名词,也是一个泛指,含义是5.1版本以后的JavaScrit的下一代标准,涵盖了ES2015、ES2016、ES2017等,而ES2015则是正式名称,特指当年发布的正式版本的语言标准市面上提到的ES6一般是指ES2015标准,但有时也是泛指下一代JavaScrit本文主要讲解以下内容:块级作用域(Blockcoig,ES2015)解构(Detructurig,ES2015)箭头函数(ArrowFuctio,ES2015)模板字符串(temlatetrig,ES2015)剩余参数/展开语法(Retadreadarameter,ES2015)对象字面量简写语法(Ojecthorthad,ES2015)数组实例的iclude()(ES2016)Ayc/await异步语法(ES2017)块级作用域为什么需要块级作用域?ES5只有全局作用域和函数作用域,没有块级作用域,这导致很多场景不合理。第一种场景,内层变量可能会覆盖外层变量。vartm=ewDate()fuctiof(){coole.log(tm)if(fale){vartm='helloworld'}}f()//udefied以上代码的原意是,if代码块的外部使用外层的tm变量,内部使用内层的tm变量。但是,函数f执行后,输出结果为udefied,原因在于变量提升导致内层的tm变量覆盖了外层的tm变量。第二种场景,用来计数的循环变量泄露为全局变量。var='hello'for(vari=Oilt.legthi++){coole.log([i])}coole.log(i)//5上面的代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,而是泄露成了全局变量。let实际上为JavaScrit新增了块级作用域。fuctiofl(){let=5if(true){let=10}coole.log()//5}上面的函数有两个代码块,都声明了变量,运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var定义变量,最后输出的值就是10那么我们能利用块级作用域做什么呢?我们先来做道面试题for(vari=0ilt5i++){etTimeout(()=gt{coole.log(i)},1000)}//55555改成ES6中的letfor(leti=0ilt5i++){etTimeout(()=gt{coole.log(i)},1000)}//01234看到这,相信聪明的你已经理解块级作用域的好处了O(∩_∩)O那么ES5能不能实现块级作用域的效果呢?可以的,我们可以利用闭包for(vari=0ilt5i++){(fuctio(idex){etTimeout(()=gt{coole.log(idex)},1000)})(i)}//01234解构解构:是将一个数据结构分解为更小的部分的过程。ES6中,从数组和对象中提取值,对变量进行赋值。那么解构有什么用处呢?可以大大的简化变量声明操作。//ES5varfoo=1varar=2varaz=3//ES6let[foo,ar,az]=[1,2,3]变量交换:看起来如同镜像。赋值语句的左侧的解构模式,右侧是临时创建的数组字面量。x被赋值为数组中的y,y被赋值为数组中的x。letx=1lety=2[x,y]=[y,x]//x=2,y=1对象解构varoj={x:1,y:2,c:1}let{x,y}=oj//x=1//y=2字符串解构cot[a,,c,d,e]='hello'//a=gth//=gte//c=gtl//d=gtl//e=gto函数参数解构cotxueyue={ame:'雪月',age:18,}fuctiogetAge({ame,age}){retur`${ame}今年${age}岁`}getAge(xueyue)//雪月今年18岁箭头函数ES6允许使用箭头=amgt定义函数varf=v=gtv//等同于ES5的varf=fuctio(v){returv}如果箭头函数不需要参数或需要多个参数,就使用圆括号代表参数部分。varf=()=gt5//等同于ES5的varf=fuctio(){retur5}varum=(uml,um2)=gtuml+um2//等同于ES5的varum=fuctio(uml,um2){returuml+um2}箭头函数可以与解构结合使用。cotfull=({firt,lat})=gtfirt+''+lat//等同于ES5的fuctiofull(ero){returero.firt+''+ero.lat}箭头函数使得表达更加简洁cotiEve==gt%2===0cotquare==gt*varreult=value.ort((a,)=gta-)//等同于ES5的varreult=value.ort(fuctio(a,){retura-})上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。箭头函数使用注意点函数体内的thi对象,就是定义时所在的对象,而不是使用时所在的对象。不可以当作构造函数,也就是说,不可以使用ew命令,否则会抛出一个错误。不可以使用argumet对象,该对象在函数体内不存在。如果要用,可以用ret参数代替。不可以使用yield命令,因此箭头函数不能用作Geerator函数。上面四点中,第一点尤其值得注意。thi对象的指向是可变的,但是在箭头函数中,它是固定的。//ES6fuctiofoo(){etTimeout(()=gt{coole.log('id:',thi.id)},100)}//转换成ES5fuctiofoo(){var_thi=thietTimeout(fuctio(){coole.log('id:',_thi.id)},100)}上面代码中,转换后的ES5版本清楚地说明了,箭头函数里面根本没有自己的thi,而是引用外层的thi。模板字符串模板字符串(temlatetrig)是增强版的字符串,用反引号(``)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。cot{log}=coolecotame='雪月'cotage=18//普通字符串拼接cotreult=ame+'今年'+age+'岁'//使用模板字符串cotreult2=`${ame}今年${age}岁`log(reult)//雪月今年18岁log(reult2)//雪月今年18岁//${}大括号可以放入任意的JavaScrit表达式,可以进行运算cotreult3=`${ame}今年${age*2}岁`log(reult3)//雪月今年36岁剩余参数/展开语法ES6引入了ret参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用argumet对象了。ret参数搭配的变量是一个数组,该变量将多余的参数放入其中。fuctioortNumer(){returArray.rototye.lice.call(argumet).ort()}//使用retcotortNumer=(...umer)=gtumer.ort()比较上面的两种写法可以发现,ret参数的写法更自然也更简洁。扩展运算符(read)是三个点(...)如同ret参数的逆运算将一个数组转为用逗号分隔的参数序列coole.log(...[1,2,3])//123coole.log(1,...[2,3,4],5)//12345下面是扩展运算符取代aly方法的一个实际例子应用Math.max方法简化求出数组中的最大元素。//ESS的写法Math.max.aly(ull,[14,3,77])//ES6的写法Math.max(...[14,3,77])//等同于Math.max(14,3,77)展运算符提供了数组合并的新写法。//ESS[1,2].cocat(more)//ES6[1,2,...more]对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。letz={a:3,:''}let={...z}//{a:3,:''}===z//fale特别注意:...扩展对象,只能做到当对象属性是基本数据类型才是深拷贝,如果是引用数据类型,那就是浅拷贝。letz={a:3,:'',c:{ame:'ccc'}}let={...z}//{a:3,:'',c:{ame:'ccc'}}===z//fale.c===z.c//true//.c跟z.c是同一个引用地址对象字面量简写语法cotame='雪月'//ES5写法cotoj={ame:ame,f:fuctio(){coole.log(thi.ame)},}//ES6简写cotoj2={ame,f(){coole.log(thi.ame)},}oj.f()//雪月oj2.f()//雪月使用vue的同学是不是感到很熟悉ewVue({el:'#a',data(){retur{lit:[],}},})数组实例的iclude()Array.rototye.iclude方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的iclude方法类似。ES2016引入了该方法。[1,2,3].iclude(2)//true[1,2,3].iclude(4)//fale[1,2,NaN].iclude(NaN)//true没有该方法之前,我们通常使用数组的idexOf方法,检查是否包含某个值。//ES5if(arr.idexOf(el)!==-1){//...}//ES6if(arr.iclude(el)){//...}//那么idexOf能不能做到类似于iclude的写法呢?我们可以利用~位运算符if(~arr.idexOf(el)){//...}idexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。[NaN].idexOf(NaN)//-1iclude使用的是不一样的判断算法,就没有这个问题[NaN].iclude(NaN)//trueAyc/await异步语法aycfuctiogetTitle(url){letreoe=awaitfetch(url)lethtml=awaitreoe.text()returhtml.match(/lttitlegt([\\S]+)lt\/titlegt/i)[1]}getTitle('htt://tc39.githu.io/ecma262/').the((re)=gtcoole.log(re))上面代码中,函数getTitle内部有三个操作:抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行the方法里面的coole.log结束(意犹未尽)文章介绍了ES6常用的一些语法以及使用场景但是ES6内容远不止于此,感兴趣的同学可以去阮一峰老师的ES6入门教程一书中查看详细内容。如果您认可这本书,也可以去正版渠道购买书籍。这样可以使出版社不因出版开源书籍而亏钱,进而鼓励更多的作者开源自己的书籍。后记(列举API)还有很多ES6实用的API我就简单提及一下,朋友们看看平时是否有用到[1,4,-5,10].fid(=gtlt0)//-5[1,5,10,15].fidIdex((value,idex,arr)=gtvaluegt9)//2[1,2,[3,[4,5]]].flat()//[1,2,3,[4,5]][1,2,[3,[4,5]]].flat(2)//[1,2,3,4,5][3,8,54,8,3,NaN,NaN,'NaN','NaN'].filter((umer,idex,arr)=gtarr.idexOf(umer)===idex)//[3,8,54,quotNaNquot]利用filter过滤去重,注意会漏掉NaN[1,2,3,4].ma((item)=gtitem*2)//[2,4,6,8]利用ma返回一个新数组,不改变原数组//使用reduce求和reduce功能极其强大!yyd[0,1,2,3,4].reduce(fuctio(accumulator,curretValue,curretIdex,array){returaccumulator+curretValue})//10//ES2017引入了跟Oject.key配套的Oject.value和Oject.etrie,作为遍历一个对象的补充手段,//供for...of循环使用。let{key,value,etrie}=Ojectletoj={a:1,:2,c:3}for(letkeyofkey(oj)){coole.log(key)//'a','','c'}for(letvalueofvalue(oj)){coole.log(value)//1,2,3}for(let[key,value]ofetrie(oj)){coole.log([key,value])//['a',1],['',2],['c',3]}

每日必应图片接口

JS实现继承的几种方式

JS实现继承的几种方式先定义一个父类fuctioFather(ame){//属性thi.ame=ame||'father'//实例方法thi.ayName=fuctio(){coole.log(thi.ame)}thi.color=['lue']}//原型方法Father.rototye.age=20Father.rototye.ayAge=fuctio(){coole.log(thi.age)}1.原型链继承将父类的实例作为子类的原型fuctioSo(ame){thi.ame=ame|'So'}So.rototye=ewFather()let1=ewSo()coole.log(1.ame)//Socoole.log(1.age)//20优点:简单,易于实现父类新增原型方法、原型属性,子类都能访问到缺点:无法实现多继承,因为原型一次只能被一个实例更改要想为子类新增属性和方法,必须要在ewFather()这样的语句之后执行,不能放到构造器中来自原型对象的所有属性被所有实例共享创建子类实例时,无法向父构造函数传参2.构造继承使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)fuctioSo(ame){Father.call(thi)thi.ame=ame||'o'}let=ewSo('So')coole.log(.ame)//So//.ayAge()抛出错误,无法继承父类原型方法//.ayName()Socoole.log(.age)//udefied(无法继承父类原型方法)优点:解决了原型链继承中子类实例共享父类引用属性的问题创建子类实例时,可以向父类传递参数可以实现多继承(call多个父类对象)缺点:实例并不是父类的实例,只是子类的实例只能继承父类的实例属性和方法,不能继承原型属性/方法无法实现函数复用,每个子类都有父类实例函数的副本,影响性能3.组合继承将原型链和借用构造函数的技术组合到一块。使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承fuctioSo(ame){//第一次调用父类构造器子类实例增加父类实例Father.call(thi,quot我是传给父类的参数quot)thi.ame=ame||quotoquot}//经过ew运算符第二次调用父类构造器子类原型也增加了父类实例So.rototye=ewFather()let=ewSo(quotoquot)coole.log(.ame)//o.ayAge()//18.ayName()//ocoole.log(.age)//18coole.log(itaceofFather)//truecoole.log(itaceofSo)//truecoole.log(.cotructor===Father)//truecoole.log(.cotructor===So)//fale优点:弥补了构造继承的缺点,现在既可以继承实例的属性和方法,也可以继承原型的属性和方法既是子类的实例,也是父类的实例可以向父类传递参数函数可以复用缺点:调用了两次父类构造函数,生成了两份实例cotructor指向问题4.实例继承为父类实例添加新特征,作为子类实例返回fuctioSo(ame){letf=ewFather('传给父类的参数')f.ame=ame||'o'returf}let=ewSo(quotoquot)//或者直接调用子类构造函数let=So(quotoquot)coole.log(.ame)//o.ayAge()//18.ayName()//ocoole.log(.age)//18coole.log(itaceofFather)//truecoole.log(itaceofSo)//falecoole.log(.cotructor===Father)//truecoole.log(.cotructor===So)//fale优点:不限制调用方式,不管是ew子类()还是子类(),返回的对象具有相同的效果缺点:实例是父类的实例,不是子类的实例不支持多继承5.拷贝继承对父类实例中的的方法与属性拷贝给子类的原型fuctioSo(ame){letf=ewFather(quot传给父类的参数quot)for(letkif){So.rototye[k]=f[k]}So.rototye.ame=ame}let=ewSo(quotoquot)coole.log(.ame)//o.ayAge()//18.ayName()//ocoole.log(.age)//18coole.log(itaceofFather)//falecoole.log(itaceofSo)//truecoole.log(.cotructor===Father)//falecoole.log(.cotructor===So)//true优点:支持多继承缺点:效率低,性能差,占用内存高(因为需要拷贝父类属性)无法获取父类不可枚举的方法(不可枚举的方法,不能使用for-i访问到)6.寄生组合继承通过寄生方式,砍掉父类的实例属性,避免了组合继承生成两份实例的缺点fuctioSo(ame){Father.call(thi)thi.ame=ame||quotoquot}//方法一自己动手创建一个中间类//(fuctio(){//letNoeFu=fuctio(){}//NoeFu.rototye=Father.rototye//So.rototye=ewNoeFu()//So.rototye.cotructor=So//})()//方法二直接借用Oject.create()方法So.rototye=Oject.create(Father.rototye)//修复构造函数指向So.rototye.cotructor=Solet=ewSo(quotoquot)coole.log(.ame)//o.ayAge()//18.ayName()//ocoole.log(.age)//18coole.log(itaceofFather)//truecoole.log(itaceofSo)//truecoole.log(.cotructor===Father)//falecoole.log(.cotructor===So)//true优点:比较完美(j实现继承首选方式)缺点:1.实现起来较为复杂(可通过Oject.create简化)7.e6--Cla继承使用exted表明继承自哪个父类,并且在子类构造函数中必须调用uerclaSoextedFather{cotructor(ame){uer(ame)thi.ame=ame||quotoquot}}let=ewSo(quotoquot)coole.log(.ame)//o.ayAge()//18.ayName()//ocoole.log(.age)//18coole.log(itaceofFather)//truecoole.log(itaceofSo)//truecoole.log(.cotructor===Father)//falecoole.log(.cotructor===So)//tru

JavaScript 中 call()、apply()、bind() 的用法

JavaScrit中call()、aly()、id()的用法其实是一个很简单的东西,认真看10分钟就从一脸懵B到完全理解!先看明白下面:例1varame='小王',age=17varoj={ame:'小张',ojAge:thi.age,myFu:()=gt{coole.log(`${thi.ame}年龄${thi.age}`)}}oj.ojAge//17oj.myFu()//小张年龄udefied例2varfav='盲僧'fuctiohow(){coole.log(thi.fav)}how()//盲僧比较一下两者thi的差别,第一个打印里的thi指向oj,第二个全局声明的how()函数thi是widow1.call()、aly()、id()都是用来定义thi这个对象的如:varame='小王',age=17varoj={ame:'小张',ojAge:thi.age,myFu:()=gt{coole.log(`${thi.ame}年龄${thi.age}`)}}vard={ame:'德玛',age:99}oj.myFu.call(d);//德玛年龄99oj.myFu.aly(d)//德玛年龄99oj.myFu.id(d)()//德玛年龄99以上出了id方法后面多了个()外,结果返回都一致~由此得出结论,id返回的是一个新的函数,你必须调用它才会被执行2.对比call、id、aly传参情况下varame='小王',age=17varoj={ame:'小张',ojAge:thi.age,myFu:(form,t)=gt{coole.log(`${thi.ame}年龄${thi.age},来自${form},去往${t}`)}}vard={ame:'德玛',age:99}oj.myFu.call(d,'成都','上海')oj.myFu.aly(d,['成都','上海'])oj.myFu.id(d,'成都','上海')()oj.myFu.id(d,['成都','上海'])()微妙的差距!从上面四个结果不难看出:call、id、aly这三个函数的第一个参数都是thi的指向对象,第二个参数差别就来了:call的参数是直接放进去的,第二第三第个参数全都用逗号分隔,直接放到后面oj.myFu.call(d,'成都',...,'trig')。aly的所有参数都必须放在一个数组里面传进去oj.myFu.aly(d,['成都',...,'trig'])id除了返回是函数以外,它的参数和call一样当然,三者的参数不限定是trig类型,允许是各种类型,包括函数、oject等等!