let变量声明以及声明特性

let a;
let a,b,c;
let a=100;

特性:

  1. let变量不能重复声明,但是var却可以。
  2. 块级作用域:变量只在代码块里有效,在代码块外无效。[全局、函数、eval]
  3. 不存在变量提升。(如果有变量提升,可以在变量未声明之前去使用这个变量)
  4. 不影响作用域链。
{
let name = "xiaolin0333";
function fn() {
console.log(name);//会向上一级作用域里去找name变量
}
fn();
}

const声明常量及其特点

const NAME = "xiaolin0333";

特性:

  1. 一定要赋初始值。
  2. 一般常量使用大写(潜规则)
  3. 常量值不能修改。
  4. 块级作用域
  5. 对于数组和对象的元素修改,不算对常量的修改,不会报错
const DATA = ['xiaolin','03','cmb'];
DATA.push('dog');//正确 常量所指的地址没有发生变化
DATA = 100;//错误

以后声明数组或者对象的时候使用const来声明可以避免误操作

变量的解构赋值

ES6允许按照一定模式从数组和对象中提取值,对变量进行操作。

//数组的解构
const F2 = ['lsh','cmb'];
let [lin, chai] = F2;
console.log(lin + chai);

//对象的解构
const lin = {
name:'xiaolin0333',
age:18,
dadaima:function() {
console.log('我可以打代码');
}
}
let {name,age,dadaima} = lin;//相当于声明了三个变量,还对他们进行了赋值
console.log(name + age);
dadaima();

【注】:一般函数使用解构的方法来写居多

let {dadaima} = lin;//但是let{}里面的dadaima需要和lin里面的dadaima函数同名才行
dadaima();

模板字符串

ES6引入新的声明字符串的方式``
ES5:' '" "

//声明
let str = `我也是个字符串`;

特性:

  1. 内容里可以直接出现换行符(单引号、双引号不能直接出现换行符、只能用+来连接)
  2. 可以通过${变量名}进行变量拼接
let lin = 'xiaolin0333';
let wo = `我是${lin}

简化对象写法

ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,这样的书写会更加简洁。

let name = 'xiaolin0333';
let dadaima = function() {
console.log('我爱打代码');
}
const lin = {
name,
dadaima
paobu() {
console.log('我也爱跑步');
}
}
//等价于如下代码
const lin = {
name = name,
dadaima = dadaima
paobu:function() {
console.log('我也爱跑步');
}
}

箭头函数

ES6允许使用箭头=>来定义函数

//函数声明
let 函数名 = (形参) => {
代码体
}
let fn = (a,b) => {
return a+b;
}
//调用函数
let result = fn(1,2);

特性:

  1. this是静态的,this始终指向函数声明时所在作用域下的 this的值。
    (普通函数是:谁调用该函数,这个this就指向谁)
function getName() {
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
}
//设置window对象的name属性
window.name = 'xiaolin0333';
const lin = {
name:'03'
}
//直接调用
getName();//输出:xiaolin0333(普通函数直接调用this值指向window)
getName2();//输出:xiaolin0333(箭头函数在全局作用域下声明的,this也是指向window)
//call()方法调用(可以改变函数内部的this值)
getName.call(lin);//输出:03(此时函数内部的this值指向了lin这个对象)
getName2.call(lin);//输出:xiaolin0333(箭头函数this值静态的,仍然指向函数在声明时所在作用域的this值)
  1. 不能作为构造函数实例化对象
let Person = (name,age) = {
this.name = name;
this.age = age;
}
let me = new Person('lin',20);//报错
  1. 不能使用arguments变量
let fn = () => {
console.log(argument);
}
fn(1,2,3); //报错
  1. 箭头函数的简写
  • 省略小括号。(当形参有且仅有一个的时候)
let add = (n) => {
return n+n;
}
//上面代码可以简写成如下代码
let add = n => {
return n+n;
}
  • 省略花括号。(当代码体仅有一条语句的时候)
let pow = (n) => {
return n*n;
}
//上面代码可以简写成如下代码
let pow = n =>n*n;

【注】此时return也必须省略,而且语句的结构就是函数的返回值

【适用场景】:

  1. 箭头函数适合与this无关的回调(定时器、数组的方法回调)
  2. 箭头函数不适合与this有关的回调(DOM元素的事件回调,对象的方法)
let ad = document.querySelector('#ad');
//绑定事件
ad.addEventListener("click",function() {
//保存this的值
let _this = this;
//定时器
setTimeout(function(){//定时器的this指向window 所以需要外面定义一个_this 才能在定时器回调函数里面使用
//修改背景颜色 this
_this.style.background = 'pink';
},2000)
})
//使用箭头函数就可以解决这个问题
ad.addEventListener("click",function() {//这里不能改成箭头函数,否则this就无法指向事件源
//定时器
setTimeout( ()=>{
//箭头函数的this是静态的,指向声明时所在作用域下的值,而这个定时器函数是在外层作用域下声明的,外层作用域里的this指向事件源ad
_this.style.background = 'pink';
},2000)
})

函数参数的默认值设置

ES6允许给函数参数(形参)赋初始值
特性:

  1. 具有默认值的参数,一般位置要靠后。(潜规则)【放前面意义不大,因为实参也会按顺序与实参对应】
function add(a,b,c=10) {
return a+b+c;
}
console.log(add(1,2));//13
  1. 默认值可以与解构赋值结合使用
function connect(options) {
let host = options.host;
let username = options.username; //代码冗余度比较高
}
//调用函数(传一个对象)
connect({
host:'localhost',
username:'root',
password:'root',
port:3306
})
//函数声明简便写法如下
function connect({host="127.0.0.1",username,password,port}) {//使用解构赋值就不是“实参按顺序与形参对应了”
let host = host;
let username = username;
}

rest参数

ES6引入rest(数组)参数,用于获取函数的实参,用来代替arguments(对象)

//ES5获取实参的方式
function data() {
console.log(arguments);//是个对象形式
}
data('xiaolin','03');
//rest参数
function data(...args) {
console.log(args);//是个数组的格式 可以使用数组的很多方法(filter some every map)使我们对参数处理更灵活
}
data('xiaolin','03');
//[注]rest参数必须放在参数最后
function fn(a,b,...args) {

}

扩展运算符

扩展运算符... 能够将数组转化为逗号分割的参数序列

const lin = ['xiaolin','03'];
//把lin数组变成这种形式 'xiaolin','03'
function name() {
console.log(arguments);
}
name(...lin);//等价于name('xiaolin','03')

扩展运算符和rest区别:rest参数的声明是放在了函数声明的形参位置,扩展运算符...是放在函数调用的实参位置

【应用】

  1. 数组的合并
const name1 = ['xiaolin'];
const name2 = ['03','33'];
const name = [...name1,...name2];//合并name1和name2
  1. 数组的克隆
const name = ['xiaolin','03','33']
const me = [...name];//克隆name数组到me数组里

【注】如果有引用类型,也是一个浅拷贝

  1. 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');//divs得到的是个伪数组
const divArr = [..divs];//divArr是真正的数组

Symbol的介绍与创建

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。他是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
【特点】:

  1. Symbol的值是唯一的,用来解决命名冲突的问题
  2. Symbol值不能与其他数据类型进行运算(加减乘除、字符串拼接、比较)
  3. Symbol定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
//创建Symbol
//法1
let s = Symbol();
//法2
let s2 = Symbol('xiaolin');//Symbol里面的字符串(描述字符串)相当于是做一个注释
let s3 = Symbol('xiaolin');
console.log(s2===s3);//false
//法3
let s4 = Symbol.for('xiaolin');
let s5 = Symbol.for('xiaolin');
console.log(s4===s5);//true

let result = s+s;//报错

【总结】:7种数据类型
undefined、string、symbol、object、null、number、boolean

Symbol的使用

向对象中添加属性和方法(如果给一个别人写的对象添加方法up(),不确定是否已经有这个方法,就可以使用Symbol来添加,更加简单高效安全。)
【例】:game是一个别人写的对象,我现在要添加up()和down()方法到这个对象里,但是不确定这个game对象里面是否已经存在这两个方法

let game = {...}//别人写的一个对象
//声明一个对象
let methods = {
up:Symbol(),
down:Symbol()
};
//将这两个方法添加到对象中
game[methods.up] = function() {
console.log("我是up()方法");
}
game[methods.down] = function() {
console.log("我是down()方法");
}
let youxi = {
name:"狼人杀",
[Symbol('say')]:function() {//因为Symbol()是个表达式,所以这里要加上[]
console.log('我可以发言');
},
[Symbol('zibao')]:function() {
console.log('我可以自爆');
},
}

Symbol内置值

除了定义自己使用的Symbol值之外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法
【例】Symbol.hasInstance:当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。

class Person {
static [Symbol.hasInstance](param) {
console.log(param);
console.log("类型检测的时候调用我");
}
let o = {};
console.log(o instanceof Person);//输出:{}
// 类型检测的时候调用我
// false
}

Symbol.hasInstance都是Symbol内部的属性,这个整体又作为对象里边的属性,来改变对象在特定场景下的表现(扩展对象功能)

迭代器

迭代器(Iterator)是一种接口,为不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作。

  1. ES6创建了一种新的遍历命令for…of循环。(只要数据结构部署了Interator接口,就可以使用for…of来遍历数据)
  2. 原生具备Interator接口的数据(可用for…of遍历):Array、Arguments、set、Map、String、TypedArray、NodeList

Interator就是指:对象里的一个函数Symbol(Symbol.Interator)

//声明一个数组
const lin = ['xiaolin','03','33'];
//使用for...of遍历数组(v是键值)
for(let v of lin) {
console.log(lin);//输出 xiaolin 03 33
}
//对比for...in遍历数组(v是键名)
for(let v in lin) {
console.log(lin);//输出 0 1 2
}

【应用】:需要自定义遍历数据的时候,要想到迭代器。
【例】:使用for…of遍历对象,每次返回结果是数组stus的成员

//声明一个对象
const banji = {
name:'物联网二班',
stus:[
'xiaolin',
'03',
'33'
],
//自定义迭代器
[Symbol.interator]() {
//索引变量
let index = 0;
let _this = this;
return {
next:function() {
if(index<_this.stus.length) {//this指向的不是banji这个对象(也可以使用箭头函数)
const result = {value:_this.stus[index],done:false};
index++;
return result;
} else {
return {value:undefined,done:true};
}

}
}
}
}
//banji.stus.forEach();//也可以遍历stus数组的成员,但是不符合面向对象的思想。(不能直接对数据成员操作)
for(let v of banji) {
console.log(v);
}

自定义迭代器工作原理如下:

  1. 创建一个对象,指向当前数据结构的起始位置
  2. 第一次调用对象的next方法(规定就叫做next方法),指针自动指向数据结构的第一个成员
  3. 接下来不断调用next方法,指针一直向后移动,直到指向最后一个成员
  4. 每次调用next方法返回一个包含value和done属性的对象
    如果指针所指向的成员有定义,{value:指针所指成员,done:false},否则{value:undefined,done:true}

生成器函数生命与调用

生成器是一个特殊函数,用来进行异步编程(纯回调函数)。

//格式:function * 函数名() {}
function * gen() {
//yield等价于函数代码的分隔符(把函数代码分割成几块)
yield '一只没有眼睛';
yield '一只没有尾巴';
yield '真奇怪';
//gen()返回迭代器对象(如果里面有console.log的话,不会输出里面的内容,得调用next()方法才能执行)
let interator = gen();
console.log(interator.next());//{value:"一只没有眼睛",done:false}
console.log(interator.next());//{value:"一只没有眼睛",done:false}
console.log(interator.next());//{value:"真奇怪",done:false}
console.log(interator.next());//{value:undefined,done:true}
for(let v of gen()) {
console.log(v); //输出:一只没有眼睛 一只没有眼睛 真奇怪
}
}

【参数传递】

  1. 整体函数传参
  2. next方法传参
function *gen(arg) {
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three)
}
//执行迭代器对象
let interator = gen('AAA'); // 整体函数传参
// 以下都是next方法传参
interator.next();//输出:AAA
//next方法可以传入实参(作为上一个yield的返回结果)
//下面代码传入BBB,作为第一个yield语句整个的返回结果
interator.next('BBB');//输出:BBB
//下面代码传入CCC,作为第二个yield语句整个的返回结果
interator.next('CCC');//输出:CCC
//下面代码传入DDD,作为第三个yield语句整个的返回结果
interator.next('DDD');//输出:DDD

【生成器函数实例】:异步编程:文件操作、网络操作(ajax、requests)、数据库操作
【例】:定时器案例(1s后控制台输出111 2s后控制台输出222 3s后控制台输出333)

//传统方法
//代码缩进不断向前缩进,阅读调试不方便(回调地狱)
setTimeout(()=>{
console.log(111);
setTimeout(()=> {
console.log(222);
setTimeout(()=> {
console.log(333);
},3000);
},2000);
},1000);

//生成器函数
function one() {
setTimeout(()=> {
console.log(111);
interator.next();
},1000);
}
function two() {
setTimeout(()=> {
console.log(222);
interator.next();
},2000);
}
function three() {
setTimeout(()=> {
console.log(333);
},3000);
}
function * gen() {
yield one();
yield two();
yield three();
}
//调用生成器函数
let interator = gen();
interator.next();

Promise

Promise是ES6引入的异步编程的解决方案(主要解决回调地狱的问题)。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

//实例化Promise对象(有三种状态:初始化、成功、失败)
const p = new Promise(function(resolve,reject) {
setTimeout(function() {//异步操作
if(成功){
let data = '数据库中的用户数据';//得到数据
//调用resolve()和reject()方法可以改变Promise对象的状态
resolve(data);//调用resolve()方法,Promise对象p的状态为成功===>then()方法执行第一个回调函数的内容
}
else {//失败
let err = '数据读取失败';
reject(err);//调用reject()方法,Promise对象p的状态为失败===>then()方法执行第二个回调函数的内容
}
},1000);


//调用Promise对象的then方法(返回结果是Promise对象,对象状态由回调函数的执行结果决定)
//1.如果回调函数中返回结果是:非Promise类型的属性(无返回值undefined 也算非Promise类型),状态为成功。返回值为对象的成功值
//2.如果回调函数中返回结果是:Promise对象,返回值和then方法返回的值类型是一样的
//3.如果回调函数中返回的是:抛出错误,then方法返回值状态也是失败

const result = p.then(function(value) {//调用成功的形参
console.log(value);//成功时输出:数据库中的用户数据
//1.非Promise类型的属性
//return 123;
//2.是promise对象
//return new Promise((resolve,reject)=> {
//resolve('ok');//返回成功,then方法也返回成功
//})
//3.抛出错误
throw new Error('出错啦');
},function(reason) {//调用失败的形参
console.error(reason);//失败时输出:数据读取失败
})
console.log(result);
})
//综上所述
//then方法可以链式调用
p.then(value=> {},reason=>{}).then(value=>{},reason=>{})
//也可以只指定一个回调
p.then(value=>{}).then(value=>{})

Set

ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值唯一,集合实现了Iterator接口,所以可以使用扩展运算符和for…of进行遍历。
【声明一个set】:

let s = new Set();
let s2 = new Set(['大事','小事','坏事','小事']);//可以传入一个可迭代数据(一般传入数组)
console.log(s2);//'大事','小事','坏事' ==> 自动去重

【元素个数】:不包括重复元素size

console.log(s2.size);//3

【增】:添加新的元素add()

s2.add('喜事');
console.log(s2);//'大事','小事','坏事','喜事'

【删】:删除元素delete()

s2.delete('坏事');//'大事','小事','喜事'

【是否存在】:检测has()

console.log(s2.has('好事'));//true

【遍历】:使用for…of遍历集合

for(let v of s2) {
console.log(v);
}

【清空】:清空clear()

console.log(s2.clear());//集合里内容为空

【集合实践】:

//集合实践:
let arr = [1,2,3,4,5,4,3,2,1];
//1.数组去重
//let result = new Set(arr);//此时result是个集合(集合可以自动去重)
let result = [...new Set(arr)];//通过扩展运算符把result集合改成数组==>此时的数组已经是去除重复元素后的数组
console.log(result);//[1,2,3,4,5]

//2.交集arr ∩ arr2
let arr2 = [4,5,6,5,6];
let result = [...new Set(arr)].filter(item => {//数组先去重 再使用过滤器过滤相同的元素
let s2 = new Set(arr2);
if(s2.has(item)) {//s2里有arr数组去重后的元素
return true;
} else {
return false;
}
})
console.log(result);//[4,5]

//3.并集
let union = [...arr,...arr2];//合并两个数组,此时存在重复元素
union = [...union];//把重复的元素去除

//4.差集====>就是交集取反

Map

ES6提供了Map数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了Iterator接口,所以可以使用扩展运算符和for…in进行遍历。

//声明Map
let m = new Map();

【增】:添加元素 对象.set(键,值)

m.set('name','xiaolin03');//key:"name"  value:"xiaolin03"
m.set('hobby',function() {
console.log('我爱打代码');//key:"hobby" value:f()
})
let key = {
name:'xiaolin03'
}
m.set(key,['宁德','厦门','福州']);//key:{name:"xiaolin03"} value:['宁德','厦门','福州'] ====>键:对象 值:数组

【元素个数】:获取元素个数 对象.size

console.log(m.size);//3

【删】:删除 对象.delete(键)

m.delete('name');

【取】:获取 对象.get(键)

m.get('hobby');
m.get(key);

【遍历】:for…of遍历

for(let v of m) {
console.log(v);//v是一个数组,第一个元素是键,第二个元素是值
}

【清空】:对象.chear();

m.clear();//清空对象

class类

ES6提供了更接近传统语言的写法,引入了Class(类)这个概念。作为对象的模板。通过class关键字可以定义类。基本上,ES6的class可以看做只是一个语法糖,他的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

class Phone {
//构造方法 名字不能修改(自动执行,使用new Person()时自动调用该方法)
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
//方法必须使用该语法 不能使用ES5的对象完整形式 call:fuction(){} <== 错误!!
call() {
console.log("我可以打电话");
}
}
let onPlus = new Phone("1+",1999);//实例化对象,自动调用constructor方法

静态成员

静态成员static标注的成员,属于类,而不属于函数对象 ====> 得用类名.成员名 来调用该属性

class Phone {
//静态属性
static name = "手机";
static change() {
console.log("我可以改变世界");
}
}
let huawei = new Phone();
console.log(huawei.name);//undefined;
console.log(Phone.name);//手机

类继承

//父类
class Phone {
//构造方法
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
//父类的成员属性
call() {
console.log("打电话");
}
}
//子类
class SmartPhone extends Phone {
//构造方法
constructor(brand,price,color,size) {
surper(brand,price);//surper()就是父类的constructor()方法
this.color = color;
this.size = size;
}
photo() {
console.log("拍照");
}
playGame() {
console.log("玩游戏");
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
xiaomi.call();
xiaomi.photo();
xiaomi.playGame();

子类对父类方法的重写

重写:在子类声明一个和父类重名的方法,从而对父类的功能进行改进。当调用这个方法时,调用的是子类的方法。

//父类
class Phone {
//构造方法
constructor(brand,price) {
this.brand = brand;
this.price = price;
}
//父类的成员属性
call() {
console.log("打电话");
}
}
//子类
class SmartPhone extends Phone {
//构造方法
constructor(brand,price,color,size) {
surper(brand,price);//surper()就是父类的constructor()方法
this.color = color;
this.size = size;
}
//子类重写父类call()方法
call() {
//无法在子类里去调用父类里的call()方法!!
console.log("打视频、聊天");
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
xiaomi.call();//"打视频、聊天"

ES6里,无法在子类里去调用父类中和他同名的那个方法,只能在子类里写。

getter和setter设置

对象的属性进行方法的绑定
get:对于对象的动态属性进行封装
set:添加更多的控制和判断

class Phone {
get price() {
console.log("价格属性被读取了");
return 'helloworld';
}
set price(newVAl) {
console.log("价格属性被修改了");
}
}
//实例化对象
let s = new Phone();
console.log(s.price());
s.price = 'free';

数值扩展

  1. Number.EPSILON 是 JavaScript 表示的最小精度
    • EPSILON 属性的值接近于 2.22E-16
    • 主要用于浮点数运算(浮点数运算经常会有误差)
console.log(0.1+0.2);//0.3000000000000000000000004
console.log(0.1 + 0.2 === 0.3);//false
function equal = (a,b) => {
if(Math.abs(a-b)<Number.EPSILON) {
return true;
}else {
return false;
}
}
console.log(equal(0.1+0.2,0.3));//true
  1. 二进制和八进制
let b = 0b1010;//二进制 0b开头 
console.log(b);//10
let o = 0o777;//八进制 0o开头
console.log(o);//511
let x = 0xff;//十六进制 0x开头
console.log(x);//255
  1. Number.isFinite 检测一个数值是否为有限数
console.log(Number.isFinite(100));//true
console.log(Number.isFinite(Infinity));//false
console.log(Number.isFinite(100/0));//false
  1. Number.isNaN 检测一个数值是否为NaN
console.log(Number.isNaN(123));//false
  1. Number.parseInt Number.parseFloat 字符串转整数
console.log(Number.parseInt("520Love"));//520
console.log(Number.parseFloat("5.20Love"));//5.20
  1. Number.isInteger 判断一个数是否为整数
console.log(Number.isInteger(4));//true
console.log(Number.isInteger(4.4));//false
  1. Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.5));//3
  1. Math.sign 判断一个数到底为正数、负数还是零
console.log(Math.sign(100));//1
console.log(Math.sign(0));//0
console.log(Math.sign(-100));//-1

对象方法扩展

  1. Object.is(a, b)判断两个值是否完全相等
console.log(Object.is(120,120));//true
console.log(Object.is(NaN,NaN));//true
console.log(NaN===NaN);//false
  1. Object.assign(a, b)对象的合并
const config1 = {
host: 'localhost',
port:3306,
name:'root',
pass:'root',
text:'text'
};
const config2 = {
host:'http://xiaolin03.com',
port:33060,
name:'xiaolin03.com',
pass:'cmb',
text2:'text2'
};
console.log(Object.assign(config1,config2));
//如果对象里面参数重名host、port、name、pass,后面一个参数会把前面一个参数覆盖掉;
//如果前面一个对象的属性后面一个对象没有text,那么以前面一个对象为准;
//如果后面一个对象的属性前面一个对象没有text2,那么不会覆盖掉。
  1. Object.setPrototypeOf 设置原型对象 Object.getPrototypeOf
const school = {
name:"学校名"
}
const cities = {
xiaoqu:['北京','上海','深圳']
}
Object.setPrototypeOf(school,cities);//为school对象设置原型
console.log(school);

不建议这么做(效率低)

模块化

将一个大的程序文件,拆分成许多小的文件(模块),然后将小文件组合起来。
【好处】:

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性
    【语法】:模块功能主要由两个命令构成:export和import
  4. export命令用于规定模块的对外接口
  5. import命令用于输入其他模块提供功能
//m1.js文件:
//希望这个数据能够被的模块使用,就在前面加上export,他模块就可以通过import引入这个数据
export let school = '学校名';
export function teach() {
console.log("heloworld");
}
//index.html文件:
<script type="module">
//引入m1.js模块内容
import * as m1 from './m1.js';//通用方式===>index.html这个文件就把m1.js文件里所有暴露数据存储到m1数据里了
console.log(m1);
</script>

【暴露数据语法汇总】:

  1. 分别暴露(在每个需要暴露的数据前面加上export) m1.js
export let school = '学校名';
export function teach() {
console.log("heloworld");
}
  1. 统一暴露(在某个地方直接用export,用对象简化写法格yin) m2.js
let school = '学校名';
function teach() {
console.log("heloworld");
}
export {school,teach};
  1. 默认暴露(对于这种方法,在调用的时候也需要上default 【例】m1.default.change()) m3.js
//格式:export default + {要暴露的数据}
export default {
school:"学校名",
change:function() {
console.log("heloworld");
}
}

【引入模块数据语法汇总】:

  1. 通用的入方式
<script type="module">
import * as m1 from './m1.js';
</script>
  1. 解构赋值形式
import {school,teach} from "./m1.js";
import {school as guigu,teach as teaching} from "./m2.js";//如果需要引入的数据重名的话,可以使用别名方式
import {default as m3} form "./m3.js";//对于默认暴露的方式用解构赋值,不能直接使用default
  1. 简便形式(只能针对默认暴露)
import m3 from "./m3.js";//直接跟变量(不写*和{})