字符串的拓展
字符串的遍历器接口
ES6位字符串添加了遍历器接口,使得字符串可以被for...of
循环遍历
includes(),startsWith(),endsWith()
包含,原字符串头部,原字符串尾部
三个方法都支持第二个参数,前两个方法表示开始搜索的位置,而endsWith针对前n个字符
repeat()
repeat
方法返回一个新字符串,表示原字符串重复n次.如果参数是小数,会被向下取整;如果参数是负数或者Infinity
,会报错;但是如果参数介于0到-1之间,取整后等于-0,视同为0;参数NaN等同于0;如果参数是字符串,则先转换成数字.
padStart(),padEnd()
ES2017引入字符串补全长度的功能.如果某个字符串不够指定长度,会在头部或尾部补全.两者分别用于头部和尾部补全
一共接收两个参数,第一个参数用来指定字符串的最小长度,第二个参数用来补全的字符串
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
如果省略第二个参数,默认使用空格补全长度
padStart
的常见用途是为数值补全指定位数和提示字符串格式.
模板字符串
用反引号标识``。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量(${}
,可以放入任意的JavaScript表达式,还能调用函数)。
如果要在模板字符串中使用反引号,前面要用反斜杠转义.
如果使用模板字符串表示多行字符,所有的空格和缩进都会被保留在输出之中.
如果大括号中的值不是字符串,将按照一般的规则转为字符串.
如果模板字符串中的变量没有声明将报错.
模板字符串的大括号内部,就是执行JavaScript
代码
标签模板
模板字符串紧跟在一个函数名后面,该函数将用来被调用来处理这个模板字符串.
标签模板是函数调用的一种特殊形式.''标签''指的就是函数,紧跟在后面的模板字符串就是它的参数,但是如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数.
let a = 5;
let b = 10;
tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);
tag函数第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,变量替换只发生在数组成员之间;其他参数,都是模板字符串各个变量被替换后的值.
"标签模板"一个重要应用就是过滤HTML字符串,防止用户输入恶意内容.
String.raw()
用来充当模板字符串的处理函数,返回一个斜杠都被转义的字符串,对应替换变量后的模板字符串.
如果原字符串的斜杠已经转义,那么String.raw
会进行再次转义
String.raw
可以作为正常的函数使用.这时它的第一个参数应该是一个具有raw
属性的对象,且raw
属性的值应该是一个数组
String.raw({ raw: 'test' }, 0, 1, 2);
// 't0e1s2t'
// 等同于
String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
RegExp构造函数
ES5中,RegExp
构造函数的参数有两种情况:
- 参数是字符串,这时第二个参数表示正则表达式的修饰符
- 参数是一个正则表达式,但是ES5这种情况不允许使用第二个参数添加修饰符;ES6改变了这种行为.而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符
new RegExp(/abc/ig, 'i').flags
// "i"
字符串的正则方法
字符串对象共有4个方法,可以使用正则表达式:match,replace,search,split
数值的拓展
二进制和八进制数值的新的写法,分别用前缀0b
(或0B
)和0o
(或0O
)表示,如果要将0b
和0o
前缀的字符串数值转为十进制,要使用Number
方法
Number新增的方法
Number.isFinite(),Number.isNaN()
与传统方法isFinite(),isNaN()区别在于传统方法先调用Number()将非数值的值转化为数值
Number.parseInt(),Number.parseFloat()
将全局方法移植到Number对象上,模块化
Number.isInterger()
Number.EPSILON
新增一个极小的常量.根据规格,它表示1于大于1的最小浮点数之间的差.Number.EPSILON
可以用来设置"能够接受的误差范围"
安全整数和Number.isSafeInteger()
ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。
Number.isSafeInteger()
则是用来判断一个整数是否在这个范围之内
Math对象的拓展
- Math.trunc()
去除一个数的小数部分 - Math.sign()
判断一个数到底是正数,负数还是零
函数的拓展
函数参数的默认值
与解构赋值默认值结合使用:
上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
函数的length属性
制定了默认值以后,函数的length
属性将返回没有指定默认值的参数个数.
length
属性的含义是,该函数预期传入的参数个数,如果设置了默认值的参数不是尾参数,那么length
属性也不再计入后面的参数了
作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域.等到初始化结束,这个作用域就会消失.
应用
利用参数默认值,可以指定某一个参数不得省略;另外,可以将参数默认值设为undefined
,表明这个参数是可以省略的
rest参数
ES6引入rest参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments
对象了
rest参数是一个真正的数组,数组特有的方法都可以使用.注意:rest参数之后不能再有其他参数;函数的length
属性,不包括rest参数
严格模式
ES2016规定只要函数参数使用了默认值,解构赋值或者拓展运算符,那么函数内部就不能显式设定为严格模式
两种方法可以规避:第一种是设定全局性的严格模式;第二种是把函数包裹在一个无参数的立即执行函数里面
name属性
如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。
Function构造函数返回的函数实例,name属性的值为anonymous。
bind返回的函数,name属性值会加上bound前缀。
箭头函数
如果箭头函数的代码块部分多余一条语句,就要使用大括号将它们括起来,并且使用return
语句返回
由于大括号被解释成代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则报错
使用注意点:
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象 - 不可以当做构造函数
- 不可以使用
arguments
对象,可以用rest
参数来替代 - 不可以使用
yield
命令
this
对象的指向是可变的,但是在箭头函数中,它是固定的
this
指向的固定化,实际原因是箭头函数根本没有自己的this
,导致内部的this
就是外层代码块的this
.正是因为它没有this
,所以不能用作构造函数
双冒号运算符
函数绑定运算符是并排的两个冒号(::
),双冒号左边是一个对象,右边是一个函数.改运算符会自动将左边的对象,作为上下文环境(即this
对象),绑定到右边的函数上面
如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面
尾调用优化
尾调用是函数式编程的一个重要概念,是指某个函数的最后一步是调用另一个函数(return一个函数的调用)
优化:只保留内层函数的调用帧.如果所有的函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存.
"尾调用优化"对递归操作意义重大
函数式编程有一个概念,叫做柯里化(currying),意思就是讲多参数的函数转换成单参数的形式
ES6的尾调用优化只在严格模式下开启,正常模式是无效的
数组的拓展
拓展运算符
拓展运算符是三个点...
.好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
应用
- 复制数组
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
- 合并数组
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
- 与解构赋值结合
如果将拓展运算符用于数组赋值,只能放在参数的最后一位 - 字符串
可以将字符串转为真正的数组 - 实现了
Iterator
接口的对象
任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组。 - Map和Set结构,Generator函数
扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符
新增方法
- Array.from()
用于将两类对象转为真正的数组:类似数组的对象和可遍历的对象(包括ES6新增的数据结构Set和Map)
只要是部署了Iterator
接口的数据结构,Array.from
都能将其转为数组
注意区别:
扩展运算符背后调用的是遍历器接口,如果一个对象没有部署这个接口,就无法转换.Array.from方法还支持类似数组的对象.所谓类似数组的对象,本质特征只有一点,即必须有length属性.因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换.
Array.from({ length: 3 });
// [ undefined, undefined, undefined ]`
对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代.
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
Array.from
还可以接受第二个参数,用来对每个元素处理,将处理后的值放入返回的数组
如果map
函数里面用到了this
关键字,还可以传入Array.from
的第三个参数,用来绑定this
- Array.of()
Array.of()
不存在由于参数个数不同而导致的重载 - 数组实例的copyWithin()
在当前数组内部,将制定位置的成员复制到其它位置(会覆盖原有成员),然后返回当前数组 - 数组实例的find()和findIndex()
用于找出第一个符合条件的数组成员,参数是一个回调函数
find方法找出第一个返回值为true
的成员,然后返回该成员,如果没有符合条件的成员,则返回undefined
find
方法的回调函数可以接受三个参数,依次为当前的值,当前的位置和原数组
findIndex方法返回第一个符合条件的数组成员的位置,如果都不符合,则返回-1
这两个方法都可以接受第二个参数,用来绑定回调函数的this
对象
这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
findIndex方法可以借助Object.is方法做到
- 数组实例的fill()
fill
方法使用给定值,填充一个数组
fill
方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置 - 数组实例的entries(),keys(),values()
用于遍历数组,都返回一个遍历器对象
keys()
是对键名的遍历,values()
是对键值的遍历,entries()
是对键值对的遍历 - 数组实例的includes()
Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似
第二个参数表示搜索的起始位置,如果第二个参数为负数,则表示倒数的位置(比如第二个参数为-4,但数组长度为3),如果这时大于数组长度,则会重置为从0开始 - 数组的空位
空位不是undefined
,一个位置的值等于undefined
,依然是有值的.空位是没有任何值,in
运算符可以说明这一点
ES5对空位的处理,大多数情况下回忽略空位
ES6则是明确的将空位转为undefined
对象的拓展
属性的简介表示法
ES6允许直接写入变量和函数,作为对象的属性和方法
属性名表达式
属性名表达式与简洁表达式不能同时使用
属性名表达式入股欧式一个对象,默认情况下会自动将对象转为字符串[object Object]
,这一点要特别小心
方法的name属性
函数的name
属性,返回函数名,;对象方法也是函数,因此也有name
属性
如果对象的方法使用了取值函数getter
和存执函数setter
,则name
属性不是在该方法上面,而是该方法的属性的描述对象的get
和set
属性上面,返回值是方法名加上get
和set
有两种特殊情况:
bind
方法创造的函数,name
属性返回bound
加上原函数的名字;
Function
构造函数创造的函数,name
属性返回anonymous
如果对象的方法是一个Symbol
值,那么name
属性返回的是这个Symbol
值的描述
Object.is()
ES6提出"Same-value equality"(同值相等)算法,Object.is
就是部署这个算法的新方法
与严格比较运算符(===)的行为基本一致,不同之处只有两处:
-
+0
不等于-0
2.NaN
等于自身
Object.assgin()
用于对象的合并,将源对象的所有可枚举属性,复制到目标对象
Object.assgin
方法的第一个参数是目标对象,后面的参数都是源对象
由于undefined
和null
无法转成对象,所以如果它们作为参数就会报错;但是非对象参数出现在源对象的位置(非首参),首先,这些参数都会转成对象,如果无法转成对象,就会跳过.
其它类型值(数值,字符串和布尔值)不在首参数,除了字符串会以数组形式拷贝目标对象,其他值都不会产生效果
Object.assgin
拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性
注意点:
1. 浅拷贝
2. 同名属性的替换
3. 数组的处理
`Object.assgin`把数组视为属性名为0,1,2的对象
4. 取值函数的处理
`Object.assgin`只能进行值的复制,如果复制的值是一个取值函数,那么将求值后再复制
属性的可枚举性和遍历
可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来描述该属性的行为.Object.getOwnPropertyDescriptor
方法可以获取该属性的描述对象
描述对象的enumerable
属性,称为"可枚举性",如果该属性为false
,就表示某些操作会忽略当前属性
目前,有四个操作会忽略enumerable
为false
的属性:
-
for...in
循环 -
Object.keys()
返回对象自身的所有的可枚举的属性的键名 -
JSON.stringify
只串行对象自身的可枚举的属性 Object.assgin()
实际上,引入"可枚举"这个概念的最初目的,就是让某些属性可以对比掉for...in
操作,不然所有的内部属性和方法都会被遍历到
另外,ES6规定,所有Class的原型的方法都是不可枚举的
属性的遍历
- for...in
对象自身的和继承的可枚举属性(不含Symbol属性) - Object.keys(obj)
返回一个数组,包括对象自身(不含继承的)所有可枚举属性(不含Symbol属性)的键名 - Object.getOwnPropertyNames(obj)
返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)的键名
4.Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象自身的所有Symbol属性的键名 - Reflect.ownKeys(obj)
返回一个数组,包含对象自身的所有键名,不管键名是Symbol或字符串,也不管是都可枚举
以上五种遍历对象的键名,都遵守同样的属性比那里的次序规则:
- 首先遍历所有数值键,按照数值升序排列
- 其次遍历所有字符串键,按照加入时间升序排列
- 最后遍历所有Symbol键,按照加入时间升序排列
Object.getOwnPropertyDescriptors()
注意区别Object.getOwnPropertyDescriptor
方法返回某个对象属性的描述对象.ES2017引入Object.getOwnPropertyDescriptors
方法,返回指定对象所有自身属性(非继承属性)的描述对象
该方法的引入目的,主要是为了解决Object.assgin()
无法正确拷贝get
属性和set
属性的问题
Object.getOwnPropertyDescriptors
方法的另外一个用处,就是配合Object.create
方法,将对象属性克隆到一个新对象中
proto,Object.setPropotypeOf(),Object.getPrototypeOf()
__proto__
用来读取和设置当前对象的prototype
对象.不过无论从语义的角度还是从兼容性的角度都不要使用这个属性,实现上,__proto__
调用的是Object.prototype.__proto__
Object.setPropotypeOf()
方法的作用与proto
相同,用来设置一个对象的prototype
对象,返回参数对象本身.
Object.getPrototypeOf()
用于读取一个对象的原型对象
super关键字
this
关键字总是指向函数所在的当前对象,ES6新增另一个类似的关键字super
,指向当前对象的原型的对象.
注意:super
关键字表示原型对象时,只能用在对象的方法之中.目前,只有对象方法的简写方式可以让JavaScript引擎确认,定义的是对象的方法.
JavaScript引擎内部,super.foo
等同于Object.getPrototypeOf(this).foo
(属性)或Object.getPrototypeOf(this).foo.call(this)
(方法)
Object.keys(),Object.values(),Object.entries()
返回的数组中都是参数对象自身的(不含继承的)所有可遍历的属性
Object.create
方法的第一个参数是新创建对象的原型对象,第二个参数添加的对象属性,如果不显示声明,默认是不可遍历的
Object.entries
基本用途是遍历对象的属性,另一个用处是将对象转为真正的Map
结构
对象的拓展运算符
- 解构赋值
解构赋值要求等号右边是一个对象,如果等号右边是undefined
或null
,会报错
解构赋值必须是最后一个参数
解构赋值的拷贝是浅拷贝
另外,拓展运算符的解构赋值,不能复制继承自原型对象的属性
跟数组的拓展运算符一样,对象的拓展运算符后面可以跟表达式
拓展运算符的参数对象之中,如果有取值函数get
,这个函数会被执行