1.let命令
块级作用域存在let,所生命的变量就绑定该区域,全局变量不起作用,在声明之前使用就会报错,声明变量之前该变量都可用叫暂时性死区,所以typeof也不是百分百安全的了。let不允许同作用域重复声明同一个变量,不管用let/var,所以也不能在函数内部重新声明参数
2.块级作用域
函数只能在顶层作用域和函数作用域中声明,不能在块级作用域声明,会在ES6的浏览器报错,但是浏览器的实现是可以的,是为了兼容,可以在块级作用域写函数表达式,而不是函数声明语句。如果没有大括号,JS认为不存在块级作用域。let只能出现在当前作用域的顶层
3.const命令
const声明制度常量,声明后常量值不能改变,只声明不赋值也会报错。const保证变量指向的内存地址保存的数据不能改动,但是对于复合类型,变量保存的只是指针,只能保证指向固定地址,指向数据结构是否可变不能控制所以声明必须非常小心。如果声明的是对象,是可以给对象添加属性的。冻结对象可以用Object.freeze,就不能添加新属性,对象属性也被冻结。
4.顶层对象属性
顶层对象在浏览器指window对象,Node中是global,ES5顶层对象属性和全局变量等价,ES6中var,function声明的全局变量是顶层对象属性,let,const,class声明的全局变量不属于顶层对象属性。
5.globalThis对象
顶层对象提供全局环境,所有代码在该环境运行,但是顶层对象的各种实现不同意,所以一般用this变量,全局环境this返回顶层对象,Node,ES6,this返回当前模块,且函数中的this,函数不作为对象方法运行,this会指向顶层对象,严格模式下返回undefined,任何模式,new Function('return this')()总会返回全局对象,但浏览器用了CSP,那eval,new Function这些方法都可能无法使用。以下为2种在所有情况都取到顶层对象的方法:
1. `// 方法一`
1. `(typeof window !== 'undefined'`
1. `? window`
1. `: (typeof process === 'object' &&`
1. `typeof require === 'function' &&`
1. `typeof global === 'object')`
1. `? global`
1. `: this);`
1. ``
1. `// 方法二`
1. `var getGlobal = function () {`
1. `if (typeof self !== 'undefined') { return self; }`
1. `if (typeof window !== 'undefined') { return window; }`
1. `if (typeof global !== 'undefined') { return global; }`
1. `throw new Error('unable to locate global object');`
1. `};`
6.数组解构赋值
ES6内部用===判断一个位置是否有值,只有数组成员严格等于undefined,默认值才会生效,否则就会被解构赋值,有Iterator接口的数据结构都可以采用数组形式的解构赋值。默认值为表达式,那么表达式惰性求值,只有用到的时候才会求值,能取到值,默认的表达式根本不会执行。默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
7.函数扩展
1)函数参数的默认值
直接在参数指定=默认值即可,参数变量是默认声明的,不可以用let,const再次声明,使用参数默认值函数不能有同名参数,参数默认值是惰性求值的,每次都重新计算默认值表达式的值
2)rest参数
形式为...变量,用来获取函数多余参数,就不用使用arguments对象,rest参数搭配的变量是一个数组,该变量把多余的参数放入数组中,利用该参数可以像函数传入任意数目的参数,使用rest代替arguments的例子
// arguments变量的写法function sortNumbers() {return Array.prototype.slice.call(arguments).sort();}- ``
// rest参数的写法const sortNumbers = (...numbers) => numbers.sort();因为arguments对象不是数组,是类似数组的对象,所以要调用Array.prototype.slice.call转成数组。rest参数只能是最后一个参数,后面不能有其他参数,而且函数的length属性不包括rest参数。
3)箭头函数
箭头=>用于定义函数,箭头前是参数,箭头后是返回值,多个参数或无参数需要用()括起来,代码块部分多于一条语句就需要用大括号括起来并使用return语句返回,如果返回的是对象,必须在对象外面加上括号。可以用于简化回调函数。函数体内的this就是定义时所在的对象,不是使用时所在的对象,箭头函数也不可以使用arguments,可以用rest参数,不可以用new命令,不可以作构造函数,不可以用yield命令,不能用作Generator,由于没有自己的this,就不能用call,apply,bind来改变this的指向。定义对象的方法,且方法内部包含this,和需要动态this时都不应使用箭头函数。 管道机制案例:
//reduce是对数组中每个元素按序执行一个提供的reducer函数,每次运行reducer函数,就把先前元素的计算结果作为参数传入,最后把结果汇总为单个返回值。有4个参数:被遍历对象.reduce((前一次返回值,正在处理元素),正在处理的元素索引)
const pipeline=(...func)=>val=>funcs.reduce((a,b)=>b(a),val);
constplus1=a=>a+1;
const mult2=a=>a*2;
const addThenMult=pipeline(plus1,mult2);
addThenMult(5);//相当于mult2(plus1(5)),结果是12
8.async函数
async是Generator的语法糖,async function(){await f1;}其实等于function *{yield f1}。 await表示紧跟的表达式需要等待结果,函数执行时一遇到就会先返回,等异步操作完成再执行后面的语句。async函数返回值是Promise对象,要等内部所有await命令后面的Promise对象执行完毕,才会发生状态改变(除非遇到return 或抛出错误),内部异步操作完才可以执行then指定的回调函数。如果await的Promise对象变为reject,那么reject的参数会被catch的回调捕捉到,且整个async函数都会中断执行。要保证前一个操作失败后一个也能执行,就把第一个await放在try块中,catch里放下一个await,或者await后面的Promise对象再跟一个catch方法。可以让独立的异步操作同时触发:
/ 写法一`
let [foo, bar] = await Promise.all([getFoo(), getBar()]);`
// 写法二`
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
9.Promise对象
1)Promise.try
不知道或不想区分函数f是同步还是异步,但想用Promise处理,因为可以不管f是否有异步操作,都用then方法制定下一步流程,用catch方法处理f抛出错误,一般用Promise.resolve.then(f),但是f是同步函数,就会在事件循环的末尾执行,要领同步函数同步执行,异步函数异步执行,就用async函数+立即执行匿名函数来写,用(async()=>f())().then().catch(),同步会得到同步结果,异步可以用then指定下一步,要捕获错误使用Promise.catch。也可以用立即执行的匿名函数+new Promise()实现。可以用Promise.try(()=>function).then().catch()