1.let和const在JavaScript中,我们以前主要是使用keyvar来定义变量。在ES6之后,增加了两个定义变量的关键字,分别是let和const。对于变量,ES5中var定义的变量会被提升到作用域内所有函数和语句的最前面,而ES6中let定义的变量不会,let声明的变量会在其对应的代码块中创建一个变量,暂时死掉zone直到变量被声明。let和const都可以声明块级作用域。用法与var类似。let的特点是不会提升变量,而是锁在当前块。一个很简单的例子:functiontest(){if(true){console.log(a)//TDZ,俗称临时死区,用来描述变量没有被提升的现象leta=1}}test()//aisnotdefinedfunctiontest(){if(true){leta=1}console.log(a)}test()//aisnotdefined唯一正确的使用方式:先声明,再访问.functiontest(){if(true){leta=1console.log(a)}}test()//1const声明一个常量,一旦声明就不能改变,必须对常量进行初始化赋值。const虽然是常量,默认赋值是不允许修改的,但是如果定义了对象Object,那么就可以修改对象内部的属性值。consttype={a:1}type.a=2//允许修改type.a的属性值,而不是直接修改type的值。console.log(type)//{a:2}const和let的相同点和不同点是一样的:const和let在当前块内有效,在块外执行时会被销毁,没有变量提升(TDZ),声明不能重复。区别:const不能再赋值,let声明的变量可以重复赋值。const实际上保证的不是变量的值不能改变,而是变量指向的内存地址中存储的数据不能改变。对于简单类型的数据(数字、字符串、布尔值),值存储在变量指向的内存地址,因此相当于一个常量。但是对于复合类型的数据(主要是对象和数组),变量指向的内存地址只是一个指向实际数据的指针,const只能保证指针是固定的(即总是指向另一个固定的address),至于它指向的数据结构是不是可变的,就完全没法控制了。因此,在将对象声明为常量时必须非常小心。块级作用域的使用场景除了上面提到的常见声明方式外,我们还可以在循环中使用。最著名的面试题:循环中timerclosure的测试题在for循环中使用var声明的循环变量,会跳出循环体污染当前函数。for(vari=0;i<5;i++){setTimeout(()=>{console.log(i)//5,5,5,5,5},0)}console.log(i)//5i跳出循环体,污染外部函数//将var改成letfor(leti=0;i<5;i++){setTimeout(()=>{console.log(i)//0,1,2,3,4},0)}console.log(i)//i未定义i不能污染外部函数在实际开发中,我们选择使用var、let还是const,取决于我们的变量是否需要更新,通常我们要保证变量不被恶意修改,大量使用const。使用const声明。声明对象时,也推荐使用const。当需要修改声明变量的值时,使用let。可以使用let而不是可以使用var的场景。symbolES6之前,我们知道五种基本数据类型是Undefined、Null、Boolean、Number、String,然后加上一个引用类型Object组成了JavaScript中所有的数据类型,但是ES6出来之后,一个新的数据类型是added类型,namedsymbol,顾名思义,就是唯一的意思,也就是说每一个Symbol类型都是唯一的,不与其他Symbol重复。可以通过调用Symbol()方法创建类型为Symbol的新值,该值是唯一的,不等于任何值。varmySymbol=Symbol();console.log(typeofmySymbol)//"symbol"2.stringES6stringUTF-16codepoint的新方法:ES6强制使用UTF-16字符串编码。UTF-16的解释请自行百度了解。codePointAt():该方法支持UTF-16,接受代码单元的位置而不是字符串位置作为参数,返回字符串中给定位置对应的代码点,即整数值。String.fromCodePoiont():功能与codePointAt相反,获取字符串中某个字符的码位,也可以根据指定的码位生成一个字符。normalize():提供Unicode的标准形式,接受一个可选的字符串参数,表示应该应用某种Unicode标准形式。在ES6中,添加了3个新方法。每个方法接收2个参数,待检测的子串和开始匹配的索引位置。模板字符串String是JavaScript中的基本类型之一。应该算是除了objects之外使用频率最高的类型了。String包含substr、replace、indexOf、slice等很多方法。ES6引入了模板字符String特性,用反引号表示,可以表示多行字符串,可以做文本插值(使用模板占位符)。//前面的多行字符串我们是这样写的:console.log("helloworld1\n\hellocala");//"helloworld//hellocala"//有了模板字符串后console.log(`helloworldstringtextline2`);//"helloworld//hellocala"可以用${}表示模板占位符,可以把自己定义的变量传入括号里,例如:varname="cala";varage=22;console.log(`hello,I'am${name},myageis${age}`)//hello,I'mcala,myageis22includes(str,index):如果在字符串中检测到指定的文本,则返回true,否则返回false。lett='abcdefg'if(t.includes('cde')){console.log(2)}//truestartsWith(str,index):如果在字符串开头检测到指定文本,则返回true,否则返回假。lett='abcdefg'if(t.startsWith('ab')){console.log(2)}//trueendsWith(str,index):如果在字符串末尾检测到指定文本,则返回true,否则返回假。lett='abcdefg'if(t.endsWith('fg')){console.log(2)}//true如果只是需要匹配字符串中是否包含某个子串,建议使用新方法,如果需要查找匹配字符串的位置,使用indexOf()。3.函数函数的默认参数在ES5中,我们给函数传递参数,然后在函数体中设置默认值,如下。函数a(num,回调){num=num||6回调=回调||function(data){console.log('ES5:',data)}callback(num*num)}a()//ES5:36.不传参输出默认值//也可以用callbacka(10,function(data){console.log(data*10)//1000,传参输出新值})在ES6中,我们用new默认值写成functiona(num=6,callback=function(data){console.log('ES6:',data)}){callback(num*num)}a()//ES6:36,不传参数输出默认值a(10,function(data){console.log(data*10)//1000,传参输出新值})4.箭头函数(=>)(箭头函数比较重要,现在简单提一下,以后有时间再写一篇箭头函数的文章。)constarr=[5,10]consts=arr.reduce((sum,item)=>sum+item)console.log(s)//this在15箭头函数中的使用与普通函数不同,在在JavaScript的普通函数中,都会有一个自己的this值,主要分为:普通函数:1.函数作为全局函数被调用时,this指向全局对象2.函数被调用时作为对象中的一个方法,this指向对象3.当函数作为构造函数时,this指向构造函数new创建的新对象4.也可以通过call,apply,and改变this的指向绑定。箭头函数:1、箭头函数没有this,函数内部的this是从父级到最近的非箭头函数,不能改变this指向的。2.箭头函数没有super3,箭头函数没有arguments4,箭头函数没有new.target绑定。5、new6不能用,没有原型。7.不支持重复的命名参数。箭头函数简单理解1、箭头函数左边代表输入参数,右边代表输出结果。consts=a=>aconsole.log(s(2))//22.在箭头函数中,this属于词法作用域,直接由上下文决定。对于普通函数中的this,在箭头函数中处理无疑更简单,如下://ES5普通函数functionMan(){this.age=22;返回函数(){this.age+1;}}varcala=newMan();console.log(cala())//undefined//ES6箭头函数functionMan(){this.age=22;return()=>this.age+1;}varcala=newMan();console.log(cala())//233,箭头函数中没有参数(我们可以用rest参数代替),有没有原型,不能使用new关键字,例如://noargumentsvarfoo=(a,b)=>{returnarguments[0]*arguments[1]}console.log(foo(3,5))//参数没有定义//没有原型varObj=()=>{};console.log(Obj.prototype);//undefined//不能使用new关键字varObj=()=>{"helloworld"};varo=newObj();//TypeError:Objisnotaconstructor4,arrowfunctiontosortthearrayconstarr=[10,50,30,40,20]consts=arr.sort((a,b)=>a-b)console.log(s)//[10,20,30,40,50]尾调用优化是指函数返回时,调用一个新的函数。由于尾调用的实现需要保存在内存中,在一个循环体中,如果函数有尾调用,你的内存可能会满或者溢出。在ES6中,引擎会帮你优化尾调用。不需要自己优化,但是需要满足以下三个要求:1.函数不是闭包。2.尾调用是函数的最后一条语句。3.尾调用的结果作为函数返回尾调用的实际使用——递归函数优化在ES5时代,我们不推荐使用递归,因为递归会影响性能。但是通过尾调用优化,递归函数的性能得到了提升。//新的尾部优化写法“usestrict”;functiona(n,p=1){if(n<=1){return1*p}lets=n*preturna(n-1,s)}//求1x2x3的阶乘letsum=a(3)console.log(sum)//6五、ES6对象新方法Object.assign()Object.assign()方法用于给所有可枚举的Copies赋值一个或多个源的属性值对象到目标对象。它将返回目标对象。Object.assign方法只是将源对象本身的可枚举属性复制到目标对象。该方法使用了源对象的[[Get]]和目标对象的[[Set]],所以调用了相关的getter和setter。因此,它分配属性,而不仅仅是复制或定义新属性。如果合并的源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括它们的可枚举性)复制到原型,应该使用Object.getOwnPropertyDescriptor()和Object.defineProperty()。String和Symbol类型的属性将被复制。合并对象varo1={a:1};varo2={b:2};varo3={c:3};varobj=Object.assign(o1,o2,o3);控制台日志(对象);//{a:1,b:2,c:3}console.log(o1);//{a:1,b:2,c:3},注意目标对象本身也会发生变化。合并具有相同属性的对象varo1={a:1,b:1,c:1};varo2={b:2,c:2};varo3={c:3};变种对象=对象。分配({},o1,o2,o3);控制台日志(对象);//{a:1,b:2,c:3}六、Map与SetMap和Set都叫集合,但也有区别。Set常用于检查对象中是否存在key,Map集合常用于获取存储的信息。Set是一个有序列表,其中包含相互独立的唯一值。与Array和Set相比,两者都是存储多个值的容器。两者可以相互转化,只是在使用场景上有所区别。如下:Array的indexOf方法效率不如Set的has方法。Set不包含重复值(此功能可用于对数组进行去重)。Set通过delete方法删除一个值,而Array只能使用splice。两者使用起来都比较方便。前者更好。Array有很多Set没有的map、filter、some、every等新方法(但可以相互转换使用)。与Object和Map相比,Object是string-value,Map是Value-valueObjectkey是string类型,Mapkey是any类型手动计算Object大小,Map.size可以得到sizeMap排序是插入顺序Object有原型,因此地图中有一些默认键。可以理解为Map=Object.create(null)Set操作setletset=newSet()//Set转化为数组letarr=Array.from(set)letarr=[...set]//实例属性(继承自Set)set.constructor===Setset.size//操作方法set.add(1)//添加一个值set.delete(1)//删除一个值set.has(1)//判断是否有这个值(Array中的indexOf)set.clear()//清除所有值//获取成员方法进行遍历(Set的遍历顺序为插入顺序)set.keys()//返回键名的遍历器set.values()//返回键值遍历器set.entries()//返回键值对遍历器set.forEach()//循环遍历每个值(与Array的方法一致)for(letkeyofset.keys()){}for(letvalofset.values()){}for(letentryofset.entries()){}//使用数组方式处理setvalueset=新集(arr)集=新集([...集]。地图((x)=>x=x*2))集=新集([...集]。过滤器((x)=>x>2))Mapmethodsetletmap=newMap()//实例属性(继承自Map)map.constructor===Mapmap.size//操作方法map.set(1,2)map.get(1)map.delete(1)map.has(1)map.clear()//遍历方法map.keys()map.values()map.entries()map.forEach()//映射与数组转换map=newMap([['key','val'],[2,1]])//需要一个二元数组letarr=[...map]//值得注意的是Map的key是绑定到内存的映射。set([1],'s')map.get([1])letarr=[1]letarr1=[1]map.set(arr,'s')map.get(arr)map.set(arr1,'s')map.get(arr1)Set和Map的深入理解见《深入理解:ES6中的Set和Map数据结构,Map与其它数据结构的互相转换》7.迭代器(Iterator)1.entries()returniterator:returnkey-valuepair//arrayconstarr=['a','b','c'];for(letvofarr.entries()){console.log(v)}//[0,'a'][1,'b'][2,'c']//Setconstarr=newSet(['a','b','c']);for(letvofarr.entries()){console.log(v)}//['a','a']['b','b']['c','c']//Mapconstarr=newMap();arr.set('a','a');arr.set('b','b');for(letvofarr.entries()){console.log(v)}//['a','a']['b','b']2.values()返回一个迭代器:返回值ofthekey-valuepair//arrayconstarr=['a','b','c'];for(letvofarr.values()){console.log(v)}//'a''b''c'//setconstarr=newSet(['a','b','c']);for(letvofarr.values()){console.log(v)}//'a''b''c'//Mapconstarr=newMap();arr.set('a','a');arr.set('b','b');for(letvofarr.values()){console.log(v)}//'a''b'3、keys()返回迭代器:返回键//键值对数组constarr=['a','b','c'];for(letvofarr.keys()){console.log(v)}//012//Setconstarr=newSet(['a','b','c']);for(letvofarr.keys()){console.log(v)}//'a''b''c'//Mapconstarr=newMap();arr.set('a','a');arr.set('b','b');for(letvofarr.keys()){console.log(v)}//'a''b'虽然内置了三个迭代器上面列出了方法,不同的集合类型也有自己的默认迭代器。在forof中,数组和Sets的默认迭代器是values(),Map的默认迭代器是entries()forof循环解构对象本身不支持迭代,但是我们可以自己加一个generator,返回一个key,value迭代器,和然后使用forof循环解构key和value。constobj={a:1,b:2,*[Symbol.iterator](){for(letiinobj){yield[i,obj[i]]}}}for(let[key,value]ofobj){console.log(key,value)}//'a'1,'b'2字符串迭代器conststr='abc';for(letvofstr){console.log(v)}//'a''b''c'ES6给数组增加了几个新的方法:find(),findIndex(),fill(),copyWithin()1,find():传入一个回调函数来查找数组返回第一个匹配当前搜索条件的元素,并终止搜索。constarr=[1,"2",3,3,"2"]console.log(arr.find(n=>typeofn==="number"))//12.findIndex():传入一个回调函数,找到数组中第一个符合当前搜索规则的元素,返回其下标,结束搜索。constarr=[1,"2",3,3,"2"]console.log(arr.findIndex(n=>typeofn==="number"))//03,fill():使用新元素要替换数组中的元素,可以指定替换下标范围。arr.fill(value,start,end)4.c??opyWithin():选择数组的某个下标,从该位置开始复制数组元素,默认从0开始复制。您还可以指定要复制的元素范围。arr.copyWithin(target,start,end)constarr=[1,2,3,4,5]console.log(arr.copyWithin(3))//[1,2,3,1,2]来自下方从标记为3的元素开始,复制数组,所以4,5被替换为1,2constarr1=[1,2,3,4,5]console.log(arr1.copyWithin(3,1))//[1,2,3,2,3]从下标为3的元素开始,复制数组,指定复制的第一个元素的下标为1,所以4,5替换为2,3constarr2=[1,2,3,4,5]console.log(arr2.copyWithin(3,1,2))//[1,2,3,2,5]从下标为3的元素开始,复制array,并指定复制的一个元素的下标为1,结束位置为2,所以4被2ES6中的class类、Promise和异步编程、代理(Proxy)和反射(Reflection)API代替。写详细点。PS:写的太仓促了,难免有错漏。
