02.ES6(2)
2.10、rest参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
<script>
// ES5 获取实参的方式
/* function date(){
console.log(arguments);
}
date('大白','二黑','三孩'); */
// rest 参数
/* function date(...args) {
console.log(args); // filter some every map
}
date("大白", "二黑", "三孩"); */
// rest 参数必须要放到参数最后
/* function fn(a,b,...args){
console.log(a);
console.log(b);
console.log(args);
}
fn(1,2,3,4,5,6); */
</script>
注意:rest 参数非常适合不定个数参数函数的场景
2.11、Symbol
2.11.1、Symbol 基本使用
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
undefined、string、 symbol 、object 、null 、number 、boolean
Symbol 特点
1) Symbol 的值是唯一的,用来解决命名冲突的问题
2) Symbol 值不能与其他数据进行运算
3) Symbol 定义的对象属性不能使用 for…in 循环遍历 ,但 是可以使用Reflect.ownKeys 来获取对象的所有键名
注: 遇到唯一性的场景时要想到 Symbol
<script>
//创建方式
//创建Symbol方式1
let s = Symbol();
// console.log(s, typeof s);
//创建方式2
// Symbol()函数可以接受一个字符串作为参数,
// 表示对 Symbol 实例的描述。这主要是为了在控制台显示,比较容易区分。
let s2 = Symbol('bdqn');//'bdqn'这个内容只是个标识
let s3 = Symbol('bdqn');
console.log('s2===s3',s2===s3);//false
// 创建方式3 Symbol.for
// Symbol.for并不是每次都会创建一个新的symbol,它会先检索symbol表中是否有,没有再创建新的,有就返回上次存储的
let s4 = Symbol.for('bdqn');
let s5 = Symbol.for('bdqn');
console.log('s4===s5',s4===s5);//true
//注意事项
//1:不能与其他数据进行运算
// let result = s + 100;
// let result = s > 100;
// let result = s + s;
</script>
2.11.2、Symbol创建对象属性
可以给对象添加属性和方法
<script>
//向对象中添加方法 up down
let game = {
name: "俄罗斯方块",
up: function () {},
down: function () {},
};
//声明一个对象
/* let methods = {
up: Symbol(),
down: Symbol(),
};
game[methods.up] = function () {
console.log("我可以改变形状");
};
game[methods.down] = function () {
console.log("我可以快速下降!!");
};
console.log(game); */
let youxi = {
name:"狼人杀",
[Symbol('say')]: function(){
console.log("我可以发言")
},
[Symbol('zibao')]: function(){
console.log('我可以自爆');
}
}
console.log(youxi);
</script>
2.11.3、Symbol的内置对象
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
Symbol.hasInstance | 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.match | 当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被str.search(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split | 当该对象被str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag | 在该对象上面调用toString方法时,返回该方法的返回值 |
Symbol.unscopables | 该对象指定了使用with关键字时,哪些属性会被with环境排除。 |
2.12、迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。(Iterator 接口就是对象里面的一个属性)
Iterator 接口就是对象的一个属性
Iterator 的作用有三个:
一是 为各种数据结构,提供一个统一的、简便的访问接口;
二是 使得数据结构的成员能够按某种次序排列;
三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of循环
1) ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费
2) 原生具备 iterator 接口的数据(可用 for of 遍历)
a) Array
b) Arguments
c) Set
d) Map
e) String
f) TypedArray
g) NodeList
3) 工作原理
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
(5) 每调用 next 方法返回一个包含 value 和 done 属性的对象
done:是否循环完毕
注: 迭代器就是按照一定的顺序对元素进行遍历的过程
<script>
//声明一个数组
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙僧"];
//使用 for...of 遍历数组
for (let v of xiyou) {
console.log(v);
}
let iterator = xiyou[Symbol.iterator]();
console.log(iterator);
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
</script>
2.13、生成器
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
2.13.1、生成器基本语法
通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号(*)可以紧挨着function关键字,也可以在中间添加一个空格
<script>
//生成器其实就是一个特殊的函数
//异步编程 纯回调函数 node fs ajax mongodb
//yield 函数代码的分隔符,分割代码
function* cook() {
let i = 1;
console.log(`我被执行了${i}次`);
yield "盛米";
i++;
console.log(`我被执行了${i}次`);
yield "淘米";
i++;
console.log(`我被执行了${i}次`);
yield "煮米";
i++;
console.log(`我被执行了${i}次`);
}
let iterator = cook(); //返回一个迭代器对象,里面有个next()方法
//函数不会一下子执行完毕,会以yield为分割线,调一次next,执行一次
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//遍历
// for(let v of cook()){
// console.log(v);
// }
</script>
代码说明:
1) cook()前的星号 * 表明它是一个生成器
2) 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值
3) yield 相当于函数的暂停标记,每当执行完一条yield语句后函数就会自动停止执行,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码
4) next 方法可以传递实参,作为 yield 语句的返回值
5)yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误
2.13.2、生成器函数参数
概念:next('BBB')传入的参数作为上一个next方法的返回值。
<script>
function * gen(arg) {
console.log(arg);//AAA
let one = yield "aaa";
console.log(one);//BBB
let two = yield "bbb";
console.log(two);//CCC
let three = yield "ccc";
console.log(three);//DDD
}
//执行获取迭代器对象
let iterator = gen("AAA ");
console.log(iterator.next()); //{value: 'aaa', done: false}
//next方法可以传入的实参,作为yield语句整体返回的结果
console.log(iterator.next("BBB"));
console.log(iterator.next("CCC"));
console.log(iterator.next("DDD"));
</script>
2.13.3、生成器函数实例
<script>
// 异步编程 文件操作 网络操作(ajax, request) 数据库操作
// 需求:1s 后控制台输出 111 2s后输出 222 3s后输出 333
// 回调地狱(一层套一层,不停回调)
/* setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000); */
// 生成器函数解决回调地域
//生成3个异步函数
function one() {
setTimeout(() => {
console.log(111);
iterator.next(); //函数one执行完毕后,调用next(),执行下一个异步函数
}, 1000);
}
function two() {
setTimeout(() => {
console.log(222);
iterator.next(); //函数two执行完毕后,调用next(),执行下一个异步函数
}, 2000);
}
function three() {
setTimeout(() => {
console.log(333);
iterator.next(); //函数three执行完毕后,调用next(),执行下一个异步函数
}, 3000);
}
//用生成器函数将三个异步函数放在yield语句中
function* gen() {
yield one();
yield two();
yield three();
}
//调用生成器函数
let iterator = gen();
iterator.next(); //调用一次next执行一个yield语句,这个只会执行one()回调
</script>
2.14、Promise
2.14.0、回调地狱
回调地狱:就是回调函数嵌套过多导致的
- 当一个回调函数嵌套一个回调函数的时候
- 就会出现一个嵌套结构
- 当嵌套的多了就会出现回调地狱的情况
- 比如我们发送三个 ajax 请求
-
- 第一个正常发送
- 第二个请求需要第一个请求的结果中的某一个值作为参数
- 第三个请求需要第二个请求的结果中的某一个值作为参数
<script>
function fn() {
setTimeout(function () {
console.log("111");
setTimeout(function () {
console.log("222");
setTimeout(function () {
console.log("333");
}, 1000);
}, 2000);
}, 3000);
}
fn();
</script>
2.14.1、什么是promise
Promise是ES6异步编程的一种解决方案(目前最先进的解决方案是async和await的搭配(ES8),但是它们是基于promise的)
从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果。
2.14.2、Promise对象的状态:
2.14.2.1、对象的状态不受外界影响。
Promise 对象通过自身的状态,来控制异步操作。Promise 实例具有三种状态。
- pending: 等待中,或者进行中,表示还没有得到结果
- resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
- rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
2.14.2.2、Promise对象三种状态不受外界影响,三种的状态的变化途径只有两种。
- 从“未完成”到“成功”
- 从“未完成”到“失败”
一旦状态发生变化,就凝固了,不会再有新的状态变化。这也是 Promise 这个名字的由来,它的英语意思是“承诺”,一旦承诺成效,就不得再改变了。这也意味着,Promise 实例的状态变化只可能发生一次。
2.14.2.3、Promise 的最终结果只有两种。
- 异步操作成功,Promise 实例传回一个值(value),状态变为fulfilled。
- 异步操作失败,Promise 实例抛出一个错误(error),状态变为rejected。
2.14.3、Promise语法格式
//写法一
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
}).catch(function (err) {
// 失败的函数
})
//写法二
new Promise(function (resolve, reject) {
// resolve 表示成功的回调
// reject 表示失败的回调
}).then(function (res) {
// 成功的函数
},function(res){
// 失败的函数
})
出现了new关键字,就明白了Promise对象其实就是一个构造函数,是用来生成Promise实例的。能看出来构造函数接收了一个函数作为参数,该函数就是Promise构造函数的回调函数,该函数中有两个参数resolve和reject,这两个参数也分别是两个函数!
简单的去理解的话
resolve函数的目的是将Promise对象状态变成成功状态,在异步操作成功时调用,将异步操作的结果,作为参数传递出去。
reject函数的目的是将Promise对象的状态变成失败状态,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
注意:then方法可以接受两个函数,第一个函数为promise状态为成功的回调函数,第二个函数为promise状态为失败的回调函数(可以不写,一般用catch方法捕获promise状态为失败的异常信息)
2.14.4、代码示例
const promise = new Promise((resolve,reject)=>{
//异步代码
setTimeout(()=>{
// resolve(['111','222','333'])
reject('error')
},2000)
})
//写法一
promise.then((res)=>{
//兑现承诺,这个函数被执行
console.log('success',res);
}).catch((err)=>{
//拒绝承诺,这个函数就会被执行
console.log('fail',err);
})
//写法二
promise.then(
function (value) {
console.log(value);
},
function (reason) {
console.error(reason);
}
);
2.14.5、Promise封装Ajax
<script>
// 接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
//3. 发送
xhr.send();
//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response);
} else {
//如果失败
reject(xhr.status);
}
}
};
});
//指定回调
p.then(
function (value) {
console.log(value);
},
function (reason) {
console.error(reason);
}
);
</script>
2.14.6、promise的对象方法
p1,p2,p3为promise的实例对象
2.14.6.1、Promise.then()方法
then方法的返回结果是 Promise 对象, 返回对象状态由回调函数的执行结果决定,结果有以下:
a. 如果回调函数中返回的结果是 非 promise 类型的属性,状态为成功, 返回值为对象的成功的值
b. 是 promise 对象,内部返回的状态,就是它的状态
c. 抛出错误 返回失败的promise状态
链式调用 可以改变回调地狱的情况
<script>
//创建 promise 对象
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
// reject('出错啦');
}, 1000);
});
const result = p.then(
(value) => {
console.log(value);
//1. 非 promise 类型的属性
// return 'iloveyou';
//2. 是 promise 对象
// return new Promise((resolve, reject)=>{
// // resolve('ok');
// reject('error');
// });
//3. 抛出错误
// throw new Error('出错啦!');
throw "出错啦!";
},
(reason) => {
console.warn(reason);
}
);
//链式调用
p.then(value=>{}).then(value=>{});
</script>
2.14.6.2、Promise.catch()
一般用catch方法捕获promise状态为失败的异常信息
<script>
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!");
}, 1000)
});
// p.then(function(value){}, function(reason){
// console.error(reason);
// });
p.catch(function(reason){
console.warn(reason);
});
</script>
2.14.6.3、Promise.all()
并发处理多个异步任务,所有任务都执行完成才能得到结果
Promise.all( [p1,p2,p3] ) .then ( (result) => {consoleog (result)
})
2.14.6.3、Promise.race()
只返回异步任务数组中第一个执行完的结果,其他任务仍在执行,不过结果会被抛弃
应用场景:
几个接口返回一样的数据,哪个快用哪个
Promise.race ( [p1,p2] ).then ( (result)=>{
console. log (result)
})
2.15、set
2.15.1、set基本知识
ES6 提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历
2.15.1.1、实例的属性和方法
- size:返回Set实例的成员总数。
- Set.prototype.add(value):添加某个value。
- Set.prototype.delete(value):删除某个value,返回一个布尔值,表示删除是否成功。
- Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
- Set.prototype.clear():清除所有成员,没有返回值。
<script>
//声明一个 set
let s = new Set();
let s2 = new Set(['张三','李四','王五','赵六']);
//元素个数
// console.log(s2.size);
//添加新的元素
// s2.add('jack');
//删除元素
// s2.delete('李四');
//检测
// console.log(s2.has('王五'));
//清空
// s2.clear();
// console.log(s2);
//遍历元素
for(let v of s2){
console.log(v);
}
</script>
2.15.1.2、set遍历
- Set.prototype.keys():返回键名的遍历器
- Set.prototype.values():返回键值的遍历器
- Set.prototype.entries():返回键值对的遍历器
- Set.prototype.forEach():遍历每个成员
let arr = new Set(["red", "green", "blue"]);
// 返回键名
for (let item of arr.keys()) {
// console.log(item);//red green blue
}
// 返回键值
for (let item of arr.values()) {
// console.log(item);//red green blue
}
// set 键名=键值
// 返回键值对
for (let item of arr.entries()) {
// console.log(item);// ['red', 'red'] ['green', 'green'] ['blue', 'blue']
}
// set也有forEach()方法
arr.forEach((value, key) => console.log(key + " : " + value));
2.15.2、set实践
<script>
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
//1. 数组去重
// let result = new Set(arr);
// result = [...result];
// console.log(result);
//2. 交集
let arr2 = [4, 5, 6, 5, 6];
/* 可以先去重,避免做无用对比 */
// let result = [...new Set(arr)].filter(item => {
// let s2 = new Set(arr2);//数组2去重后的结果 4 5 6
// if(s2.has(item)){
// return true;
// }else{
// return false;
// }
// });
//简化版本
// let result = [...new Set(arr)].filter((item) => new Set(arr2).has(item));
//console.log(result);
//3. 并集
// let union = [...new Set([...arr, ...arr2])];
// console.log(union);
//4. 差集
// let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
// console.log(diff);
</script>
2.16、Map
ES6 提供了 Map数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
Map 的属性和方法:
1) size 返回 Map 的元素个数
2) set 增加一个新元素,返回当前 Map
3) get 返回键名对象的键值
4) has 检测 Map 中是否包含某个元素,返回 boolean 值
5) clear 清空集合,返回 undefined
<script>
/* map升级后的对象,键名可以是对象 */
//声明 Map
let m = new Map();
//添加元素 set()
m.set('name','bdqn');//键名:字符串
m.set('change', function(){//键名:字符串
console.log("我们可以改变你!!");
});
let key = {
school : '北京大学'
};
m.set(key, ['北京','上海','深圳']);//键名:对象
//size
// console.log(m.size);
//删除
// m.delete('name');
//获取 get()
// console.log(m.get('change'));
// console.log(m.get(key));
//清空 clear()
// m.clear();
//遍历
// for(let v of m){
// console.log(v);
// }
console.log(m);
</script>
2.17、class类
JavaScript 语言中,生成实例对象的传统方法是通过构造函数,ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
2.17.1、class初体验
<script>
// //es5通过构造函数实现
// function PersonO(username, password) {
// this.username = username;
// this.password = password;
// }
// // 添加方法
// PersonO.prototype.getstr = function () {
// console.log("用户名:" + this.username + ",密码:" + this.password);
// };
// // 实例化对象
// const po1 = new PersonO("章三", "abc123");
// console.log(po1);
// po1.getstr();
// es6实现 class方法实现
class PersonN {
//构造方法 constructor名字不能修改
// 当我们new实例对象的时候,这个方法自动执行
constructor(username, password) {
this.username = username;
this.password = password;
}
//方法必须使用该语法, 不能使用 ES5 的对象完整形式
getstr() {
console.log("用户名:" + this.username + ",密码:" + this.password);
}
//ES5 的对象完整形式 报错
// getstr:function () {}
}
const pn1 = new PersonN("123456", "789012");
pn1.getstr();
</script>
说明:使用class关键词 声明类,constructor为构造方法,一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加,
this关键字则代表实例对象,
getstr()为普通方法,不要用es5完整写法,getstr()存在 prototype上。
pn1.constructor === pn1.prototype.constructor // true
2.17.2 类的静态成员
es5可以直接给构造函数添加属性,方法,这些属性方法不在这个构造函数实例的对象身上
对应的es6中,类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
<script>
// es5
function Phone() {}
// 给Phone添加静态属性
Phone.name = "手机";
Phone.call = function () {
console.log("我可以打电话");
};
//实例化对象
let apple = new Phone();
// console.log(apple.name); //undefined
//apple.change(); //报错 实例身上是没有构造函数身上的属性和方法
// Phone.call(); //我可以打电话
class PhoneN{
static name='手机'
static game(){
console.log('我可以打游戏');
}
}
let p1=new PhoneN()
console.log(p1.name);//undefined
console.log(PhoneN.name);//手机
</script>
2.17.3 类的继承
2.17.3.1、es5的继承
<script>
//手机 父级构造函数
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function () {
console.log("我可以打电话");
};
//智能手机 子级构造函数
function SmartPhone(brand, price, color, size) {
//通过call方法改变this指向,指向SmartPhone的实例对象
Phone.call(this, brand, price);
//子类独有的属性
this.color = color;
this.size = size;
}
//设置子级构造函数的原型 让SmartPhone的实例对象有父类的属性方法
SmartPhone.prototype = new Phone();
SmartPhone.prototype.constructor = SmartPhone;
//声明子类的方法
SmartPhone.prototype.photo = function () {
console.log("我可以拍照");
};
SmartPhone.prototype.playGame = function () {
console.log("我可以玩游戏");
};
//实例化对象
const chuizi = new SmartPhone("锤子", 2499, "黑色", "5.5inch");
console.log(chuizi);
</script>
2.17.3.2、es6的继承
<script>
// 定义父类
class Student {
//构造方法
constructor(realname, age) {
this.realname = realname;
this.age = age;
}
//父类的成员属性
play(str) {
console.log("我会玩" + str);
}
}
//定义子类
class ItStudent extends Student {
//构造方法
constructor(realname, age, major) {
//调用父类的方法,相当于Student.call(this,realname, age)
super(realname, age);
this.major = major;
}
//子类的成员属性
program(type) {
console.log("我会编程的语言是:" + type);
}
// 子类对父类方法的重写
play(str) {
console.log("我只学习,不玩" + str);
}
}
//实例化对象
const It1 = new ItStudent("张三", 20, "大数据");
console.log(It1.realname);
It1.play("游戏"); //我只学习,不玩游戏
</script>
说明:Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法。ES6 规定,子类必须在constructor()方法中调用super(),否则就会报错。
2.17.4、 getter和setter设置
<script>
// get 和 set
class Phone {
// get 监测动态属性的变化
get price() {
console.log("价格属性被读取了");
return "iloveyou";
}
// set 对更改设置的值做判断,合法怎么办,不合法怎么办
set price(newVal) {
console.log("价格属性被修改了");
}
}
//实例化对象
let s = new Phone();
console.log(s.price);//输出price属性,只要读取,就会执行get后面对应的函数代码,函数的返回值就是属性的返回值
s.price = "9.9";//更改price属性,只要更改,就会执行set后面对应的函数代码
</script>
2.18、模块化(module)
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
2.18.1、模块化的优势
1) 防止命名冲突
2) 代码复用
3) 高维护性,可以对某些模块化升级
2.18.2、ES6 模块化语法
模块功能主要由两个命令构成:export 和 import。
export 命令用于规定模块的对外接口,对外暴露
import 命令用于输入其他模块提供的功能 ,引入暴露的文件
注意:
1、用了es6的语法, 浏览器默认将它作为js解析会出现问题,需要将它作为模块导入,script
标签默认type="text/javascript"
,需要改为type="module"
,更改后的index.html
:
2、跨域请求只支持这些协议:http, data, chrome, chrome-extension, chrome-untrusted, https.,而我们的协议是file,这里我们需要本地起一个服务器来作为资源的提供方,简单的方式是安装VSCode的一个扩展Live Server
2.18.2.1、暴露语法汇总
2.18.2.1.1、分别暴露
//m1.js 分别暴露
export let school = 'bdqn';
export function teach() {
console.log("我们可以教给你开发技能");
}
2.18.2.1.2、统一暴露
// m2.js 统一暴露
let school = 'bdqn';
function findJob(){
console.log("我们可以帮助你找工作!!");
}
export {school, findJob};
2.18.2.1.3、默认暴露
//m3.js 默认暴露
export default {
school: 'bdqn',
change: function(){
console.log("我们可以改变你!!");
}
}
export和export default的区别(面试题)
1、两者均可用于导出常量、函数、文件、模块;
2、在一个文件中可以多次使用export,但是export default只能用一次;
3、通过export输出的,在import导入时需要使用{},export default不需要;
4、export与export default不可同时使用;
2.18.2.2、引入暴露文件语法
2.18.2.2.1、通用方式
//1. 通用的导入方式
//引入 m1.js 模块内容
import * as m1 from './js/m1.js'
//引入 m2.js 模块内容
import * as m2 from "./js/m2.js";
//引入 m3.js
import * as m3 from "./js/m3.js";
console.log(m1);
2.18.2.2.2、解构附值
//2. 解构赋值形式的导入方式
import { school, teach } from "./js/m1.js";
//用别名
import {school as yyzx, findJob} from "./js/m2.js";
import {default as m3} from "./js/m3.js";
2.18.2.2.3、简易形式
//3. 简便形式的导入方式 针对默认暴露
import m3 from "./js/m3.js";
2.18.2.3、建立入口文件形式
// app.js 入口文件
//模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";
console.log(m1);
console.log(m2);
console.log(m3);
<!--index.html 引入 入口文件 -->
<script src="./js/app.js" type="module"></script>