加载中...
JavaScrit可以做很多神奇的事情!从复杂的框架到处理API,有太多的东西需要学习。但是,它也能让你只用一行代码就能做一些了不起的事情。看看这13句JavaScrit单行代码,会让你看起来像个专家!1.获取一个随机布尔值(true/fale)这个函数使用Math.radom()方法返回一个布尔值(true或fale)。Math.radom将在0和1之间创建一个随机数,之后我们检查它是否高于或低于0.5。这意味着得到真或假的几率是50%/50%。cotradomBoolea=()=gtMath.radom()gt=0.5coole.log(radomBoolea())//Reult:a50/50chageoreturigtrueoffale2.检查日期是否为工作日使用这个方法,你就可以检查函数参数是工作日还是周末。cotiWeekday=(date)=gtdate.getDay()%6!==0coole.log(iWeekday(ewDate(2021,0,11)))//Reult:true(Moday)coole.log(iWeekday(ewDate(2021,0,10)))//Reult:fale(Suday)3.反转字符串有几种不同的方法来反转一个字符串。以下代码是最简单的方式之一。cotrevere=tr=gttr.lit('').revere().joi('')revere('helloworld')//Reult:'dlrowolleh'4.检查当前Ta页是否在前台我们可以通过使用documet.hidde属性来检查当前标签页是否在前台中cotiBrowerTaIView=()=gtdocumet.hiddeiBrowerTaIView()//Reult:returtrueorfaledeedigoiftaiiview/focu5.检查数字是否为奇数最简单的方式是通过使用模数运算符(%)来解决。如果你对它不太熟悉,这里是tackoverflow上的一个很好的图解。cotiEve=um=gtum%2===0coole.log(iEve(2))//Reult:truecoole.log(iEve(3))//Reult:fale6.从日期中获取时间通过使用toTimeStrig()方法,在正确的位置对字符串进行切片,我们可以从提供的日期中获取时间或者当前时间。cottimeFromDate=date=gtdate.toTimeStrig().lice(0,8)coole.log(timeFromDate(ewDate(2021,0,10,17,30,0)))//Reult:quot17:30:00quotcoole.log(timeFromDate(ewDate()))//Reult:willlogthecurrettime7.保留小数点(非四舍五入)使用Math.ow()方法,我们可以将一个数字截断到某个小数点。cottoFixed=(,fixed)=gt~~(Math.ow(10,fixed)*)/Math.ow(10,fixed)//ExamletoFixed(25.198726354,1)//25.1toFixed(25.198726354,2)//25.19toFixed(25.198726354,3)//25.198toFixed(25.198726354,4)//25.1987toFixed(25.198726354,5)//25.19872toFixed(25.198726354,6)//25.1987268.检查元素当前是否为聚焦状态cotelemetIIFocu=(el)=gt(el===documet.activeElemet)elemetIIFocu(ayElemet)//Reult:willreturtrueififocu,faleifotifocu9.检查浏览器是否支持触摸事件cottouchSuorted=()=gt{('otouchtart'iwidow||widow.DocumetTouchamamdocumetitaceofwidow.DocumetTouch)}coole.log(touchSuorted())//Reult:willreturtrueiftouchevetareuorted,faleifot10.检查当前用户是否为苹果设备我们可以使用avigator.latform来检查当前用户是否为苹果设备。cotiAleDevice=/Mac|iPod|iPhoe|iPad/.tet(avigator.latform)coole.log(iAleDevice)//Reult:willreturtrueifuerioaAledevice11.滚动到页面顶部widow.crollTo()方法会取一个x和y坐标来进行滚动。如果我们将这些坐标设置为零,就可以滚动到页面的顶部。注意:IE不支持crollTo()方法。cotgoToTo=()=gtwidow.crollTo(0,0)goToTo()//Reult:willcrolltherowertothetooftheage12.获取所有参数平均值我们可以使用reduce方法来获得函数参数的平均值。cotaverage=(...arg)=gtarg.reduce((a,)=gta+)/arg.legthaverage(1,2,3,4)//Reult:2.513.转换华氏度/摄氏度。(这个应该很少在国内用到吧)处理温度有时会让人感到困惑。这2个功能将帮助你将华氏温度转换为摄氏温度,反之亦然。cotceliuToFahreheit=(celiu)=gtceliu*9/5+32cotfahreheitToCeliu=(fahreheit)=gt(fahreheit-32)*5/9//ExamleceliuToFahreheit(15)//59celiuToFahreheit(0)//32celiuToFahreheit(-20)//-4fahreheitToCeliu(59)//15fahreheitToCeliu(32)//0谢谢你的阅读!希望你今天能学到一些新的东西。
加载中...
在这里,我不会浪费篇幅给大家讲如何写配置文件。如果你想学习这方面的内容,那么完全可以去官网学习。在这部分的内容中,我们会聚焦于以下两个知识点,并且每一个知识点都属于高频考点:有哪些方式可以减少Weack的打包时间有哪些方式可以让Weack打出来的包更小减少Weack打包时间优化Loader对于Loader来说,影响打包效率首当其冲必属Bael了。因为Bael会将代码转为字符串生成AST,然后对AST继续进行转变最后再生成新的代码,项目越大,转换代码越多,效率就越低。当然了,我们是有办法优化的。首先我们可以优化Loader的文件搜索范围module.exort={module:{rule:[{//j文件才使用aeltet:/\.j$/,loader:'ael-loader',//只在rc文件夹下查找iclude:[reolve('rc')],//不会去查找的路径exclude:/ode_module/}]}}对于Bael来说,我们肯定是希望只作用在JS代码上的,然后ode_module中使用的代码都是编译过的,所以我们也完全没有必要再去处理一遍。当然这样做还不够,我们还可以将Bael编译过的文件缓存起来,下次只需要编译更改过的代码文件即可,这样可以大幅度加快打包时间loader:'ael-loader?cacheDirectory=true'HayPack受限于Node是单线程运行的,所以Weack在打包的过程中也是单线程的,特别是在执行Loader的时候,长时间编译的任务很多,这样就会导致等待的情况。HayPack可以将Loader的同步执行转换为并行的,这样就能充分利用系统资源来加快打包效率了module:{loader:[{tet:/\.j$/,iclude:[reolve('rc')],exclude:/ode_module/,//id后面的内容对应下面loader:'hayack/loader?id=hayael'}]},lugi:[ewHayPack({id:'hayael',loader:['ael-loader?cacheDirectory'],//开启4个线程thread:4})]DllPlugiDllPlugi可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。接下来我们就来学习如何使用DllPlugi//单独配置在一个文件中//weack.dll.cof.jcotath=require('ath')cotweack=require('weack')module.exort={etry:{//想统一打包的类库vedor:['react']},outut:{ath:ath.joi(__dirame,'dit'),fileame:'[ame].dll.j',lirary:'[ame]-[hah]'},lugi:[ewweack.DllPlugi({//ame必须和outut.lirary一致ame:'[ame]-[hah]',//该属性需要与DllReferecePlugi中一致cotext:__dirame,ath:ath.joi(__dirame,'dit','[ame]-maifet.jo')})]}然后我们需要执行这个配置文件生成依赖文件,接下来我们需要使用DllReferecePlugi将依赖文件引入项目中//weack.cof.jmodule.exort={//...省略其他配置lugi:[ewweack.DllReferecePlugi({cotext:__dirame,//maifet就是之前打包出来的jo文件maifet:require('./dit/vedor-maifet.jo'),})]}ltacla=quotlake-achorquottyle=quotto:9xquotgtltacla=quotlake-achor-uttolake-icolake-ico-h3quotgtlt/agtlt/agt代码压缩在Weack3中,我们一般使用UglifyJS来压缩代码,但是这个是单线程运行的,为了加快效率,我们可以使用weack-arallel-uglify-lugi来并行运行UglifyJS,从而提高效率。在Weack4中,我们就不需要以上这些操作了,只需要将mode设置为roductio就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩JS代码,还可以压缩HTML、CSS代码,并且在压缩JS代码的过程中,我们还可以通过配置实现比如删除coole.log这类代码的功能。一些小的优化点我们还可以通过一些小的优化点来加快打包速度reolve.exteio:用来表明文件后缀列表,默认查找顺序是['.j','.jo'],如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度,然后将出现频率高的后缀排在前面reolve.alia:可以通过别名的方式来映射一个路径,能让Weack更快找到路径module.oPare:如果你确定一个文件下没有其他依赖,就可以使用该属性让Weack不扫描该文件,这种方式对于大型的类库很有帮助减少Weack打包后的文件体积注意:该内容也属于性能优化领域。按需加载想必大家在开发SPA项目的时候,项目中都会存在十几甚至更多的路由页面。如果我们将这些页面全部打包进一个JS文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。那么为了首页能更快地呈现给用户,我们肯定是希望首页能加载的文件体积越小越好,这时候我们就可以使用按需加载,将每个路由页面单独打包为一个文件。当然不仅仅路由可以按需加载,对于loadah这种大型类库同样可以使用这个功能。按需加载的代码实现这里就不详细展开了,因为鉴于用的框架不同,实现起来都是不一样的。当然了,虽然他们的用法可能不同,但是底层的机制都是一样的。都是当使用的时候再去下载对应文件,返回一个Promie,当Promie成功以后去执行回调。ScoeHoitigScoeHoitig会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去。比如我们希望打包两个文件//tet.jexortcota=1//idex.jimort{a}from'./tet.j'对于这种情况,我们打包出来的代码会类似这样[/*0*/fuctio(module,exort,require){//...},/*1*/fuctio(module,exort,require){//...}]但是如果我们使用ScoeHoitig的话,代码就会尽可能的合并到一个函数中去,也就变成了这样的类似代码[/*0*/fuctio(module,exort,require){//...}]这样的打包方式生成的代码明显比之前的少多了。如果在Weack4中你希望开启这个功能,只需要启用otimizatio.cocateateModule就可以了。module.exort={otimizatio:{cocateateModule:true}}TreeShakigTreeShakig可以实现删除项目中未被引用的代码,比如//tet.jexortcota=1exortcot=2//idex.jimort{a}from'./tet.j'对于以上情况,tet文件中的变量如果没有在项目中使用到的话,就不会被打包到文件中。如果你使用Weack4的话,开启生产环境就会自动启动这个优化功能。小结在这一章节中,我们学习了如何使用Weack去进行性能优化以及如何减少打包时间。Weack的版本更新很快,各个版本之间实现优化的方式可能都会有区别,所以我没有使用过多的代码去展示如何实现一个功能。这里重点是学习到我们可以通过什么方式去优化,具体的代码实现可以查找具体版本对应的代码即可。
加载中...
此文参考MDN_thi写成定义thi是函数运行时自动生成的内部对象,即调用函数的那个对象。(不一定很准确的定义,但还算通俗易懂)在大多数情况下,thi的值由函数调用方式决定,它不能在执行期间赋值来设置,它在每次执行下可能都有不同的值。全局执行环境(outidefuctio)在全局执行环境中,thi一直指向全局对象(gloaloject),不管是在严格模式还是在非严格模式中。代码1coole.log(thi.documet===documet)//true//在浏览器中,widow对象也就是全局对象(gloaloject)coole.log(thi===widow)//truethi.a=37coole.log(widow.a)//37ltacla=quotcoy-code-tquotgt复制代码lt/agt函数执行环境(iidefuctio)在函数执行环境中,thi的值取决于函数的调用方式。而函数的调用方式主要有4种:函数直接调用对象方法调用构造函数调用call/aly/id箭头函数(ES6)函数直接调用下面的代码在非严格模式执行时,thi的值会指向全局对象;而在严格模式中,thi的值将会默认为udefied。代码2/*非严格模式*/fuctiof1(){returthi}coole.log(f1()===widow)//true//iodecoole.log(f1()===gloal)//true/*严格模式*/fuctiof2(){'uetrict'returthi}coole.log(f1()===udefied)//truecall/aly/id改变thi的指向call/alycall和aly的用法很像,只是后面参数的传入形式不同。代码3fuctioadd(c,d){returthi.a+thi.+c+d}varo={a:1,:3}//call的第一个参数是对象,也就是thi的指向对象。后面的参数就是函数argumet对象的成员add.call(o,5,7)//1+3+5+7=16//call的第一个参数是对象,也就是thi的指向对象。后面的参数是数组,数组里的成员也就是函数argumet对象成员add.aly(o,[10,20])//1+3+10+20=34使用call和aly时需要注意的是,当传入的第一个参数的值不是对象时,JavaScrit会尝试使用ToOject操作将其转化为对象。代码4fuctioar(){coole.log(Oject.rototye.toStrig.call(thi))}ar.call(7)//[ojectNumer]ltacla=quotcoy-code-tquotgt复制代码lt/agtid方法ECMAScrit5引入了Fuctio.rototye.id。调用f.id(omeOject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,thi将永久地被绑定到了id的第一个参数,无论这个函数是如何被调用的。代码5fuctiof(){returthi.a}varg=f.id({a:'azerty'})//生成一个绑定函数gcoole.log(g())//azertyvaro={a:10,f:f,g:g}coole.log(o.f(),o.g())//10,azerty//需要注意的是,绑定函数不可以再idvarh=g.id({a:'foo'})coole.log(h())//azerty,不会变成foo对象方法调用当以对象里的方法的方式调用函数时,它们的thi是调用该函数的对象.下面的例子中,当o.f()被调用时,函数内的thi将绑定到o对象。varro=36varo={ro:37,ar:fuctio(){returthi.ro}}coole.log(o.ar())//37构造函数调用代码6fuctioPero(ame,age){thi.ame=amethi.age=agethi.itroduce=fuctio(){coole.log('Myamei'+thi.ame+',I\'m'+thi.age)}}varJoeh=ewPero('Joeh',19)Joeh.itroduce()//quotMyameiJoeh,I'm19quot由上述代码可以清晰的看到thi与被新创建的对象绑定了。注意:当构造器返回的默认值是一个thi引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回thi。(这句话看起来比较难理解,我们看下一个例子)。代码7fuctioF2(){thi.a=9//deadcoderetur{a:10}}varo=ewF2()coole.log(o.a)//10这个例子说明了当构造函数返回的是一个对象的话,此时thi的值会变成此时返回的对象。‘thi.a=9’成了僵尸代码。箭头函数在箭头函数(Arrowfuctio)中,thi的值是封闭执行环境决定的。在全局环境中,那么被赋值为全局对象。vargloalOject=thivarfoo=(()=amgtthi)coole.log(foo()===gloalOject)//true更重要的是它与其他情况不同的是,不管函数如何调用,上面thi的值一直都是全局对象。call/id也不能改变它的值。代码8//作为对象方法被调用varoj={foo:foo}coole.log(oj.foo()===gloalOject)//true//尝试用call改变thi的值coole.log(foo.call(oj)===gloalOject)//true//thi的值并未变成oj//尝试用id改变thi的值foo=foo.id(oj)coole.log(foo()===gloalOject)//true案例本文知识点都看完了。让我们看几个案例,检查自己的掌握情况。例1varro=36varo={ro:37,ar1:fuctio(){fuctiofoo1(){returthi.ro}returfoo1},ar2:fuctio(){varfoo2=(()=amgtthi.ro)//ES6箭头函数returfoo2}}coole.log('reult1:'+o.ar1()())//reult1?coole.log('reult2:'+o.ar2()())//reult2?varf2=o.ar2coole.log('reult3:'+f2()())//reult3?先揭晓答案:例1reult1=36,reult2=37,reult3=36。我的理解是,在reult1中,o.ar1()执行导致foo函数retur到了全局环境中,然后执行就变成了在全局中执行,所以得到的是全局中36的值。reult2呢?因为thi在箭头函数中。它的值不会改变。所以thi仍指向o。那为什么reult3又重新变了呢?因为此时varf2=o.ar2相当于重新定义了一个函数,而thi的值当然也就变为了全局对象。//相当于这样varf2=fuctio(){fuctiofoo1(){returthi.ro}returfoo1}f2()()例2fuctioum(a,){retura+}varo={um:1,f:fuctio(){fuctiohadle(){returthi.um=um(thi.um,thi.um)}hadle()}}coole.log('reult:'+o.f())//reult?同样先揭晓答案:reult=udefied,用控制台可以看到此时thi指向widow,而不是o。这是个比较容易掉进去的坑(一般认为是当初的语言设计错误,被人诟病不少)。看似函数是由对象方法调用的,其实细心的话,我们可以看到。hadle函数的执行,前面的没有对象的。这种情况下,thi指向全局对象。解决办法也很简单。//1、取消hadle函数的定义,直接在对象的方法中使用thif2:fuctio(){thi.value=um(thi.value,thi.value)//2},///2、使用变量保存外部函数的thi。f3:fuctio(){varthat=thi//that==ofuctiohadle(){that.value=add(that.value,that.value)}hadle()}参考文章:Javacrit中thi与闭包学习笔记lt/ligt理解JS中的thilt/ligt
加载中...
为了保证可读性,本文采用意译而非直译,并且对示例代码进行了大量修改。另外,本文版权归原作者所有,翻译仅用于学习。1.立即执行函数立即执行函数,即ImmediatelyIvokedFuctioExreio(IIFE),正如它的名字,就是创建函数的同时立即执行。它没有绑定任何事件,也无需等待任何异步操作:(fuctio(){//代码//...})()fuctio(){…}是一个匿名函数,包围它的一对括号将其转换为一个表达式,紧跟其后的一对括号调用了这个函数。立即执行函数也可以理解为立即调用一个匿名函数。立即执行函数最常见的应用场景就是:将var变量的作用域限制于你们函数内,这样可以避免命名冲突。2.闭包对于闭包(cloure),当外部函数返回之后,内部函数依然可以访问外部函数的变量。fuctiof1(){varN=0//N是f1函数的局部变量fuctiof2()//f2是f1函数的内部函数,是闭包{N+=1//内部函数f2中使用了外部函数f1中的变量Ncoole.log(N)}returf2}varreult=f1()reult()//输出1reult()//输出2reult()//输出3代码中,外部函数f1只执行了一次,变量N设为0,并将内部函数f2赋值给了变量reult。由于外部函数f1已经执行完毕,其内部变量N应该在内存中被清除,然而事实并不是这样:我们每次调用reult的时候,发现变量N一直在内存中,并且在累加。为什么呢?这就是闭包的神奇之处了!3.使用闭包定义私有变量通常,JavaScrit开发者使用下划线作为私有变量的前缀。但是实际上,这些变量依然可以被访问和修改,并非真正的私有变量。这时,使用闭包可以定义真正的私有变量:fuctioProduct(){varamethi.etName=fuctio(value){ame=value}thi.getName=fuctio(){returame}}var=ewProduct().etName(quotFudeugquot)coole.log(.ame)//输出udefiedcoole.log(.getName())//输出Fudeug代码中,对象的的ame属性为私有属性,使用.ame不能直接访问。4.rototye每个JavaScrit构造函数都有一个rototye属性,用于设置所有实例对象需要共享的属性和方法。rototye属性不能列举。JavaScrit仅支持通过rototye属性进行继承属性和方法。fuctioRectagle(x,y){thi._legth=xthi._readth=y}Rectagle.rototye.getDimeio=fuctio(){retur{legth:thi._legth,readth:thi._readth}}varx=ewRectagle(3,4)vary=ewRectagle(4,3)coole.log(x.getDimeio())//{legth:3,readth:4}coole.log(y.getDimeio())//{legth:4,readth:3}代码中,x和y都是构造函数Rectagle创建的对象实例,它们通过rototye继承了getDimeio方法。5.模块化JavaScrit并非模块化编程语言,至少ES6落地之前都不是。然而对于一个复杂的We应用,模块化编程是一个最基本的要求。这时,可以使用立即执行函数来实现模块化,正如很多JS库比如jQuery以及我们Fudeug都是这样实现的。varmodule=(fuctio(){varN=5fuctiorit(x){coole.log(quotThereulti:quot+x)}fuctioadd(a){varx=a+Nrit(x)}retur{decritio:quotThiidecritioquot,add:add}})()coole.log(module.decritio)//输出quotthiidecritioquotmodule.add(5)//输出“Thereulti:10”所谓模块化,就是根据需要控制模块内属性与方法的可访问性,即私有或者公开。在代码中,module为一个独立的模块,N为其私有属性,rit为其私有方法,decritio为其公有属性,add为其共有方法。6.变量提升JavaScrit会将所有变量和函数声明移动到它的作用域的最前面,这就是所谓的变量提升(Hoitig)。也就是说,无论你在什么地方声明变量和函数,解释器都会将它们移动到作用域的最前面。因此我们可以先使用变量和函数,而后声明它们。但是,仅仅是变量声明被提升了,而变量赋值不会被提升。如果你不明白这一点,有时则会出错:coole.log(y)//输出udefiedy=2//初始化y上面的代码等价于下面的代码:vary//声明ycoole.log(y)//输出udefiedy=2//初始化y为了避免BUG,开发者应该在每个作用域开始时声明变量和函数。7.柯里化柯里化,即Curryig,可以是函数变得更加灵活。我们可以一次性传入多个参数调用它;也可以只传入一部分参数来调用它,让它返回一个函数去处理剩下的参数。varadd=fuctio(x){returfuctio(y){returx+y}}coole.log(add(1)(1))//输出2varadd1=add(1)coole.log(add1(1))//输出2varadd10=add(10)coole.log(add10(1))//输出11代码中,我们可以一次性传入2个1作为参数add(1)(1),也可以传入1个参数之后获取add1与add10函数,这样使用起来非常灵活。8.aly,call与id方法JavaScrit开发者有必要理解aly、call与id方法的不同点。它们的共同点是第一个参数都是thi,即函数运行时依赖的上下文。三者之中,call方法是最简单的,它等价于指定thi值调用函数:varuer={ame:quotRahulMhatrequot,whatIYourName:fuctio(){coole.log(thi.ame)}}uer.whatIYourName()//输出quotRahulMhatrequot,varuer2={ame:quotNehaSamatquot}uer.whatIYourName.call(uer2)//输出quotNehaSamatquotaly方法与call方法类似。两者唯一的不同点在于,aly方法使用数组指定参数,而call方法每个参数单独需要指定:aly(thiArg,[argArray])call(thiArg,arg1,arg2,…)varuer={greet:quotHello!quot,greetUer:fuctio(uerName){coole.log(thi.greet+quotquot+uerName)}}vargreet1={greet:quotHolaquot}uer.greetUer.call(greet1,quotRahulquot)//输出quotHolaRahulquotuer.greetUer.aly(greet1,[quotRahulquot])//输出quotHolaRahulquot使用id方法,可以为函数绑定thi值,然后作为一个新的函数返回:varuer={greet:quotHello!quot,greetUer:fuctio(uerName){coole.log(thi.greet+quotquot+uerName)}}vargreetHola=uer.greetUer.id({greet:quotHolaquot})vargreetBojour=uer.greetUer.id({greet:quotBojourquot})greetHola(quotRahulquot)//输出quotHolaRahulquotgreetBojour(quotRahulquot)//输出quotBojourRahulquot9.MemoizatioMemoizatio用于优化比较耗时的计算,通过将计算结果缓存到内存中,这样对于同样的输入值,下次只需要中内存中读取结果。fuctiomemoizeFuctio(fuc){varcache={}returfuctio(){varkey=argumet[0]if(cache[key]){returcache[key]}ele{varval=fuc.aly(thi,argumet)cache[key]=valreturval}}}varfioacci=memoizeFuctio(fuctio(){retur(===0||===1)?:fioacci(-1)+fioacci(-2)})coole.log(fioacci(100))//输出354224848179262000000coole.log(fioacci(100))//输出354224848179262000000代码中,第2次计算**fioacci(100)**则只需要在内存中直接读取结果。10.函数重载所谓函数重载(methodoverloadig),就是函数名称一样,但是输入输出不一样。或者说,允许某个函数有各种不同输入,根据不同的输入,返回不同的结果。凭直觉,函数重载可以通过if…ele或者witch实现,这就不去管它了。jQuery之父JohReig提出了一个非常巧(ia)妙(tai)的方法,利用了闭包。从效果上来说,eole对象的fid方法允许3种不同的输入:0个参数时,返回所有人名;1个参数时,根据firtName查找人名并返回;2个参数时,根据完整的名称查找人名并返回。难点在于,eole.fid只能绑定一个函数,那它为何可以处理3种不同的输入呢?它不可能同时绑定3个函数fid0,fid1与fid2啊!这里的关键在于old属性。由addMethod函数的调用顺序可知,eole.fid最终绑定的是fid2函数。然而,在绑定fid2时,old为fid1;同理,绑定fid1时,old为fid0。3个函数fid0,fid1与fid2就这样通过闭包链接起来了。根据addMethod的逻辑,当f.legth与argumet.legth不匹配时,就会去调用old,直到匹配为止。fuctioaddMethod(oject,ame,f){varold=oject[ame]oject[ame]=fuctio(){//f.legth为函数定义时的参数个数//argumet.legth为函数调用时的参数个数if(f.legth===argumet.legth){returf.aly(thi,argumet)}eleif(tyeofold===quotfuctioquot){returold.aly(thi,argumet)}}}//不传参数时,返回所有amefuctiofid0(){returthi.ame}//传一个参数时,返回firtName匹配的amefuctiofid1(firtName){varreult=[]for(vari=0iamltthi.ame.legthi++){if(thi.ame[i].idexOf(firtName)===0){reult.uh(thi.ame[i])}}returreult}//传两个参数时,返回firtName和latName都匹配的amefuctiofid2(firtName,latName){varreult=[]for(vari=0iamltthi.ame.legthi++){if(thi.ame[i]===(firtName+quotquot+latName)){reult.uh(thi.ame[i])}}returreult}vareole={ame:[quotDeaEdwardquot,quotAlexRuellquot,quotDeaTomquot]}addMethod(eole,quotfidquot,fid0)addMethod(eole,quotfidquot,fid1)addMethod(eole,quotfidquot,fid2)coole.log(eole.fid())//输出[quotDeaEdwardquot,quotAlexRuellquot,quotDeaTomquot]coole.log(eole.fid(quotDeaquot))//输出[quotDeaEdwardquot,quotDeaTomquot]coole.log(eole.fid(quotDeaquot,quotEdwardquot))//输出[quotDeaEdwardquot]参考链接闭包–MDNJavaScrit闭包-块级作用域和私有变量Javacrit继承机制的设计思想–阮一峰变量提升–MDNJS函数式编程指南浅谈JavaScrit函数重载
加载中...
JS里的操作符大家每天都在使用,还有一些ES2020、ES2021新加的实用操作符,这些共同构成了JS灵活的语法生态。本文除介绍常用的操作符之外,还会介绍JS里一些不常用但是很强大的操作符,下面我们一起来看看吧~1.数值分割符_ES2021引入了数值分割符_,在数值组之间提供分隔,使一个长数值读起来更容易。Chrome已经提供了对数值分割符的支持,可以在浏览器里试起来。letumer=100_0000_0000_0000//0太多了不用数值分割符眼睛看花了coole.log(umer)//输出100000000000000此外,十进制的小数部分也可以使用数值分割符,二进制、十六进制里也可以使用数值分割符。0x11_1===0x111//true十六进制0.11_1===0.111//true十进制的小数011_1===0111//true二进制2.逗号运算符,什么,逗号也可以是运算符吗?是的,曾经看到这样一个简单的函数,将数组的第一项和第二项调换,并返回两项之和:fuctiorevere(arr){retur[arr[0],arr[1]]=[arr[1],arr[0]],arr[0]+arr[1]}cotlit=[1,2]revere(lit)//返回3,此时lit为[2,1]逗号操作符对它的每个操作数求值(从左到右),并返回最后一个操作数的值。exr1,exr2,exr3...会返回最后一个表达式exr3的结果,其他的表达式只会进行求值。3.零合并操作符??零合并操作符??是一个逻辑操作符,当左侧的操作数为ull或者udefied时,返回右侧操作数,否则返回左侧操作数。exr1??exr2空值合并操作符一般用来为常量提供默认值,保证常量不为ull或者udefied,以前一般使用||来做这件事variale=variale||'ar'。然而,由于||是一个布尔逻辑运算符,左侧的操作数会被强制转换成布尔值用于求值。任何假值(0,'',NaN,ull,udefied)都不会被返回。这导致如果你使用0、''、NaN作为有效值,就会出现不可预料的后果。正因为||存在这样的问题,而??的出现就是解决了这些问题,??只会在左侧为udefied、ull时才返回后者,??可以理解为是||的完善解决方案。可以在浏览器中执行下面的代码感受一下:udefied||'default'//'default'ull||'default'//'default'fale||'default'//'default'0||'default'//'default'udefied??'default'//'default'ull??'default'//'default'fale??'default'//'fale'0??'default'//0另外在赋值的时候,可以运用赋值运算符的简写??=leta={:ull,c:10}a.??=20a.c??=20coole.log(a)//输出{:20,c:10}4.可选链操作符?.可选链操作符?.允许读取位于连接对象链深处的属性的值,而不必验证链中的每个引用是否有效。?.操作符的功能类似于.链式操作符,不同之处在于,在引用为ull或者udefied的情况下不会引起错误,该表达式短路返回值是udefied。当尝试访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。cotoj={a:'foo',:{c:'ar'}}coole.log(oj.?.c)//输出arcoole.log(oj.d?.c)//输出udefiedcoole.log(oj.fuc?.())//不报错,输出udefied以前可能会通过ojamamoj.aamamoj.a.来获取一个深度嵌套的子属性,现在可以直接oj?.a?.即可。可选链除了可以用在获取对象的属性,还可以用在数组的索引arr?.[idex],也可以用在函数的判断fuc?.(arg),当尝试调用一个可能不存在的方法时也可以使用可选链。调用一个对象上可能不存在的方法时(版本原因或者当前用户的设备不支持该功能的场景下),使用可选链可以使得表达式在函数不存在时返回udefied而不是直接抛异常。cotreult=omeIterface.cutomFuc?.()5.私有方法/属性在一个类里面可以给属性前面增加#私有标记的方式来标记为私有,除了属性可以被标记为私有外,getter/etter也可以标记为私有,方法也可以标为私有。claPero{getDec(){returthi.#ame+''+thi.#getAge()}#getAge(){returthi.#age}//私有方法get#ame(){retur'foo'}//私有访问器#age=23//私有属性}cota=ewPero()coole.log(a.age)//udefied直接访问不到coole.log(a.getDec())//foo236.位运算符gtgt与gtgtgt有符号右移操作符gtgt将第一个操作数向右移动指定的位数,多余的位移到右边被丢弃,高位补其符号位,正数补0,负数则补1。因为新的最左位与前一个最左位的值相同,所以符号位(最左位)不会改变。(0111gtgt1).toStrig(2)//quot11quot(-0111gtgt1).toStrig(2)//quot-100quot感觉跟直觉不一样正数的好理解,负数怎么理解呢,负数在计算机中存储是按照补码来存储的,补码的计算方式是取反加一,移位时将补码形式右移,最左边补符号位,移完之后再次取反加一求补码获得处理后的原码。-111//真值10000111//原码(高位的0无所谓,后面加不到)11111001//补码11111100//算数右移10000100//移位后求补码获得原码-100//移位后的真值一般我们用gtgt来将一个数除2,相当于先舍弃小数位然后进行一次Math.floor:10gtgt1//513gtgt1//6相当于13.9gtgt1//6-13gtgt1//-7相当于-13.9gtgt1//-7无符号右移操作符gtgtgt,将符号位作为二进制数据的一部分向右移动,高位始终补0,对于正整数和算数右移没有区别,对于负数来说由于符号位被补0,成为正数后就不用再求补码了,所以结果总是非负的。即便右移0个比特,结果也是非负的。(0111gtgtgt1).toStrig(2)//quot11quot(-0111gtgtgt1).toStrig(2)//quot1111111111111111111111111111100quot可以这样去理解-111//真值1000000000000000000000000000111//原码1111111111111111111111111111001//补码0111111111111111111111111111100//算数右移(由于右移后成为正数,就不要再求补码了)1073741820//移位后的真值左移运算符ltlt与之类似,左移很简单左边移除最高位,低位补0:(01111111111111111111111111111100ltlt1).toStrig(2)//quot-1000quot(01111111111111111111111111111100ltltlt1).toStrig(2)//quot-1000quotPS:JS里面没有无符号左移,而且其他语言比如JAVA也没有无符号左移。7.位运算符am与|.位运算符是按位进行运算,am与、|或、~非、^按位异或:am:1010|:1010~:1010^:1010011001100110----------------0010111001011100使用位运算符时会抛弃小数位,我们可以利用这个特性来给数字取整,比如给任意数字am上二进制的32个1,或者|上0,显而易见后者简单些。所以我们可以对一个数字|0来取整,负数也同样适用1.3|0//1-1.9|0//-1判断奇偶数除了常见的取余%2之外,也可以使用am1,来判断二进制数的最低位是不是1,这样除了最低位之外都被置0,取余的结果只剩最低位,是不是很巧妙。负数也同样适用:cotum=3!!(umam1)//true!!(um%2)//true8.双位运算符~~可以使用双位操作符来替代正数的Math.floor(),替代负数的Math.ceil()。双否定位操作符的优势在于它执行相同的操作运行速度更快。Math.floor(4.9)===4//true//简写为:~~4.9===4//true不过要注意,对正数来说~~运算结果与Math.floor()运算结果相同,而对于负数来说与Math.ceil()的运算结果相同:~~4.5//4Math.floor(4.5)//4Math.ceil(4.5)//5~~-4.5//-4Math.floor(-4.5)//-5Math.ceil(-4.5)//-4PS:注意~~(um/2)方式和umgtgt1在值为负数时的差别9.短路运算符amam与我们知道逻辑与amam与逻辑或||是短路运算符,短路运算符就是从左到右的运算中前者满足要求,就不再执行后者了。可以理解为:amam为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值||为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值letaram1=exr1amamexr2letaram2=exr1||exr2运算符示例说明amamexr1amamexr2如果exr1能转换成fale则返回exr1,否则返回exr2。因此,在Boolea环境中使用时,两个操作结果都为true时返回true,否则返回faleexr1||exr2如果exr1能转换成true则返回exr1,否则返回exr2。因此,在oolea环境(在if的条件判断中)中使用时,二者操作结果中只要有一个为true,返回true;二者操作结果都为fale时返回fale!!exr如果单个表达式能转换为true的话返回fale,否则返回true因此可以用来做很多有意思的事,比如给变量赋初值:letvariale1letvariale2=variale1||'foo'如果variale1是真值就直接返回了,后面短路就不会被返回了,如果为假值,则会返回后面的foo。也可以用来进行简单的判断,取代冗长的if语句:letvariale=aramamamaram.ro//有了可选链之后可以直接aram?.ro如果aram如果为真值则返回aram.ro属性,否则返回aram这个假值,这样在某些地方防止aram为udefied的时候还取其属性造成报错。10.void运算符void运算符对给定的表达式进行求值,然后返回udefied可以用来给在使用立即调用的函数表达式(IIFE)时,可以利用void运算符让JS引擎把一个fuctio关键字识别成函数表达式而不是函数声明。fuctioiife(){coole.log('foo')}()//报错,因为JS引擎把IIFE识别为了函数声明voidfuctioiife(){coole.log('foo')}()//正常调用~fuctioiife(){coole.log('foo')}()//也可以使用一个位操作符(fuctioiife(){coole.log('foo')})()//或者干脆用括号括起来表示为整体的表达式还可以用在箭头函数中避免传值泄漏,箭头函数,允许在函数体不使用括号来直接返回值。这个特性给用户带来了很多便利,但有时候也带来了不必要的麻烦,如果右侧调用了一个原本没有返回值的函数,其返回值改变后,会导致非预期的副作用。cotfuc=()=gtvoidcutomMethod()//特别是给一个事件或者回调函数传一个函数时安全起见,当不希望函数返回值是除了空值以外其他值,应该使用void来确保返回udefied,这样,当cutomMethod返回值发生改变时,也不会影响箭头函数的行为。11.其他常用操作符三元表达式:很简单了,大家经常用,exr?exr1:exr2如果exr为真值则返回exr1,否则返回exr2赋值运算符简写:加法赋值+=、减法赋值-=、乘法赋值*=、除法赋值/=、求幂赋值**=、按位或复制|=、按位与赋值am=、有符号按位右移赋值gtgt=、无符号按位右移赋值gtgtgt=、逻辑空赋值??=....求幂运算符:var1**var2相当于Math.ow,结果为var1的var2次方12.操作符优先级正因为有操作符优先级,所以variale=1,2的含义是将变量先赋值为1,再返回数字2,而不是变量赋值给1,2的返回值2,这是因为=运算符的优先级高于,逗号运算符。再比如表达式6-2*3===0amam1,-*===amam这四个运算符优先级最高的*先运算,然后-运算符结果为0,===运算符优先级高于amam而trueamam1的结果为1,所以这就是运算的结果。下面的表将运算符按照优先级的不同从高(20)到低(1)排列,但这个不是最新的,至少没包括可选链,建议参考这个表或者MDN。网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出,如果本文帮助到了你,别忘了点赞支持一下哦,你的点赞是我更新的最大动力!(收藏不点赞,都是耍流氓🤣)~参考文档:运算符优先级-JavaScrit|MDNJS中可以提升幸福度的小技巧4个未听说过的强大JavaScrit操作符聊聊JavaScrit中的二进制数
加载中...
今天写一个下载页面的时候需要判断adroid或者io设备以及不同的浏览器打开方式。上网搜索了一下教程发现这个教程最清晰简单,并且很好用。在此记录一下,方便以后使用原理:根据avigator.uerAget返回值识别varu=avigator.uerAgetvariAdroid=u.idexOf('Adroid')gt-1||u.idexOf('Adr')gt-1//adroid终端variiOS=!!u.match(/\\(i\[^\]+(U)?CPU.+MacOSX/)//io终端判断访问终端//判断访问终端varrower={verio:fuctio(){varu=avigator.uerAget,a=avigator.aVerioretur{tridet:u.idexOf('Tridet')gt-1,//IE内核reto:u.idexOf('Preto')gt-1,//oera内核weKit:u.idexOf('AleWeKit')gt-1,//苹果、谷歌内核gecko:u.idexOf('Gecko')gt-1amamu.idexOf('KHTML')==-1,//火狐内核moile:!!u.match(/AleWeKit.\*Moile.\*/),//是否为移动终端io:!!u.match(/\\(i\[^\]+(U)?CPU.+MacOSX/),//io终端adroid:u.idexOf('Adroid')gt-1||u.idexOf('Adr')gt-1,//adroid终端iPhoe:u.idexOf('iPhoe')gt-1,//是否为iPhoe或者QQHD浏览器iPad:u.idexOf('iPad')gt-1,//是否iPadweA:u.idexOf('Safari')==-1,//是否we应该程序,没有头部与底部weixi:u.idexOf('MicroMeeger')gt-1,//是否微信(2015-01-22新增)qq:u.match(/\\QQ/i)==quotqqquot//是否QQ}}(),laguage:(avigator.rowerLaguage||avigator.laguage).toLowerCae()}不同的浏览器设备显示不同内容//判断是否adroid浏览器if(rower.verio.adroidamam!rower.verio.weixi){alert(这是adroid浏览器)}//判断是否io浏览器if(rower.verio.ioamam!rower.verio.weixi){alert(这是IOS浏览器)}//判断是否微信浏览器if(rower.verio.weixi){alert(这是微信浏览器)}if(!rower.verio.adroidamam!rower.verio.ioamam!rower.verio.weixi){alert(非adriod和io以及微信浏览器)}
加载中...
1、鼠标移进网页里,不见了{curor:oe!imortat}2、简单的文字模糊效果{color:traarettext-hadow:#111005x}3、多重边框.div{ox-hadow:0006xrga(0,0,0,0.2),00012xrga(0,0,0,0.2),00018xrga(0,0,0,0.2),00024xrga(0,0,0,0.2)height:200xmargi:50xautowidth:400x}4、实时编辑CSSlttyletyle=quotdilay:lockquotcotetEditalegtody{color:lue}lt/tylegt5、CSS中简单运算.div{width:calc(100%-500x)}6、order-radiu因为基本上很多人都是这么用的:.div{order-radiu:4x}稍微高端一点的是这样的:.div{order-radiu:4x6x6x4x}然而,终极黑科技是这样用的:.div{order-radiu:5x5x3x2x/5x5x1x3x}order-radiu它可以赋8个值:斜线前面的影响的是水平方向,斜线后面影响的是垂直方向,各个数字就分别代表四个不一样的方向。7、outlie-offetiut{outlie:oe}iut:focu{outlie:oe}这就是将iut输入框去掉默认的蓝线框的方法。CSS中还有一个outlie-offet属性,在这个属性中,你可以设置默认线框的距离:iut{outlie-offet:4x}