ES6基础(2)
1 、ECMAScript6中字符串的用法
1.1 模版字符串
什么是标签模板?这里的模板指的是上面讲的字符串模板,用反引号定义的字符串;而标签,则指的是一个函数,一个专门处理模板字符串的函数。
以上代码有两处要仔细讲解的,首先是tagFn函数,是我们自定义的一个函数,它有三个参数分别是arr,v1,v2。函数tagFn的调用方式跟以往的不太一样,以往我们使用括号( )表示函数调用执行,这一次我们在函数名后面直接加上一个模板字符串,如下面的代码:
这样就是标签模板,你可以理解为标签函数+模板字符串,这是一种新的语法规范。
接下来我们继续看函数的3个参数,从代码的打印结果我们看到它们运行后对应的结果,arr的值是一个数组:[ "他叫" , ",身高" , "米。" ],而v1的值是变量name的值:“张三”,v2的值是变量height的值:1.8。
你是否看出规律了:第一个参数arr是数组类型,它是内容是模板字符串中除了${ }以外的其他字符,按顺序组成了数组的内容,所以arr的值是[ "他叫", ",身高" , "米。" ];第2,3个参数则是模板字符串中对应次序的变量name和height的值。
标签模板是ES6给我们带来的一种新语法,它常用来实现过滤用户的非法输入和多语言转换,这里不展开讲解。因为一旦我们掌握了标签模板的用法后,以后就可以好好利用它的这个特性,再根据自己的需求要来实现各种功能了。
1.2 repeat函数
repeat( )函数:将目标字符串重复N次,返回一个新的字符串,不影响目标字符串。
重复3次后返回一个新字符串赋值给name2,name1不受影响,所以name1的值不变。
1.3 includes函数
includes( )函数:判断字符串中是否含有指定的子字符串,返回true表示含有和false表示未含有。第二个参数选填,表示开始搜索的位置。
传统的做法我们可以借助indexOf( )函数来实现,如果含有指定的字符串,indexOf( )函数就会子字符串首次出现的位置,不含有,则返回-1。我们通过返回值是否为-1来判断字符串中是否含有指定的子字符串,但是,我们现在可以用includes( )函数代替indexOf( )函数,因为它的返回值更直观(true或false),况且我们并不关心子字符串出现的位置。
注意,上面最后一句代码,第二个参数为1,表示从第2个字符“端“开始搜索,第一个字符”前“的位置是0。
1.4 startsWith函数
startsWith( )函数:判断指定的子字符串是否出现在目标字符串的开头位置,第二个参数选填,表示开始搜索的位置。
我们如果判断字符串是否以某个子字符串开头,就可以直接使用startsWith( )函数即可,同样,第二个参数为1表示从第2个字符开始搜索。若要从第一个字符开始搜索,参数应该为0或者为空(默认从第一个字符开始搜索)。
1.5 endsWith函数
endsWith( )函数:判断子字符串是否出现在目标字符串的尾部位置,第二个参数选填,表示针对前N个字符。
1.6 string.raw函数
最后讲解的一个函数是String.raw( );看函数名raw是未加工的的意思,正如这个函数的作用一样:返回字符串最原始的样貌,即使字符串中含有转义符,它都视而不见,直接输出。举个例子: 未经String.raw( )处理的字符串:
\n会被识别为换行符,实现换行效果,而经过String.raw( )的同一个字符串的结果是:
\n被识别为\和n两个字符,失去换行的效果,直接输出,这就是String.raw( )的功能。它常用来作为一个模板字符串的处理函数,也就是直接在后面加一个模板字符串。
ES6中字符串方法: 模版字符串 repeat() includes() startsWidth() endsWidth() String.raw+模版字符串
2 、ECMAScript6中数值的用法
2.1传统写法
在ES5中,我们存在几个全局函数 isNaN函数,isFinite函数,parseInt函数,parseFloat函数等,对于这些全局函数的使用很简单,就拿isNaN函数来举例。
ES5中的写法是:
以上两种写法均可,isNaN是全局函数,本身就是属于window对象下的一个方法,所以大部分人会使用第一种写法。
但是在ES6的标准中,isNaN方法被移植到了Number对象上,也就是原本属于全局对象window下的函数,现在属于Number对象上了,同样被处理的函数还有isFinite函数,parseInt函数,parseFloat函数。
被移植后的函数使用方式是这样的:
在使用之前必须指明它是Number对象下的函数,否则会被默认为window下的函数。
2.2 Number.isNaN函数
Number.isNaN函数:用于判断传入的是否是非数值,注意:是判断非数值,而不是判断数值,IsNaN的全称是: is not a number。
由于2.5是一个number类型的数值,所以返回false(再次注意:判断是非数值,所以是false,表示2.5是一个数值类型的值)。
那么,移植到Number对象isNaN函数和原本是全局函数的isNaN函数,有不一样的地方吗? 传统的isNaN函数会把非数值的参数转化成数值再进行判断,而Number. isNaN只对数值类型有效,非数值类型的参数一律返回false。
Number下面的isNaN都懒得给字符串’abc’做类型转换,直接返回false。而ES5中的isNaN函数会对字符串’abc’进行类型转换后,发现它是一个NaN(非数值),才返回true。
所以我们在使用这个函数到时候还要小心,当返回false的时候,不一定就是一个数值,有可能是一个非数值类型的参数。
总结:挂在到Number上之后,传入的值是数字或者字符串返回结果都为false。 Infinity 无穷
2.3 Number.isFinite函数
Number.isFinite函数:用来检查一个数值是否非无穷。注意是判断非无穷,不是判断无穷,这里跟isNaN函数一样,有点绕。
注意第二行代码的参数:Infinity,Infinity是window对象下的一个常量,表示一个无穷数。所以第二行代码会返回false。此外,isFinite函数跟isNaN函数一样,也只是对数值类型有效,对非数值类型的参数一律返回false。
所以同样要注意,当Number.isFinite函数返回false的时候,参数不一定就是一个有穷的数值类型,也有可能是一个非数值类型的参数。如:字符串’abc’。
总结: isFinete 验证数字的,只有infinity是无穷,其他的数字都是有穷的,如果是字符串,则返回false
2.4 Number.parseInt函数
parseInt函数:解析一个字符串,返回一个整数。parseInt函数同样是从window对象下移植到Number对象下,但是它的作用没有任何变化。
以上函数都是在window对象下,移植到了Number对象下,这么做的目的是慢慢地减少全局性的函数,把全局函数合理地规划到其他对象下,渐渐实现语言的模块化。
2.5 Number.isInteger函数
Number.isInteger函数:用来判断是否是整数。
上面的运行结果也如我们所料,数值3.2不是整数,返回false。不过有一点要注意:在javascript内部对整数和浮点数采用一样的存储方式,因此小数点后如果都是0的浮点数,都会被认为是整数。看个例子就知道了:
数值3.0和3.00都会被认为是整数。
总结:isInteger 用来判断是否是整数,小数点后面均为0,则认为是整数
2.6 Math.trunc函数
Math.trunc函数:用于去除一个数的小数部分,返回整数部分。
Math.trunc函数:用于去除一个数的小数部分,返回整数部分。
运行结果也很好理解,如果传入的参数是整数,就直接返回整数,如果是小数,就去除了小数部分,返回整数部分:3。
2.7 Math.sign函数
Math.sign函数:用来判断一个数到底是正数、负数、还是零。
返回的结果类型有点多,我们分别来讲解一下,参数如果是正数,结果返回1;如果是负数,结果返回-1;如果是0,结果返回0;如果是一个非数值类型的参数,结果返回:NaN
3 、ECMAScript6中数组的用法
3.1 Array.of函数
函数作用:将一组值,转换成数组。
是不是很好理解,传进去的数字是:1~5,最后经过Array.of函数处理,得到了一个数组,而数组的内容就是[1,2,3,4,5]。
3.2 Array.from函数
函数作用:可以将类似数组的对象或者可遍历的对象转换成真正的数组。
有哪些是类似数组的对象?最常见的就是调用getElementsByTagName方法得到的结果,它就是一个类似数组的结果,getElementsByTagName方法一定不陌生吧,我们来看一下:
看上面的代码,我们调用getElementsByTagName方法,得到结果存到变量ele中,然后对其进行类型判断,发现变量ele并不是一个数组,而是一个对象Array,一个类似数组的对象Object,接下来我们用Array.from( )对其进行处理,并再一次进行类型判断。
这个时候我们运行的结果是:true,也就是经过Array.from函数处理返回的结果,已经变成了一个真正的数组。
Array.from函数其中一个用处就是将字符串转换成数组。看下面的案例:
字符串“hello”被转换后的返回的结果已经变成了一个数组:["h", "e", "l", "l","o"]。
注意:Array.from() 可以放两个参数,第一个是要转变的对象,但是此对象必须要有长度,第二个参数是回掉函数,是指完成前面的转换后做什么事情。
3.3 find函数
函数作用:找出数组中符合条件的第一个元素。
函数作用:找出数组中符合条件的第一个元素。
看上面的代码,find()函数的参数是一个匿名函数,数组的每个元素都会进入匿名函数执行,直到结果为true,find函数就会返回value的值:3。倘若所有元素都不符合匿名函数的条件,find( )函数就会返回undefind。看下面的代码案例:
上面的案例中,数组中没有存在大于7的元素,所以find函数就会返回:undefined。
3.4 findIndex函数
函数作用:返回符合条件的第一个数组成员的位置。
上面的代码结果是:2,因为数组元素中大于8的元素是9,而元素9的位置正是2,(数组元素是从0算起)。倘若所有元素都不符合匿名函数的条件,findIndex( )函数就会返回-1。
3.5 fill函数
函数作用:用指定的值,填充到数组。
经过fill( )函数处理后的数组arr已经变成了[4,4,4];正如函数名fill(填充)一样。所有元素都被填充为数字4了。如果我想只填充部分元素可不可以呢?可以的,fill( )函数提供了一些参数,用于指定填充的起始位置和结束位置。
还是上面的案例,我们稍做调整,再演示一下:
上面的代码中第2个参数和第3个参数的意思是:从位置1的元素开始填充数字4,截止到位置3之前,所以是位置1和位置2的元素被数字4填充了,得到的结果:[1,4,4]。
总结:fill(替换内容,开始索引,结束索引但是不包括该索引内容)
3.6 entries函数
函数作用:对数组的键值对进行遍历,返回一个遍历器,可以用for..of对其进行遍历。
遍历器和for..of也是ES6的新增特性,目前我们还没有介绍到,我们可以先进行一下简单地认知。
回到我们的entries( )函数,看个案例:
函数作用:对数组的键值对进行遍历,返回一个遍历器,可以用for..of对其进行遍历。
遍历器和for..of也是ES6的新增特性,目前我们还没有介绍到,我们可以先进行一下简单地认知。
回到我们的entries( )函数,看个案例:
上面的代码中,我们将entries( )函数返回的一个遍历器,用for...of进行遍历,并打印出结果,能得到数组的键值:0和1,以及对应的数组元素:‘a‘和’b‘。
如果只想遍历数组的索引键的话,可以使用另一个实例方法。
3.7 keys函数
函数作用:对数组的索引键进行遍历,返回一个遍历器。
正如上面的运行结果一样,我们打印出了数组的索引键:0和1。
如果我们想只对数组的元素进行遍历,我们可以使用两一个实例方法。
3.8 values函数
作用:对数组的元素进行遍历,返回一个遍历器。
上面的代码运行结果也是我们预料之中的,最终打印出了数组的元素:a和b。
除了新增函数以外,ES6还为数组带来了一个新的概念:数组推导。
3.9 数组的推倒
数组推导:用简洁的写法,直接通过现有的数组生成新数组。
举个例子:加入我们有一个数组,我在这个数组的基础上,每个元素乘以2,得到一个新数组。我们看看传统的实现方法:
传统的方式,除了以上的方式,我们还可以用forEach方法实现。但我们现在介绍的主要是利用ES6提供的新写法:数组推导。
我们同样可以得到数组arr2,它的值和上一段代码的一样。并且代码量大大地减少了。这就是数组推导给我们带来的便利。 在数组推导中,for..of后面还可以加上if语句,我们来看看代码案例:
在上面代码中,我们不再是对数组的元素乘以2,而是用if做了个判断:数组的元素要大于3。所以运行的结果是:[ 4 ]。
4 、ECMAScript6中对象的使用
4.1 对象的传统写法
传统表示法:
上面的案例很简单,变量person就是一个对象,对象含有name属性和一个say方法。表示法是用键值对的形式来表示,这就是传统的表示法。
ES6中的简洁写法:
咱们来讲解一下上面这个案例,首先定义两个变量name和age,分别用传统的写法和ES6中的写法,把变量作为person对象的属性值。第一种写法大家很熟悉,使用键值对的表示法,而ES6中的新写法只是简单地用两个变量名即可,而得到的结果跟传统的写法一样。
这也就是新的写法更简捷了,变量名可以作为键值对的键(变量名name和age),而变量的名作为值(变量的值Zhangsan和12),最后一起组成了person对象的内容{name:”Zhangsan”,age:12}。
对象的属性可以这样简写,那么对象的方法表示呢?
我们来讲解方法的简写表示法:
通过上面的案例,可以看出两种写法的区别,不管是属性还是方法,确实ES6给我们带来的表示法更加简捷,代码量更少。
4.2 ECMAScript6中属性名的更新
用字面量定义一个对象的时候,可以用表达式作为对象的属性名或者方法名。
注意上面person对象的定义,其中属性名和方法名都是用中括号[ ]包裹着,里面都是一个字符串相加的表达式,这就告诉我们,用字面量(大括号{ })定义对象的时候,属性名和方法名可以是一个表达式,表达式的运算结果就是属性名或者方法名。这点改进会使得对象在实际开发中的使用变得更加的灵活方便。
4.3 Object.is函数
函数的作用:比较两个值是否严格相等,或者说全等。
严格相等和抽象相等:
可以看到,抽象相等用“==”表示,严格相等用“===”表示,进行严格相等判断的时候,首先要求类型是一样的,否则会直接返回false。
所以上面的例子中,变量str和变量num一个是字符串类型,一个是数字类型,所以结果是false,而抽象相等会对类型不一致的两个变量进行类型转化,转成同一类型再进行判断。“12”转换成数字类型得到12,再进行比较后得到的结果是true。
上述的执行结果跟我们预期的一样,参数类型不一样str和num进行比较,得到的结果是false。变量类型和值都一样的num和num2进行比较,得到的结果是true。
4.4 Object.assign函数
函数作用:将源对象的属性赋值到目标对象上。(assign [əˈsaɪn] :分派、指派、委派)这么讲肯定是有点抽象的,咱们用案例说话,更直观更形象:
注意输出的结果,target对象已经不是{ a:1 }了,而是变成了{a: 1, b: 2, c: 3},经过Object.assign( )函数的处理,源对象的属性被添加到了target对象上。这就是Object.assign( )函数的作用。
此外,Object.assign( )函数的参数还可以是多个(至少是两个)。我们在上面的案例稍做修改,加一个参数:
我们从最后打印出来的结果可以看出,对象origin1和对象origin2的属性都被添加赋值到了对象target上。也就是Object.assign( )函数参数中的源对象可以是一个或者一个以上,而目标对象只有一个。
那么,如果赋值过程中,对象的属性出现了相同的名字怎么办?如果这样,后面的属性值就会覆盖前面的属性值。还是上面的案例稍做修改,看代码:
每个对象属性都含有属性a,它的值从1到最后变成了3,也就是Object.assign()函数处理的过程中,会把最后出现的属性覆盖前面的同名属性。
巧妙利用Object.assign( )函数的功能,我们可以完成很多效果,比如:给对象添加属性和方法,克隆对象,合并多个对象,为对象的属性指定默认值。
4.5 Object.getPrototypeOf函数
函数作用:获取一个对象的prototype属性。这里的对象我们用一个自定义类实例出来的对象来演示。
这个案例代码有点长,但是为了大家能看懂,我把注释写得比较详细,前面部分都是关于面向对象的实现。把函数Person用new关键字调用,这个时候函数Person就相当于构造函数或者说是一个类,实例化后是一个对象,这个对象会继承Person类的prototype的属性和方法。上述例子中,也就是对象allen继承了一个say方法,可以直接调用。
如果你想看看prototype中还有哪些方法和属性,那么,你就可以使用Object.getPrototypeOf( )函数来获取,参数就是allen对象,最后的结果也如我们所料,确实打印出了我们刚开始定义好的内容:一个对象,含有一个say方法{say:function(){.....}}。
那么,如果我想为这个对象修改prototype的内容,要怎么办?这个时候,我们可以用ES6给我们的另一个方法。
4.6 Object.setPrototypeOf函数
函数作用:设置一个对象的prototype属性。
上面的代码,我们使用Object.setPrototypeOf()函数对对象的prototype属性进行了修改,具体的修改是重写了say方法。在修改前,我们曾经调用过一次say( )方法,得到的结果是打印hello,修改之后我们再一次调用allen.say( );得到的结果是打印出hi,说明我们修改成功了。
最后两个函数Object.getPrototypeOf()和Object.setPrototypeOf()的案例代码有点长,涉及到了javascript的面向对象内容。新手如果没有接触过面向对象的话,读起来一定会懵逼,这里有必要拓展一下javascript的面向对象。
4.7 Javascript中面向对象
Javascript本身不是一种面向对象的编程语言,在ES5中,它的语法中也没有class(类的关键字),但是,开发者可以利用对象的原型prototype属性来模拟面向对象进行编程开发。
上面的案例告诉我们,模拟面向对象编程有几个关键步骤:1、构造函数;2、给prototype对象添加属性和方法;3、实例化;4、通过实例化后的对象调用类的方法或者属性。
注意:面向对象是一种编程思想,并不是具体的工具。
5 、ECMAScript6中函数的使用
5.1 参数的默认值
传统方式:
上面是传统的实现方式,通过或运算实现,原理:如果运算符 || 左侧为true,直接返回左侧的值,否则返回右侧的值; 在person函数内,如果参数n没有传参,那么变量name得到的值就是“Zhangsan”,如果传参了,变量name的值就为参数n的值。
但是,前提是参数对应的布尔值不能false(比如:数字0,空字符串等转换成布尔值就是false),这就使得这种传统的实现方式存在一定的不足和缺陷。
ES6的写法:
看,我们把默认值的设定放在了参数上:(name = 'Zhangsan',age = 25),这样就实现了参数name的默认值为‘Zhangsan’,age的默认值为25。而不需要在函数体内进行检测,函数体内可以专注对参数的使用或者运算,再也不用担心函数的实际传参情况了。
上面的案例,我们对person( )函数进行两次调用,区别是有传参数和没传参数,运行的结果也符合我们的预期:没传参,得到的是默认值Zhangsan 25,传参就会得到传入的参数值:Lisi 18。
但是,多留个心眼,凡事总会有但是,如果函数有多个参数,但只有部分需要指定默认值,另一部分不需要的话,那么,设定默认值的参数一定要放在最后。看案例:
上面的person函数,两个参数name和age,其中只有age需要指定默认值,name不需要,那么,age的排序就必须放在最后,name放在前面。也就是有默认值的参数后面不能再跟不需默认值的参数了。
另外,只有当传入的参数为undefined,才会触发默认值赋值。否则,哪怕你传的参数值为0,false,null都不会触发默认值赋值,这就完美的解决了传统实现方式的弊端,试试看:
看person( )函数的4次调用和结果,只有不传或者传入undefined的时候才会触发默认值赋值,得到12,传入数字0或者null都不会触发默认值赋值。
还有一个要注意的地方,函数的参数是默认声明的,声明过的变量,就不能用let或者const关键字再次声明,否则会报错的,不信给你看一个案例:
上面这种情况,函数被调用后,就会报错,会提示你age已经被声明过了,你别再声明它了。
// 默认值如果传递的是对象,则只有undefined,NaN和空可以触发默认值 其他情况传入值就是实际值
// 默认值如果传递的是变量,则只有undefined,空可以触发默认值 其他情况传入值就是实际值
5.2 rest函数
rest参数,这是一个新的概念,rest的中文意思是:剩下的部分。
rest参数,这是一个新的概念,rest的中文意思是:剩下的部分。
上面的代码注释把每一步都写得很详细,首先,我们会看到一个陌生的语法:...values;咦?这是什么鬼?这是一种新的写法,也就是我们要介绍的rest参数,它代表的意思是:在实参中,除了第一个参数以外,剩余的参数都会被...values获取到。
在上面的案例中:sum(res,1,2,3,4),也就是除去实参 res 以外的参数,它们是1,2,3,4一共4个参数。它们全被...values收入囊中,接着,我们打印了values,看到的结果是一个数组:[1,2,3,4],也就是这4个参数被装在了一个数组中,我们想要使用这4个参数的话,就可以用数组的方法来对他们进行处理,所以我们用了forEach方法对它们进行了循环,并求和,把求和结果存储到了变量res中,最后我们打印出结果,得到了数字10,也就是数字1,2,3,4的求和结果。
(上面这段有点长,耐心读完了吗?读不懂,可以拉上去再阅读一遍上面的代码案例)
上面的案例主要是介绍了rest参数的用法,首先是表示法:...values(三个点+变量名);其次,values是一个数组;我们要学会这两点即可。
要注意的是,rest参数必须是函数的最后一个参数,后面不能再跟其他参数,什么意思呢?看案例:
上面有错误和正确的两种表示,在注释上有说明原因。总之rest参数后面不能再跟有其他参数。
rest参数并不是适合所有的函数使用,只在特定的情境下会比较适用,比方说刚刚案例中的求和函数,就十分适合要rest参数把实际的参数收集起来放在数组中进行求和,在适当的情况下使用它可以事半功倍,减少代码量,提高开发效率。
5.3 扩展运算符
上面讲到的rest参数,它的表示法使用...(三个点),它除了用在rest参数中,还有其他用途,我们称这种表示法为扩展运算符,那么,它还有什么作用呢?
它一般结合数组使用,把数组的元素用逗号分隔开来,组成一个序列。我们看一下实际案例:
上面讲到的rest参数,它的表示法使用...(三个点),它除了用在rest参数中,还有其他用途,我们称这种表示法为扩展运算符,那么,它还有什么作用呢?
它一般结合数组使用,把数组的元素用逗号分隔开来,组成一个序列。我们看一下实际案例:
上面的sum( )函数是简单的将两个数字相加求和,参数是两个普通的参数x,y,并不是什么高逼格的rest参数,所以我们运用了扩展运算符...将数组[ 2,3 ] 转成了 2,3 两个普通的数列,再传进sum( )方法,对应上x,y两个参数。实际上,sum( ...arr ) 的效果相当于sum( 2,3 ) 。
从这个案例我们就可以理解扩展运算符...的作用了,它可以将一个数组转成一个对应的参数数列。在实际开发中,你可以根据扩展运算符的作用,可以灵活运用,实现各种效果。
5.4 箭头函数
箭头函数,这又是一个新概念,ES6给我们介绍一种全新的定义函数的方式,就是用箭头符号(=>),故得名为箭头函数。具体怎么用,我们来看案例:
箭头函数,这又是一个新概念,ES6给我们介绍一种全新的定义函数的方式,就是用箭头符号(=>),故得名为箭头函数。具体怎么用,我们来看案例:
什么?第二种写法这么简短.....不敢信!!
上面演示了两种写法,函数的作用都是一样的,传入参数a,直接返回a;第一种传统的写法大家都熟悉,我们看看第二种写法:a=>a; 这里的第一个a代表是传进去的参数,箭头=>后面的a表示函数体;也许大家跟我一样,很不习惯,但这种写法确实简洁了很多。
看到这里,有开发经验的“老司机”可能会问:如果传入的参数不止一个,或者函数体不是简单的返回a,需要做一些其他的运算,含有多条语句的话,怎么办?
问得好,对于这种情况,我们又有另一种处理办法。举个例子,假如我们给函数传入2个参数,然后进行相加运算,我们用箭头函数来实现:
注意上面的参数和函数体部分,如果参数超过1个的话,需要用小括号()括起来,函数体语句超过1条的时候,需要用大括号{ }括起来。
箭头函数的最大作用就是简化函数的实现,大大地减少代码量。来举个例子对比一下,假设我们现在要对一个数组 [ 1,2,3,4 ] 里面的数求和,我们分别用传统的方式和箭头函数都实现一次,大家看看差别:
箭头函数的最大作用就是简化函数的实现,大大地减少代码量。来举个例子对比一下,假设我们现在要对一个数组 [ 1,2,3,4 ] 里面的数求和,我们分别用传统的方式和箭头函数都实现一次,大家看看差别:
大家对比一下,用箭头函数代替了传统的匿名函数,确实减少了代码量。