ES6阮一峰老师的书已经出到第三版了,从中受益匪浅,第二版读了三遍,在项目中常用到的一些语法和方法做些总结
字符串扩展
常用的一些方法
at()
// 参数传入角标,返回值为角标对应的字符'abc'.at(0)// 'a''吉'.at(0)// '吉'// 与ES5中charAt()不同之处,汉字的话ES5会返回对应Unicode编码,js内部用UTF-16
includes(), startsWith(), endsWith()
includes(),返回值为布尔值,表示是否找到参数的字符串
startsWith(),返回值为布尔值,表示参数字符串是否在原字符串的头部
endsWith(),返回布尔值,表示参数字符串是否在原字符串的尾部
这三个方法都支持第二个参数,表示开始搜索的位置。endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。
letstr ='Hello World'str.includes('Hello') //truestr.startsWith('Hello') //truestr.endsWith('d') //truestr.startsWith('world', 6) //truestr.endsWith('Hello', 5) //truestr.includes('Hello', 6) //false
repeat()
'x'.repeat(3) //"xxx"
注意:参数不能传负数和Infinity,参数NaN为0,参数为字符串会先转换为数字
padStart(),padEnd()
参数两个,第一个为位数,第二个是用什么补全,省略第二个参数时默认为空格补全。这两个更多的用途是补全指定位数
'1'.padStart(10,'0') //"0000000001"'12'.padStart(10,'0') //"0000000012"'123456'.padStart(10,'0') //"0000123456"
数组扩展
Array.from()
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
...运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(...[1,2,3])// 1 2 3console.log(1, ...[2,3,4],5)// 1 2 3 4 5
关于函数传参的问题,如果函数直接用...传参,传入的参数实际上是个数组,且后面不能再有参数。如果函数参数定义了一个数组,用...传入,实际上参数为数组中的值
functionfn(...items){}//等同于functionfn([数组内容]){}letitems = ['a','b','c']functionfn(...items){}// 等同于functionfn('a','b','c'){}
...运算符的应用
复制数组(克隆数组)
letarr1 = [1,2,3]letarr2 = [...arr1]// [1, 2, 3]
合并数组
constarr1 = ['a','b']constarr2 = ['c']constarr3 = ['d','e']constarr4 = [...arr1, ...arr2, ...arr3]// [ 'a', 'b', 'c', 'd', 'e' ]
与解构赋值结合
const[first, ...rest] = [1,2,3,4,5];first// 1rest// [2, 3, 4, 5]
将字符串转换为真正的数组
[...'hello']// ["h","e","l","l","o"]
Array.of(), 用于将一组值,转换为数组。
Array.of(3,11,8)// [3,11,8]Array.of(3)// [3]Array.of(3).length// 1
数组实例的fill()
三个参数,后面两个可以省略,第一个参数为替换成什么内容,第二个为替换的起始位置,第三个为替换的终止位置
['a','b','c'].fill(7)// [7, 7, 7]['a','b','c'].fill(7,1,2)// ['a', 7, 'c']
数组实例的 entries(),keys() 和 values()
三个方法都是遍历数组,都可以用for...of...唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
for(letindexof['a','b'].keys()) {console.log(index);}// 0// 1for(letelemof['a','b'].values()) {console.log(elem);}// 'a'// 'b'for(let[index, elem]of['a','b'].entries()) {console.log(index, elem);}// 0 "a"// 1 "b"
对象扩展
对象的话,解构赋值就不说了太基础了,我用的最多的就是...运算符和Object.assgin(),好吧,实际上...底层的方法就是assign()
Symbol
js数据类型除了string,number,boolean,undefined,null,object,第七种为symbol,他的作用就是防止定义变量时出现重复的定义导致覆盖的一些问题,这个我知道有这个,但从没用过
Set和Map结构
Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成Set数据结构。此结构不会添加重复的值(你想到了什么,一定想到了数组去重)
// 例一constset=newSet([1,2,3,4,4]);[...set]// [1, 2, 3, 4]// 例二constitems =newSet([1,2,3,4,5,5,5,5]);items.size// 5// 例三 数组去重[...newSet(array)]
Map
这个结构我个人感觉不如对象好用,没用过。实际上Map结构就是键值对的结构,里面设置和获取分别用set和get方法,我要设置的话每次都set一下,我觉得很不方便
Proxy拦截
基本说明
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
varobj =newProxy({}, {get:function(target, key, receiver){console.log(`getting${key}!`);returnReflect.get(target, key, receiver); },set:function(target, key, value, receiver){console.log(`setting${key}!`);returnReflect.set(target, key, value, receiver); }});
get()方法
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(即this关键字指向的那个对象),其中最后一个参数可选。
varperson = {name:"张三"};varproxy =newProxy(person, {get:function(target, property){if(propertyintarget) {returntarget[property]; }else{thrownewReferenceError("Property \""+ property +"\" does not exist."); } }});proxy.name// "张三"proxy.age// 抛出一个错误
set()方法
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
letvalidator = {set:function(obj, prop, value){if(prop ==='age') {if(!Number.isInteger(value)) {thrownewTypeError('The age is not an integer'); }if(value >200) {thrownewRangeError('The age seems invalid'); } }// 对于满足条件的 age 属性以及其他属性,直接保存obj[prop] = value; }};letperson =newProxy({}, validator);person.age =100;person.age// 100person.age ='young'// 报错person.age =300// 报错
async和await
基本说明
async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
await命令
正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。
asyncfunctionf(){returnawait123;}f().then(v=>console.log(v))// 123
class类
说明几个问题
constructor()
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
classPoint{}// 等同于classPoint{constructor() {}}
类继承中的super()
如果你要用类继承的话,里面一定要写super(),他是指定父类的this的,否则会报错
classA{}classBextendsA{constructor() {super(); }}
浏览器加载
defer和async
defer与async的区别是:defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
ES6的新语法
浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。
importutilsfrom"./utils.js";// other code
对于外部的模块脚本(上例是foo.js),有几点需要注意。
- 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 - 模块脚本自动采用严格模式,不管有没有声明use strict。 - 模块之中,可以使用import命令加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用export命令输出对外接口。 - 模块之中,顶层的this关键字返回undefined,而不是指向window。也就是说,在模块顶层使用this关键字,是无意义的。 - 同一个模块如果加载多次,将只执行一次。