ECMAScript 和 JavaScript 的关系 : 前者是后者的规格,后者是前者的一种实现。
let
不存在变量提升,暂时性死区(它所声明的变量就绑定这个区域,不再受外部的影响),不允许重复声明,块级作用域(ES5 只有全局作用域和函数作用域)
const
不存在变量提升,暂时性死区(它所声明的变量就绑定这个区域,不再受外部的影响),不允许重复声明,块级作用域,const一旦声明变量,就必须立即初始化,不能留到以后赋值。提示:const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针。
变量的解构赋值
允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。如果解构不成功,变量的值就等于undefined。另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。 注意:如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。对于 Set 结构,也可以使用数组的解构赋值。 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
// 例一 let { log, sin, cos } = Math;
// 例二 const { log } = console; log('hello') // hello
字符串的解构赋值:
const [a, b, c, d, e] = 'hello';用途:(1)交换变量的值 (2)从函数返回多个值 (3)函数参数的定义 (4)函数参数的默认值
字符串的扩展:模板字符串 (只列举了觉得重要的以及常用的)
示例:let x = 1;
let y = 2;
${x} + ${y} = ${x + y}
字符串的新增方法
实例方法:includes(), startsWith(), endsWith(),repeat()
padStart(),padEnd() (如果某个字符串不够指定长度,会在头部或尾部补全)
正则的扩展
字符串的正则方法::match()、replace()、search()和split()
数值的扩展
Number.isNaN()用来检查一个值是否为NaN
Number.parseInt(), Number.parseFloat()
Number.isInteger()用来判断一个数值是否为整数。
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
指数运算符(**) 2 ** 2 // 4
函数的扩展
函数参数的默认值
函数的 length 属性 : 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。
作用域 : 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
rest参数:代替arguments对象(类似数组的对象),使用Array.prototype.slice.call先将其转为数组
箭头函数: 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
使用注意点:
(1) 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。 (2) 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。 (3) 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
数组的扩展
扩展运算符:它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
扩展运算符的应用:
(1)复制数组 (2)合并数组 (3)与解构赋值结合
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
Array.of方法用于将一组值,转换为数组
数组实例的 find() 和 findIndex()
对象的扩展
属性的遍历:for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性),Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。对象的新增方法
Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。 Object.setPrototypeOf(),Object.getPrototypeOf()
Symbol
根本上防止属性名的冲突,凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。Set 和 Map 数据结构
Set类似于数组,但是成员的值都是唯一的,没有重复的值。四个操作方法:add(),delete(),has(),clear(),四个遍历方法:keys(),values(),entries(),forEach()
WeakSet结构与Set类似,区别成员只能是对象,三个操作方法:add(),delete(),has()
Map它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。Map结构的实例有以下属性和操作方法。size属性,set(key,value),get(key),has(key),delete(key),clear()
const map = new Map([ ['name', '张三'], ['title', 'Author'] ]);
map.get('name') // "张三"
Map 结构原生提供三个遍历器生成函数和一个遍历方法。Map.prototype.keys(),Map.prototype.values(),Map.prototype.entries(),Map.prototype.forEach()
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。只接受对象作为键名(null除外),不接受其他类型的值作为键名。只有四个方法可用:get()、set()、has()、delete()。
Proxy
let proxy = new Proxy(target,handler)target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
Reflect
对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。 (1)将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。Promise
异步编程的一种解决方案,对象的状态不受外界影响。Promise对象的状态改变,只有两种可能:从pending变为resolved和从pending变为rejected。Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。 (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
Iterator
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
async 函数
async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
Class
类的数据类型就是函数,类本身就指向构造函数。在类的实例上面调用方法,其实就是调用原型上的方法。
在类的实例上面调用方法,其实就是调用原型上的方法。
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
取值函数(getter)和存值函数(setter),在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
注意点:(1)严格模式 类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。(2)类不存在变量提升(hoist),这一点与 ES5 完全不同。(3)name 属性 由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。(4)Generator 方法 (5)this的指向 类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
静态方法:如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”如果静态方法包含this关键字,这个this指的是类,而不是实例。
实例属性的新写法: 实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。
静态属性:静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。
Class的继承
Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
super 关键字
ES6 要求,子类的构造函数必须执行一次super函数。
Module 的语法
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict" 模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。Module 的加载实现
两个重大差异:
(1)CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。(2)CommonJS模块是运行时记载,ES6模块是编译时输出接口。
第二个差异是因为CommonJS加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。