1.let const
都具有块级作用域:任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的.
存在暂时性死区: 区块中存在 const/let 就不能在声明之前使用该变量
var tmp = 123
if(true){
tmp = 456 //报错 ReferenceError
let tmp;
}
都不具备变量提升 不允许重复声明
let 申明变量,const 申明常量,除非定义的是对象,否则不能修改
2.模板字符串
${变量}
不用写之前的++
符号
使用反引号`123${变量}456` ==> 123变量456
3.变量解构赋值
-
使用[]和{}对数组和对象进行解构
//数组 按照对应位置 只要某种数据具有Iterator接口都可以采用数组形式的解构赋值 const foods = ['辣椒','肉','其他'] const [food1,food2,food3] = foods //对象 按照键名 const person = {name:'xx',age:24} const {name,age} = person ==> {name:name,age:age} = person 以上代码说明对象的解构赋值的内部机制,是先找到同名属性,再赋给对应的变量.真正被赋值的是后者,而不是前者
-
字符串/数值/布尔值解构赋值
只要等号右边的值不是对象或数组,都会将其转为对象 undefined和null无法转为对象会报错 //字符串 字符串被转换成了一个类似数组的对象,所以具有length属性,可以进行解构赋值 const [a,b,c] = '123' // a == 1,b == 2,c == 3 //数值和布尔值 数值和布尔值得包装对象都有toString属性,因此toString修饰下的s1,s2可取得值 let {toString:s1} = 123 let {toString:s2} = true s1 === Number.prototype.toString //true s2 === Boolean.prototype.toString //true
-
函数参数的解构赋值 类似于赋默认参数
-
常见用途
交换变量值 [x,y] = [y,x] 从函数返回多个值 let {foo,bar} = example() 函数参数定义 提取JSON数据 let {id,status,data} = res 遍历Map结构 for(let [key,value] of map){...} 获取模块指定方法 const {SourceNode} = require('source-map')
4.函数
参数默认值
function print(text = 'default'){...}
JavaScript 中函数的参数默认是undefined
新特性允许在没有值或undefined被传入时使用默认形参,有了默认参数,我们不需要再在函数体内做不必要的检查
尾调用和尾递归
尾调用:函数式编程的一个概念 指某个函数最后一步是调用一个函数 目前只有Safari支持尾调用优化
function f(x){return g(x)} //需要return一个函数 不然等同于函数操作后return undefined
函数调用会在内存形成一个"调用记录",又称调用帧(call frame),保存调用位置和内部变量信息,
如果A函数调用B,B再调用C,...循环往复所有的调用帧就会形成一个调用栈(call stack)
尾调用因为是函数内部最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置和变量信息都用不到了,
所以只需保留内层函数的调用帧,可以节省很大内存.
注意!闭包的情况下无法进行尾调用优化
尾递归:函数调用自身为递归;尾调用自身,就成为尾递归
尾递归不会发生栈溢出的情况,这里使用到了尾调用优化
function dg(n){if(n===1) return 1; return n*dg(n-1)} //递归 复杂度O(n)
function wdg(m,total=1){if(m===1) return total; return wdg(m-1,m*total)}
//尾递归 复杂度O(1)
箭头函数
不需要function关键字,不需要进行return关键字
箭头函数没有不可以使用arguments对象,该对象不存在于函数体内,可以用rest代替
this指向函数声明时的上下文作用域,所以是固定的定义时上层作用域中的this
需要获取动态this的时候,不要用箭头函数定义!
js传统的this指向情况有很多种,有时间的话会单独写一篇文章谈谈自己的体会
5.Spread/Rest 操作符
也被称作"三点运算符" 具体是 Spread 还是 Rest 需要结合上下文语境
当被用于迭代器中时,是一个Spread 扩展操作符:
迭代器(Iterator)是按照一定的顺序对一个或多个容器中的元素进行遍历的一种机制
function foo(x,y,z){...}
let arr = [1,2,3]
foo(...arr) //1,2,3 替代foo.apply(null,arr)这种写法
用途:
复制数组
合并数组
与解构赋值结合生成数组
具有迭代器接口的对象转为真正的数组(包含Map,Set结构,Generator函数)
对没有Iterator接口的对象使用扩展运算符会报错
当用于函数传参/解构赋值时,为Rest 剩余集合操作符:可以获取多余参数到数组,替代arguments对象
function foo(...args){...}
foo(1,2,3,4) //[1,2,3,4]
6.新增的对象 Symbol
首先是新的一种基本数据类型
为了避免对象属性名的冲突,Symbol类型的值通过Symbol('描述值')函数生成,该类型具有静态属性和静态方法;
不是严格意义上的构造函数,因为它不支持语法:"new Symbol()"
每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。
Symbol.for(名称): 接收字符串为参数,可以使用此方式使两个Symbol相等 具有"全局登记"机制
7.for...of
-
for...of 是遍历键值对中的值
-
for...in 遍历键值对中的键名
for...of和迭代器适用的其他类型...
8.class 类
支持 class 语法,不是新的对象继承模型,可以看做是原型链继承的语法糖
原型链继承也是js的核心部分,也会单独出一篇文章记录一下自己的理解。
9.import 和 export
ECMA 官方支持 export default 和 import 进行模块导出和导入,之前使用的是 require 模块引入的方法
10.Promise
异步编程的解决方案 比传统的--回调函数和事件--更合理和强大
简单来说就是一个容器 里面保存着未来才会结束的事件的结果.对象的状态不受外界影响;一旦状态改变就不会再变
注意: 如果某些事件不断地重复发生,一般来说,使用 Stream 模式比部署 Promise 更好
Promise是一个构造函数,接收一个函数作为参数,该函数有两个参数resolve,reject,它们是两个函数,由js部署.
1、基本逻辑:
有三个状态pending,fulfilled,rejected(只有异步操作的结果可以决定这个状态是哪一种)
pending--->resolved/rejected 这个过程是不可逆的
resolve将Promise状态由pending变为fulfilled,异步操作成功时调用,将异步操作结果以参数返回(res)
reject将pending变为rejected,是处理失败的回调,将异步操作报出错误以参数形式传递出去(error)
2、常用方法:
.then方法返回一个新的Promise实例且具有链式(.then.then...)传递结果的能力.接收两个回调函数作为参数.
第一个回调是Promise对象状态变为resolve时调用,第二个回调是Promise状态变为rejected时调用,
两个回调都接受Promise对象传出的值作为参数
.catch用于指定发生错误时的回调函数,Promise对象的错误具有"冒泡"性质,会一直向后传递,知道被捕获为止.
所以错误总是会被下一个catch语句捕获
3、Promise实例方法:
finally():不管Promise对象最后状态如何都会执行的操作
all():将多个Promise实例包装成一个新的Promise实例 必须多个实例状态都为fulfilled,
返回的才会是fulfilled
race():将多个Promise实例包装成一个新的Promise实例 只要有一个实例率先改变状态,新的实例状态就跟着改变
4、常见应用:
加载图片:
将图片加载写成一个Promise,一旦加载完成,Promise状态发生变化.多见于绘图库(Konva)的使用
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
Generator函数与Promise结合:
...待续
11.async/await
Promise 不能很好的解决一个请求依赖于另一个请求的情况,容易形成地狱回调
async/await 能将异步请求同步化
12.Set/Map 集合
-
Set 集合: 存储任何类型的"唯一"值,集合中的元素是不会重复的 常用来数组去重
let arr = [1,1,2,3] let singleArr = new Set(arr) // 1,2,3 singleArr是类数组结构,要转化成数组需要Array.from(singleArr) Set本身是一个构造函数,用来生成Set数据结构 方法: Set.prototype === new Set().__proto__ 指向同一个原型对象 实例方法: has() 用来查找值 Set.prototype.has(value) delete(value) 删除某个值 add(value) 添加某个值 clear() 删除所有成员 常用方法:[...new Set([+1,-1,NaN,NaN,2,2])] //[1,-1,NaN,2] 去除数组重复成员 [...new Set('ababbc')].join('') //"abc"
-
Map 集合: 各种类型的值(包括对象)都可以当做键的集合(一种更完善的 Hash 结构实现)
1、特性: ES6之后,如果需要"键值对"的数据结构,Map比Object更合适! 注意点: 只有对同一个对象的引用,Map结构才将其视为同一个键!! 比如如下情况:['a']是两个不同的数组实例,内存地址不一样 const map = new Map(); map.set(['a'], 555); map.get(['a']) // undefined 即Map的键实际上跟内存地址绑定,只要内存地址不一样,就视为两个键 !Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 !Map 的键值对个数可以轻易地通过size 属性获取 !Map 是 iterable 的,所以可以直接被迭代。 !在频繁增删键值对的场景下表现更好。 !骚年,用什么Object.create(null)啊? 直接new Map()走起... 2、方法: Map对象方法: has() 用来查找键名 Map.prototype.has(key) set(key,value) 设置键值对 get(key) ... has(key) ... delete(key) ... clear() ... 常用方法: Map转为数组: [...new Map()] 数组转为Map: new Map([[true,7],[{foo:3},['a,b,c']]]) Map转为对象: 利用Object.create(null) 再进行键值对赋值 对象转为Map: new Map(Object.entries(obj))
13.Proxy 和 Reflect
非常重要的新特性,vue3对于数据响应式处理就是基于Proxy和Reflect,代替了vue2的Object.defineProperty
Proxy
Proxy用于修改某些操作的默认行为,等于在语言层面做出修改,属于对编程语言进行编程
行为:在目标对象外层设置了"拦截",外界对该对象的访问都必须先通过这层拦截,
通常对外界访问进行过滤和改写,"代理"某些操作
操作:var proxy = new Proxy({},{get(){return...},set(){...}})
实例方法:
get(target,propKey,receiver):目标对象 属性名和实例本身(严格说是操作行为针对对象,可选);
如果一个属性不可配置且不可写,则Proxy不能修改该属性,否则通过Proxy对象访问该属性会报错
set(target,propKey,value,receiver):拦截某个属性的赋值操作,应该返回布尔值表明是否修改值成功
apply(target,ctx,args):目标对象,目标对象的上下文this,目标对象的参数数组 拦截某个函数
has(target,key):拦截HasProperty操作 典型的操作就是in运算符
constructor(target,args,newTarget):用来拦截new命令 必须返回一个对象
deleteProperty(target,key):拦截delete操作
defineProperty(target,key,descriptor):拦截Object.defineProperty()操作
...
this问题:
Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理
Reflect?
Reflect和Proxy对象一样,是ES6为了操作对象而提供的
(1)将Object对象一些明显属于语言内部的方法,放在Reflect对象上(比如Object.defineProperty)
会逐步迁移到Reflect上
(2)修改某些Object方法的返回结果
(3)让Object操作都变成函数行为 Reflect.has(Object,'assign') === 'assign' in Object
(4)Reflect对象的方法与Proxy对象的方法一一对应,
只要是Proxy对象的方法,都可以在Reflect对象上找到对应的方法(很重要)
14 新增方法
字符串:
实例方法 includes/startsWith/endsWith: 返回boolean值,是否含有/在头部/在尾部
实例方法 repeat: 返回新字符串,将原字符串重复n次
实例方法 padStart/padEnd: 头部补全/尾部补全
实例方法 trim/trimStart/trimEnd: 去除全部/头部/尾部空格
数值:
Number对象 isFinite/isNaN: 是否有限/是否为数值类型
Number对象 parseInt/parseFloat: 去除小数点后的数字/转成浮点数
Number对象 isInteger: 判断一个数值是否为整数
函数:
Function原型链 toString:返回和函数一模一样的原始代码的字符串
try...catch...:catch后面不必带有参数error
数组:
Array.from(): 类数组对象(具有length属性)/可遍历对象转为数组
Array.of(): 将一组值转换为数组 只有一个参数时是指定数组长度
实例方法 find/findIndex((value,index,arr)=>{},obj): 接收第二个参数obj,绑定回调函数的this对象;
都可以发现NaN,弥补indexOf方法的不足
实例方法 fill:使用给定值 填充一个数组
实例方法 entries():返回一个遍历器对象 是对键值对的遍历;
遍历器对象新增next()方法可以代替for..of进行手动遍历
实例方法 keys()/values():返回一个遍历器对象 对键名/键值的遍历
实例方法 includes: 判断数组是否包含给定的值
实例方法 flat()/flatMap(fn,this): 转换为一维数组/对数组成员执行一个函数返回一个数组,
然后对数组执行flat方法,但是默认只能展开一层
实例方法 at(): arr.at(-2) 获取到数组倒数第二位的值
实例方法 sort(): 排序算法 排序稳定性是排序算法的重要属性,即排序关键字相同的项目,排序前后的顺序不会变化.
现在sort和插入排序/合并排序/冒泡排序都是稳定的;堆排序/快速排序是不稳定的.
对象:
遍历对象属性的方法:
for...in 循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
Object.keys 返回数组,包括对象自身(不含继承)所有可枚举属性(不含Symbol)
Object.getOwnPropertyNames 返回数组,包括不可枚举属性的键名
Object.getOwnPropertySymbols 返回数组,包含对象自身所有Symbol属性的键名
Reflect.ownKeys 返回数组,包含对象所有键名
克隆对象的方法:
Json.parse(Json.stringify) //字符串类型的值储存在栈内存,可以达到深克隆效果
Object.assign({},obj) //obj的属性是基本类型的话,就是深克隆
{...obj} //浅克隆
for...in循环 逐个进行赋值
自己构建深克隆函数
Object对象方法 is(): 按照SameValue算法比较两个值是否相等 +0 !== -0 NaN===NaN 其他与严格相等运算符一致
Object对象方法 assign():将源对象可枚举属性复制到目标对象,后者会覆盖前者 无法正确拷贝get/set属性
Object对象方法 getOwnPropertyDescriptors:返回所有自身属性的描述对象,可以正确拷贝get/set属性
实例对象方法 __proto__: 这个属性时es5中对象的属性
首先 __proto__ === Object.prototype.__proto__
其次 obj.__proto__ === Object.create(obj) 读取或设置当前对象的原型对象,
__proto__只在浏览器环境下必须进行支持,从兼容性的角度,需要使用ES6中的方法进行此属性的替代
Object.getPrototypeOf(): 读取原型对象操作
Object.setPrototypeOf(): 设置原型对象操作
Object.create(): 生成原型对象操作
Object.entries():返回自身(不含继承)所有可遍历属性的键值对数组
Object.keys():返回所有可遍历属性的键名的数组
Object.values():返回可遍历属性的键值的数组,会过滤属性名为Symbol值得属性
n.ES6 转 ES5
目前通常使用 babel 转码器并配置.babelrc 文件
但是 Babel 默认只转换新的 js 句法(syntax),不转换新的 API,比如 Iterator Generator Set Map Proxy Reflect Symbol Promise 等全局对象 以及一些定义在全局对象上的方法 Object.assign()都不会进行转码
所以还需要为当前环境提供一个垫片: polyfill