#ES6基础知识点
JavaScript的组成:ECMAScript(核心)、DOM(文档对象模型)、BOM (浏览器对象模型)
ECMAScript,规定了语言的组成部分:语法、类型、语句、关键字、保留字、操作符、对象。我们现在使用的是ES5,也就是ECMAScript的第5版本。顾名思义,ES6就是ECMAScript的第6版本,在ES5的基础上,在保证向下兼容的前提下,提供大量新特性。
let const命令
1. let
let用于声明变量,用法类似于var
let与const的区别如下:
1.1 let声明的变量只在let命令所在的代码块中有效
1 | {var a = 10;let b = 1;} |
1.2 不存在变量提升,使用let时,一定要在变量声明后使用,否则会报错
1 | console.log(foo);//===>ReferenceError:foo is not defined |
1.3 暂时性死区(TDZ)
1 | //只要块级作用域内存在let命令,它所声明的变量就绑定在这个区域,不再受外部的影响 |
1.4 不允许重复声明
1 | let不允许在相同作用域内重复声明同一个变量 |
2. const
const 用来声明常量,一旦声明,其值就不能改变
1 | const PI = 3.14;<br/> |
2.1 const 一旦声明常量,就必须立即初始化,不能留到以后赋值;
2.2 const 只声明不赋值就会报错;SyntaxError:missing = in const declaration
2.3 const 作用域与let命令相同:只在声明所在的块级作用域内有效
2.4 const 也存在暂时性死区,只能在声明后使用
2.5 const 不可重复声明常量
2.6 对于复合类型的变量,变量名指向地址,不指向数据
1 | const foo = {}; |
模板字符串
<html></html>
传统的写法就是用+进行拼接,并且还要考虑到单引号与双引号的交替以及转义,并且不能换行
模板字符串直接使用反引号 `` 可以定义多行代码,并且嵌入变量
使用模板字符串的注意如下:
2.1 ``转义 +``
2.2 多行字符串,所有的空格以及缩进都会被保留在输出中
2.3 嵌入变量,需要将变量名写在${}
1 | function(name,value){ |
2.4 调用函数
1 | function fn(){ |
2.5 ${}大括号中的值不是字符串,按照一般的规则转为字符串
对象 – toString()
方法 – 执行方法后得到的值
变量的解构赋值
按照一定模式,从数组和对象中提取值,对变量进行赋值。类似于“模式匹配”
1. 数组解构赋值
1 | let [a, b] = [1, 2] // let a = 1; let b = 2; |
1.1 解构不成功,值就会变成undefined
1 | let [x, y, ...z] = ['1'] // let x = '1'; let y = undefined; let z = []; |
1.2 如果等号的右边不是数组,将会报错。
1 | // 报错 |
只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
1.3 解构赋值允许指定默认值
只有当一个数组成员严格等于(===)undefined,默认值才会生效
1 | let [x = 1] = []; // x = 1 |
如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值
1 | function f() { |
默认值可以引用解构赋值的其他变量,但该变量必须已经声明
1 | let [x = 1, y = x] = []; // x=1; y=1 |
2. 对象解构赋值
1 | let {a, b} = {a: 1, b: 2} // a = 1; b = 2; |
数组的解构与对象解构的不同:数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
1 let {sin, cos} = Math
2.1 变量名和属性名不一致
1 | let {a: m, b: n} = {a: 1, b: 2} // m = 1; n = 2; |
a,b其实是匹配的模式,而真正的变量是m,n
以前所写的let {a, b} = {1, 2}
其实是let {a: a, b: b} = {a: 1, b: 2}
的缩写
2.2 嵌套解构
1 | const node = { |
上面代码有三次解构赋值,分别是对loc、start、line三个属性的解构赋值。注意,最后一次对line属性的解构赋值之中,只有line是变量,loc和start都是模式,不是变量
1 | let obj = {}; |
2.3 对象解构也有默认值,默认值起作用的条件也是严格等于(===)undefined时
1 | let {x: y = 5, z: m = 1, w = 3} = {null} |
3. 解构注意的点⚠️
3.1 将一个已经声明的变量用于解构赋值
1 | let x; |
3.2 解构赋值允许等号左边的模式之中,不放置任何变量名
1 | ({} = [true, false]); |
不会报错,可以执行,但是没有任何意义
3.3 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
1 | let obj ={}; |
1 | let arr = [1, 2, 3]; |
除了数组,对象可以解构赋值,还有字符串,布尔值,数值,函数参数等的解构赋值,需要大家自己去学习。
箭头函数
定义函数: var f = v => v;相当于: var f = function(v){return v;}
1. 无参数或者需要多个参数,使用圆括号代表参数部分
1 | var f = () =>5; |
2. 代码块部分多于一行代码,使用大括号包裹起来
1 | var f = (num1,num2) => {return num1+num2;} |
3. 大括号是被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号
1 | var getTempItem = id => ({id:id,name:'Temp'}); |
4. 作用:简化回调函数
1 | var result = values.sort(function(a,b){return a-b;}); |
5. rest参数与箭头函数的结合
1 | const numbers = (...nums) => nums; |
⚠️注意:
1⃣️函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象
2⃣️不可以当做构造函数
1 | function foo(){ |
3⃣️不可以使用arguments对象,该对象在函数体内不存在,如果要使用,可以使用rest参数代替
//除了this,arguments、super、new.target在箭头函数中也是不存在的
Symbol
Symbol 独一无二的值,是js语言的第7种数据类型 (Undefined,Null,Boolean,String,Number,Object)
1. 使用注意
1.1 symbol值通过symbol函数生成,symbol值不是对象
1 | let s = Symbol(); |
1.2 可以接受一个字符串作为参数,用于对symbol实例的描述
1 | var s1 = Symbol('foo'); |
1.3 Symbol值不能与其他类型的值进行运算
1.4 Symbol值可以显式转为字符串
1 | var sym = Symbol('my symbol'); |
1.5 Symbol值可以转为Boolean值
1 | var sym1 = Symbol(); |
2. 作为属性名的Symbol
每一个symbol值都是不相等的,因此不会出现同名的属性
1 | var mySymbol = Symbol(); |
2.1 Symbol值作为对象属性名时不能使用点运算符,要放在方括号中
1 | var mySymbol = Symbol(); |
2.2 Symbol类型还可用于定义一组常量,保证这组常量的值都是不相等的。
2.3 消除魔术字符串
魔术字符串:在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或数值
使用同一个Symbol
1 | Symbol.for() |
Promise
所谓Promise,简单说就是一个容器
,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
1. Promise特点
1.对象的状态不受外界的影响,三种状态:pending(进行中),fufilled(已成功),rejected(已失败)
2.一旦状态改变,就不会再变。状态的两种改变:pending—>fulfilled,pending—>rejected
2. promise对象是一个构造函数,用来生成promise实例
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署
1 | var promise = new Promise(function(resolve, reject) { |
3. then方法指定resolved和rejected回调,then方法返回的是一个新的Promise实例,因此可以使用链式写法
1 | promise.then(function(value){ |
4. Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
1 | let promise = new Promise(function(resolve, reject) { |
p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行
5. catch方法是用于指定发生错误时的回调函数
1 | getJSON('/posts.json').then(function(posts) { |
getJSON方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
1 | var promise = new Promise(function(resolve, reject) { |
6. Promise.all() 用于将多个Promise实例包装成一个新的Promise实例
1 | var p1 = new Promise(function(resolve,reject){ |
p1、p2、p3只要有一个rejected,p就会变成rejected;只有全部是fulfilled,p才会变成fulfilled
7. Promise.race() 将多个Promise实例包装成一个新的Promise实例
1 | var p = Promise.race([p1,p2,p3]); |
8. Promise.reject() 将对象转为Promise对象,并且状态为rejected
9. Promise.resolve() 将对象转为Promise对象,并且状态为resolved
Module语法
前言:只介绍module的语法以及使用过程中的注意事项,不会介绍原理,感兴趣的可以自己去研究。
由于原生JavaScript没有模块这一个概念,对于过长的JavaScript文件而言,只能拆分,但是彼此之间又不能建立联系(除了使用window顶层对象,但是这种方法是不提倡的),后期社区执行了一些模块加载方案,主要就是CommonJs与AMD。CommonJs主要应用于服务器端(NodeJs);AMD主要用于客户端(Javascript),其中最常用的就是RequireJs。
ES6的module可以在服务器端,也可以在客户端,而且语法一致。
ES6 的模块自动采用严格模式,不管你有没有在模块头部加上”use strict”「ES5的东西,大家可以自己去看看」。ES6模块中的this指向undefined。
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能
1. export
1 | export var a = 1; |
等价于
1 | var a = 1; |
1.1 别名
1 | function v1() { ... } |
使用as
关键字,重新命名了函数v1和v2的对外接口。
1.2 export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系
1 | // 报错,直接输出1,1只是一个值,不是接口 |
1 | // 写法一 |
1.3 export命令可以出现在模块的任何位置,只要处于模块顶层就可以。
如果处于块级作用域内,就会报错,import命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。
2. import
使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
1 | // main.js |
form
后面的是模块的位置,可以是绝对路径,也可以是相对路径。.js的后缀可以省略。
2.1 大括号里面的变量名,必须与被导入模块对外接口的名称相同
2.2 别名使用as
1 | import { lastName as surname } from './profile.js'; |
2.3 import命名输入的变量是只读
如果是一个对象,则对象里面的属性是可写的,但是不利于后期的排错什么。
1 | import {a} from './xxx.js' |
2.4 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次
2.5 import命令具有提升效果,会提升到整个模块的头部,首先执行
1 | foo(); |
⚠️ES6模块是编译时加载,因此在import是静态执行,不可以使用变量,表达式。动态加载使用import()
1 | // 报错 |
参考资料:《ECMAScript 6 入门》阮一峰译(http://es6.ruanyifeng.com)