最新最热

【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等等!