1. ES6 函数
1.1 函数参数的扩展
1.1.1 默认参数
function fun(name,age=17){
console.log(name+","+age);
}
fn("张美丽",18); // "张美丽",18
fn("张美丽",""); // "张美丽"
fn("张美丽"); // "张美丽",17
1.1.2 不定参数
不定参数用来表示不确定参数个数,形如,...变量名,由...加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。
function fun1(...values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
function fun2 (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
1.2 箭头函数
箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
//参数 => 函数体
var f = v => v;
//等价于
var f = function(a){
return a;
}
f(1); //1
当箭头函数没有参数或者有多个参数,要用 () 括起来。
var f = (a,b) => a+b;
f(6,2); //8
当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
var f = (a,b) => {
let result = a+b;
return result;
}
f(6,2); // 8
箭头函数的使用
setInterval(() => {
console.log(this);
}, 1000);
2. Iterator(迭代器)
Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:
-
迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
-
迭代器是用于遍历数据结构元素的指针(如数据库中的游标)。
迭代的过程如下:
-
通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置
-
随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象,对象包含了 value 和 done 两个属性, value 是当前属性的值, done 用于判断是否遍历结束
-
当 done 为 true 时则遍历结束
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator]();
it.next();
>{value: "zero", done: false}
it.next();
>{value: "one", done: false}
it.next();
>{value: "two", done: false}
it.next();
>{value: undefined, done: true}
迭代的意义
var arr = ['a', 'b', 'c'];
var rel = arr[Symbol.iterator]();
do{
var obj = rel.next();
console.log(obj.value);
}while(!obj.done);
3. ES6 Promise 对象(重点)
3.1 Promise前言
在JavaScript的世界中,所有代码都是单线程执行的。
由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现。
AJAX就是典型的异步操作。
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
思考:把回调函数success(request.responseText)
和fail(request.status)
写到一个AJAX操作里很正常,但是不好看,而且不利于代码复用。
ajaxGet('http://...');
ajax.ifSuccess(success)
.ifFail(fail);
这种链式写法的好处在于,先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success
函数或fail
函数。
ES6提供了Promise的解决方案,可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果。
3.1.1 Promise概述
-
Promise 是异步编程的一种解决方案。
-
所谓promise,简单说是一个容器,里面保存着一个异步操作的结果。
-
从语法上说,promise是一个对象,从它可以获取异步操作的消息,promise提供了统一的API,各种异步操作都可以用同样的方法进行处理。
3.1.2 Promise 状态
对象的状态不受外界影响 (3种状态)
-
Pending状态(进行中)
-
Fulfilled状态(已成功)
-
Rejected状态(已失败)
一旦状态改变就不会再变 (两种状态改变:成功或失败)
-
Pending -> Fulfilled
-
Pending -> Rejected
3.1.3 then 方法
then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
3.2 基本使用
var pro = new Promise(function(resolve,reject){
resolve('成功内容');
// reject('错误信息');
});
pro.then(function(data){
console.log("success",data,pro);
},function(error){
console.log('error',error,pro);
});
3.2 promise结合数据请求
var baseURL = 'http://localhost:3008/api';
var pro = new Promise(function (resolve, reject) {
$.ajax({
type: 'GET',
url: baseURL + '/student/getStudent',
data: {
name: '小美'
},
success: function (data, status, xhr) {
resolve(data)
},
error: function (err) {
reject(err)
},
});
});
pro.then(function (data) {
console.log(data);
}, function (err) {
console.log(err);
});
3.3 回调地狱(Callback hell)多学一招
为了实现某些逻辑需要层层嵌套的回调函数来达到异步执行的先后顺序,但是如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱,代码阅读性非常差。
Promise解决方案
function fun1(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('定时器1');
},3000)
});
}
function fun2(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('定时器2');
},2000)
});
}
function fun3(){
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve('定时器3');
},1000)
});
}
// 通过链式调用来控制异步执行程序的顺序
fun1().then(data=>{
console.log(data);
return fun2();
}).then(data=>{
console.log(data);
return fun3();
}).then(data=>{
console.log(data);
});
4. ES6 Generator(生成器)
ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
Generator 函数组成
Generator 有两个区分于普通函数的部分:
-
一是在 function 后面,函数名之前有个 * ;
-
函数内部有 yield 表达式。
其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。
function* func() {
console.log("one");
yield '1';
console.log("two");
yield '2';
console.log("three");
return '3';
}
var f = func()
f.next();
// one
// {value: "1", done: false}
f.next();
// two
// {value: "2", done: false}
f.next();
// three
// {value: "3", done: true}
f.next();
// {value: undefined, done: true}
-
第一次调用 next 方法时,从 Generator 函数的头部开始执行,先是打印了 one ,执行到 yield 就停下来,并将yield 后边表达式的值 '1',作为返回对象的 value 属性值,此时函数还没有执行完, 返回对象的 done 属性值是 false。
-
第二次调用 next 方法时,同上步 。
-
第三次调用 next 方法时,先是打印了 three ,然后执行了函数的返回操作,并将 return 后面的表达式的值,作为返回对象的 value 属性值,此时函数已经结束,多以 done 属性值为true 。
-
第四次调用 next 方法时, 此时函数已经执行完了,所以返回 value 属性值是 undefined ,done 属性值是 true 。如果执行第三步时,没有 return 语句的话,就直接返回 {value: undefined, done: true}。
5. ES6 async 函数
5.1 async
async 是 ES7 才有的与异步操作有关的关键字。顾名思义,异步。async函数对 Generator 函数的改进,async 函数必定返回 Promise,我们把所有返回 Promise 的函数都可以认为是异步函数。
5.2 await
顾名思义,等待。正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。
特点体现在以下四点:
-
内置执行器
-
更好的语义
-
更广的适用性
-
返回值是 Promise
5.3 语法
async function name([param[, param[, ... param]]]) { statements }
-
name: 函数名称。
-
param: 要传递给函数的参数的名称。
-
statements: 函数体语句。
返回值:async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误。
function testAwait(){
return new Promise((resolve) => {
setTimeout(function(){
console.log("testAwait");
resolve();
}, 1000);
});
}
async function helloAsync(){
await testAwait();
console.log("helloAsync");
}
helloAsync();
// testAwait
// helloAsync
await 操作符用于等待一个 Promise 对象, 它只能在异步函数 async function 内部使用。
6. ES6 模块
6.1 什么是模块化开发
-
传统开发主要问题 1.命名冲突,多个js文件的全局变量有冲突问题。 2.文件依赖,多个js文件必须按顺序下载
-
于是引申出模块化开发 模块化开发就是把单独的一个功能封装到一个模块(文件中),模块之间相互隔离,但是可以通过特点的接口公开内部成员,可以依赖别的模块
6.2 模块化开发规范
在 ES6 前, 实现模块化使用的是 RequireJS 或者 seaJS(分别是基于 AMD 规范的模块化库和基于 CMD 规范的模块化库)。
ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。
ES6 的模块化分为导出(export)与导入(import)两个模块。
规范 | 内容 | 使用 |
---|---|---|
AMD规范 | 浏览器端的模块化 | Require.js |
CMD规范 | 浏览器端的模块化 | Sea.js |
CommonJS | 服务器端的模块化 | node模块化开发的规范<br/>1.模块分为单文件模块和包<br/>2.模块成员导出:module.exports和exports<br/>3.模块成员导入:require(‘模块标识符’) |
ES6模块化规范 | ES6模块化规范上面的三种都存在一定的差异性和局限性,<br />并不是浏览器和服务通用的模块化标准 | 1.每个js文件都是一个独立的模块<br/>2.导入模块成员使用import关键字<br/>3.暴露模块成员使用export关键字<br/>4.这是大一统的模块化规范,都使用这个 |
6.3 ES6模块化的export 与 import
基本用法
模块导入导出各种类型的变量,如字符串,数值,函数,类。
-
导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。
-
不仅能导出声明还能导出引用(例如函数)。
-
export 命令可以出现在模块的任何位置,但必需处于模块顶层。
-
import 命令会提升到整个模块的头部,首先执行。
语法
导入指定的模块
<script src="./module/module2.js" type="module"></script>
导出
export var 变量名 = 值;
批量导出
export {变量名1, 变量名2}
default 导出(唯一)
export default 变量名 ;
导入
import {变量名} from '模块'
批量导入
import {变量名1,变量名2} from '模块'
default导入
import 变量名 from '模块'
案例
/*-----export [test.js]-----*/
let myName = "zhangmeili";
let myAge = 20;
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is zhangmeili! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// zhangmeili
console.log(myClass.a );// yeah!
export default 命令。
-
在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
-
export default 中的 default 是对应的导出接口变量。
-
通过 export 方式导出,在导入时要加{ },export default 则不需要。
-
export default 向外暴露的成员,可以使用任意变量来接收。
var a = "My name is zhangmeili!";
export default a; // 仅有一个
export default var c = "error";
// error,default 已经是对应的导出变量,不能跟着变量声明语句
import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收