1. 基础语法
1.1 变量
- 变量需要声明和赋值,声明和赋值可以同时进行(未赋值的变量类型都是undefined)
- 有五种基本数据类型:
String
、Number
、Boolean
、Undefined
、Null
- 复杂数据类型都是Object类型
typeof 变量或常量
:查看变量或常量的数据类型- 基本类型的比较
都是值比较
===
:当类型相同取值也相同的时候才会返回为true(不会自动类型转换)- 全局变量在函数外/类外声明,如果在函数中直接对一个变量赋值,则这个变量也是全局变量。
对象和函数都是引用数据类型
1.2 类型转换
var ret = Number(arg)
- 作用:将非数值变量转换为数值变量
- arg:可以是数字字符串,布尔,null和undefined
var ret = parseInt(str)
- 作用:将字符串转换为整数
- str:字符串
var ret = parseFloat(str);
- 作用:将字符串转换为浮点数
- str:字符串
var ret = Boolean(arg);
- 作用:将其他数据类型转换为Boolean类型
- arg:可以是数字类型、字符串、null、undefined、Object
1.3 控制语句与运算符
- 控制语句几乎跟java没啥区别,不写了
===
类型要相同,取值也要相同(不会自动类型转换)!==
不全等运算- 基本类型的比较是值比较
2. 对象(es6之前)
2.1 对象的两种创建方式
var person = new Object();
// 给对象增加属性
person.name = "孙悟空";
person.age = 18;
// 创建对象时给其添加属性
var person = {
name: "孙悟空",
age: 18
};
2.2 访问、删除、遍历对象属性
obj.name
:访问属性方式一obj["name"]
:访问属性方式二delete obj.name
:删除属性方式for (var 变量 in 对象) {}
:对象元素的遍历
3. 函数
3.1 函数的创建
function fun1(参数列表) {
语句...
}
// 匿名函数
var fun1 = function(参数列表) {
语句....
}
3.2 构造函数
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
// 设置对象的方法
this.sayName = function () {
console.log(this.name);
};
}
- 构造函数和普通函数的定义没有啥区别
- 构造函数首字母一般
大写
,调用构造函数需要用new
关键字来生成对象。 - 构造函数里面可以通过
this
来表示新建对象 - 通过构造函数名添加的属性为
静态属性
,只能通过构造函数名来访问
Person.nation="中国";
console.log(Person.nation);
3.3 this在不同函数里代表什么
- 当以
函数
的形式代用时,this代表window - 当以
方法
的形式调用时,哪个对象调用的就代表谁 - 当以
构造函数
的形式调用时,this代表新创建的那个对象
4. 原型
- 每个构造函数都会有一个prototype属性,它是一个对象类型
- prototype属性为原型对象,这个对象里面需要有一个属性叫
constructor
,并且要指向当前构造函数
- prototype属性为原型对象,这个对象里面需要有一个属性叫
- 每个对象都会有原型(
__proto__
),原型也是一个对象,指向的是构造函数中的prototype - 因此原型会形成一个原型链
- Object对象是所有对象的祖先,Object的原型对象指向为null
- 如果一个构造器没有指定prototype属性,则原型默认就是Object的实例对象
- 一个对象的原型链上的属性和方法对这个对象来说都是可见的
综上,es6之前通过构造函数与原型实现了类与继承等概念
// 使用构造函数来创建对象
function Person(name, age) {
// 设置对象的属性
this.name = name;
this.age = age;
}
// 在Person类的原型对象中添加方法
Person.prototype.sayName = function() {
console.log(this.name);
};
var person1 = new Person("孙悟空", 18);
var person2 = new Person("猪八戒", 19);
var person3 = new Person("沙和尚", 20);
person1.sayName();
person2.sayName();
person3.sayName();
4.1 对象的内置的方法
toString()
:继承自Object对象hasOwnProperty(name)
:查看当前对象是否拥有某个属性或方法(而非原型链上)
5. 对象继承(es6之前)
5.1 原型链继承
// 定义父类型构造函数
function SupperType() {
this.supProp = 'Supper property';
}
// 给父类型的原型添加方法
SupperType.prototype.showSupperProp = function () {
console.log(this.supProp);
};
// 定义子类型的构造函数
function SubType() {
this.subProp = 'Sub property';
}
// 创建父类型的对象赋值给子类型的原型
SubType.prototype = new SupperType();
// 将子类型原型对象的构造属性设置为子类构造函数
SubType.prototype.constructor = SubType;
// 给子类型原型对象添加方法
SubType.prototype.showSubProp = function () {
console.log(this.subProp)
};
// 创建子类型的对象: 可以调用父类型的方法
var subType = new SubType();
subType.showSupperProp();
subType.showSubProp();
- 实现原理是:
子类的原型对象是父类型的一个实例对象
- 特点:
- 子类多个实例对象的原型指向同一个原型对象,一个子类对象修改了原型属性,其他所有子类对象的原型属性也会被修改
- 不能传递参数
- 基础单一
5.2 构造函数继承
// 定义父类型构造函数
function SuperType(name) {
this.name = name;
this.showSupperName = function () {
console.log(this.name);
};
}
// 定义子类型的构造函数
function SubType(name, age) {
// 在子类型中调用call方法继承自SuperType
SuperType.call(this, name);
this.age = age;
}
// 给子类型的原型添加方法
SubType.prototype.showSubName = function () {
console.log(this.name);
};
// 创建子类型的对象然后调用
var subType = new SubType("孙悟空", 20);
subType.showSupperName();
subType.showSubName();
console.log(subType.name);
console.log(subType.age);
- 原理:使用call将父类构造函数引入子类构造函数中
5.3 组合继承
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function (name) {
this.name = name;
};
function Student(name, age, price) {
Person.call(this, name, age); // 为了得到父类型的实例属性和方法
this.price = price; // 添加子类型私有的属性
}
Student.prototype = new Person(); // 为了得到父类型的原型属性和方法
Student.prototype.constructor = Student; // 修正constructor属性指向
Student.prototype.setPrice = function (price) { // 添加子类型私有的方法
this.price = price;
};
var s = new Student("孙悟空", 24, 15000);
console.log(s.name, s.age, s.price);
s.setName("猪八戒");
s.setPrice(16000);
console.log(s.name, s.age, s.price);
- 原理:原型链+借用构造函数的组合继承
6. 常用对象
6.1 数组对象
var arr=new Array();
arr.push(a1,a2)
:尾部追加多个元素arr.pop()
:弹出最后一个元素arr.unshift(a1,a2)
:头部追加多个元素arr.shift()
:弹出头部一个元素arr.slice(1,4)
:切片(起始位置,结束位置)不更改原数组arr.reverse()
:翻转数组arr.sort()
:排序数组arr.join(" ")
:按空格将数组拼接成字符串arr.concat(arr1,arr2);
:将多个数组拼接成一个数组返回arr.length
:数组长度[...arr1,...arr2]
:也可以实现两个数组拼接
数组遍历
var arr=[1,2,3];
// 第三个参数可以不要
arr.forEach(function(value,index,array){
console.log(value);
})
// 遍历方式2
const xiyou=['唐僧','孙悟空','猪八戒','沙僧'];
for(let v in xiyou){
console.log("v 是index");
}
for(let v of xiyou){
console.log("v 是value");
}
filter()
var arr=[1,2,3,4,5,6,7];
// 第三个参数可以不要
var arr1=arr.filter(function(value,index,array){
return value>=3;
})
some()
:查找是否存在满足条件的元素
var arr=[10,30,4];
var isExist=arr.some(function(value){
return value>=20;
})
map()
var arr=[10,30,4];
var arr1=arr.map(function(value){
return value*20;
})
6.2 函数对象
函数本身也是一个对象,函数的构造函数时Function
函数的调用方式
// 普通函数: this指向window
fn();
// 作为对象的方法:this指向obj
obj.fn();
// 构造函数:this指向新创建的对象
new Fn();
// 绑定事件函数:this指向btn
btn.onclick=function(){};
// 定时器函数:this指向window
setInterval(function(){},1000);
// 立即执行函数:this指向window
(function(){})();
call()
:改变了this指向
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.call(obj, 2, 3);
apply()
:会将参数封装在一个数组中,改变了this指向
function fun(a, b) {
console.log("a = " + a);
console.log("b = " + b);
console.log("fun = " + this);
}
var obj = {
name: "obj",
sayName: function () {
console.log(this.name);
}
};
fun(2, 3);
console.log("===============");
fun.apply(obj, [2, 3]);
bind()
: 返回一个新的函数,可以改变绑定的this
var obj={
name:"andy"
};
function fun(){
console.log(this);
}
var fun1=fun.bind(obj);
fun1();
6.3 Date对象
var date=new Date();
date.getFullYear();
date.getMonth();
date.getDate()
date.getHours()
date.getMinutes()
date.getSeconds()
date.getMilliseconds()
6.4 Math工具类
Math.abs(1)
Math.ceil(1.1)
Math.floor(1.1)
Math.round(1.1)
Math.round(Math.random()*10)
Math.pow(12,3)
Math.sqrt(16)
6.5 String对象
str.charAt(1)
str.charCodeAt(1)
str.concat("你好","世界")
:拼接多个字符串str.indexof("o")
str.lastIndexof("o")
str.slice(0,5)
str.substring(0,5)
str.substr(0,5)
str.split(" ")
str.toUpperCase()
str.toLowerCase()
6.7 浅拷贝与深拷贝
var obj={
id=1,
name:"andy",
msg:{
age:18;
}
}
var o={};
// 浅拷贝
Object.assign(o,obj);
- 深拷贝自己加实现逻辑
7. Exception
7.1 Error对象属性
name
:设置或返回错误名message
:错误消息
try {
// 可能发生异常的代码
} catch (error) {
// 发生错误执行的代码
} finally {
// 无论是否出错都会执行的代码
}
7.2 抛出异常
/*该函数接收一个数字,返回它的平方。*/
function foo(num) {
if (typeof num == "number") {
return num * num;
} else {
throw new TypeError("您输入的是一个非法数字!")
}
}
console.log(foo(4));
console.log(foo("abc"));
7.3 自定义异常
/*自定义错误*/
function MyError(message) {
this.message = "注意:这是自定义的错误"
this.name = "自定义错误";
}
MyError.prototype = new Error();
try {
throw new MyError("注意:这是自定义错误类型")
} catch (error) {
console.log(error.message)
}
8. 闭包
- 当一个嵌套的内部函数引用了嵌套的外部函数的变量,就产生了闭包
- 闭包的作用
- 可以读取函数内部的变量
- 让这些变量的值始终保存在内存中
8.1 闭包的演示
function fun1() {
var a = 2;
function subFun() {
a++;
console.log(a);
}
// 返回值是子函数
return subFun;
}
var f1 = fun1();
f1();
f1();
- 通过当前函数调用返回
子函数
,保证当前函数中的变量保存在内存中不会被清除
8.2 闭包的生命周期
- 产生:在嵌套内部函数定义执行完就产生了(不是在调用)
- 死亡:当嵌套的内部函数成为垃圾对象时就死亡了
function fn1() {
//此时闭包就已经产生了(函数提升, 内部函数对象已经创建了)
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn1();
f(); // 3
f(); // 4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)
8.3 闭包的应用
- 具有特定功能的js文件
- 将所有的数据和功能都封装在一个函数内部(私有的)
- 只向外暴露一个包含n个方法的对象或函数
- 而模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
方式一
function myModule() {
//私有数据
var msg = 'Hello, World';
//操作数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase());
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase());
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
// 其他文件
var module = myModule();
module.doSomething();
module.doOtherthing();
方式二
(function (window) {
//私有数据
var msg = 'Hello, World';
//操作数据的函数
function doSomething() {
console.log('doSomething() ' + msg.toUpperCase());
}
function doOtherthing() {
console.log('doOtherthing() ' + msg.toLowerCase());
}
//向外暴露对象(给外部使用的方法)
window.myModule = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})(window);
//在页面中
myModule.doSomething();
myModule.doOtherthing();
9. es6新特性
9.1 let关键字
- 用于声明一个变量
let a=1
- 不允许重复声明
- 支持块级作用域
- 不存在变量提升
- 不影响作用域链
9.2 const 关键字
- 用于声明一个常量
const MAX=100
- 不允许重复声明
- 支持块级作用域
- 声明时必须赋值
- 值不可修改
- 标识符一般大写
9.3 数组和对象支持析构解构
数组
const arr = ["张学友", "刘德华", "黎明", "郭富城"];
let [zhang, liu, li, guo] = arr;
对象
//对象的解构赋值
const lin = {
name: "林志颖",
tags: ["车手", "歌手", "小旋风", "演员"]
};
let {name, tags} = lin;
let wangfei = {
name: "王菲",
age: 18,
songs: ["红豆", "流年", "暧昧"],
history: [
{name: "窦唯"},
{name: "李亚鹏"},
{name: "谢霆锋"}
]
};
let {name, age, songs: [one, two, three], history: [first, second, third]} = wangfei;
9.4 模板字符串
let name = '小可爱';
let result = `欢迎${name}访问我的文章`;
- 可以使用
${变量名}
形式输出变量 - 字符串中支持换行符
9.5 对象支持简写
let name = "张三";
let age = 18;
let speak = function () {
console.log(this.name);
};
//属性和方法简写
let person = {
name,
age,
speak
};
9.6 箭头函数
- 通过箭头定义函数
let fn = (arg1, arg2, arg3) => {
return arg1 + arg2 + arg3;
}
let fn = num => {
return num * 10;
};
let fn = score => score * 20;
- 箭头中的this 指向
声明时
所在作用域中 this 的值
9.7 不定参数
// 作用与 arguments 类似
function add(...args) {
console.log(args);
}
add(1, 2, 3, 4, 5);
- rest 参数必须是最后一个形参
9.8 展开运算符(伪数组)
对数组的展开
// 展开数组
let tfboys1 = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
let tfboys2 = ["德玛西亚之力", "德玛西亚之翼", "德玛西亚皇子"];
let tfboys=[...tfboys1,...tfboys2];
对对象的展开
// 展开对象
let skillOne = {
q: "致命打击"
};
let skillTwo = {
w: "勇气"
};
let skillThree = {
e: "审判"
};
let skillFour = {
r: "德玛西亚正义"
};
let gailun = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
9.9 class
- 类本质其实还是函数,它就是一个语法糖
- class:声明类
- constructor:定义构造函数初始化
- extends:继承父类
- supper:调用父类构造函数
- static:定义静态属性和方法
//父类
class Phone {
//构造方法
constructor(brand, color, price) {
this.brand = brand;
this.color = color;
this.price = price;
}
//对象方法
call() {
console.log("我可以打电话!!!")
}
}
//子类
class SmartPhone extends Phone {
constructor(brand, color, price, screen, pixel) {
super(brand, color, price);
this.screen = screen;
this.pixel = pixel;
}
//子类方法
photo() {
console.log("我可以拍照!!");
}
playGame() {
console.log("我可以玩游戏!!");
}
//方法重写
call() {
console.log("我可以进行视频通话!!");
}
//静态方法
static run() {
console.log("我可以运行程序")
}
static connect() {
console.log("我可以建立连接")
}
}
//实例化对象
const Nokia = new Phone("诺基亚", "灰色", 230);
const iPhone6s = new SmartPhone("苹果", "白色", 6088, "4.7inch", "500w");
//调用子类方法
iPhone6s.playGame();
//调用重写方法
iPhone6s.call();
//调用静态方法
SmartPhone.run();
get和set
class Phone{
get price(){
console.log("价格属性被读取了");
return 10;
}
set price(newVal){
console.log("价格被修改了");
}
}
9.10 Symbol数据类型
- Symbol的值是唯一的(动态值,但唯一),用来解决命令冲突的问题
- Symbol值不能与其他数据进行运算
- Symbol的作用
是给对象添加属性和方法
Symbol的创建
let s=Symbol();
let s1=Symbol("张三");
let s2=Symbol("张三");
console.log(s1===s2); // false
let s3=Symbol.for("张三");
let s4=Symbol.for("张三");
console.log(s3===s4); // true
给对象添加属性或方法
//在方法中使用 Symbol
let game = {
name: "狼人杀",
[Symbol('say')]: function () {
console.log("我可以发言")
},
[Symbol('zibao')]: function () {
console.log('我可以自爆');
}
};
// 方式二
let game={
省略
}
let methods={
up:Symbol(),
down:Symbol()
};
game[methods.up]=function(){
console.log("我可以向上")
}
game[methods.down]=function(){
console.log("我可以向下")
}
9.11 迭代器
- 内置迭代器的对象类型有
Array
、Arguments
、Set
、String
、TypedArray
、NodeList
- es6提供了一种新的遍历
for ... of
循环,迭代器主要提供for…of消费
如何获取对象的迭代器
//声明一个数组
const xiyou = ["唐僧", "孙悟空", "猪八戒", "沙僧"];
//使用 for...of 遍历数组
for (let v of xiyou) {
console.log(v);
}
console.log("===============");
//获取迭代器对象
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每次调用next方法会返回一个包含value和done属性的对象
如何自定义一个迭代器
//声明一个对象
const banji = {
name: "五班",
stus: [
"张三",
"李四",
"王五",
"小六"
],
[Symbol.iterator]() {
//索引变量
let index = 0;
let _this = this;
return {
next: function () {
if (index < _this.stus.length) {
const result = {value: _this.stus[index], done: false};
//下标自增
index++;
//返回结果
return result;
} else {
return {value: undefined, done: true};
}
}
};
}
}
//遍历这个对象
for (let v of banji) {
console.log(v);
}
- 通过闭包实现
9.12 生成器函数
定义生成器函数与访问生成器
function * gen() {
/*代码1开始执行*/
console.log("代码1执行了");
yield "一只没有耳朵";
/*代码2开始执行*/
console.log("代码2执行了");
yield "一只没有尾巴";
/*代码3开始执行*/
console.log("代码3执行了");
return "真奇怪";
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log("===============");
//遍历
for (let v of gen()) {
console.log(v);
}
生成器参数
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 iterator = gen('AAA');
console.log(iterator.next());
//next方法可以传入实参
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));
模拟获取用户数据,订单数据,商品数据
function getUsers() {
setTimeout(() => {
let data = "用户数据";
iterator.next(data);
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data);
}, 1000);
}
function * gen() {
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
//调用生成器函数
let iterator = gen();
iterator.next();
9.13 Promise
- Promise是一个构造函数
基本使用
//实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
setTimeout(function () {
if(远程调用成功){
resolve(data);
}else{
reject(err)
}
}, 1000);
});
//调用 promise 对象的 then 方法
p.then(function (value) {
console.log(value);
}, function (reason) {
console.error(reason);
});
使用案例
// 接口地址: 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);
});
支持链式调用
//创建 promise 对象
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
}, 1000)
});
//链式调用+箭头函数
p.then(value => {
console.log(value);
return value;
}).then(value => {
console.log(value);
});
支持catch异常
const p = new Promise((resolve, reject) => {
setTimeout(() => {
//设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!");
}, 1000);
});
p.catch(function (reason) {
console.error(reason);
});
9.14 Set集合
size
add()
delete()
has()
clear()
//创建一个空集合
let s = new Set();
//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);
//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());
9.15 Map集合
size
set(key,value)
get(key)
has(key)
clear()
//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map([
["name", "张三"],
["gender", "女"]
]);
//属性和方法
//获取映射元素的个数
console.log(m2.size);
//添加映射值
console.log(m2.set("age", 6));
//获取映射值
console.log(m2.get("age"));
//检测是否有该映射
console.log(m2.has("age"));
//清除
console.log(m2.clear());
9.16 对象扩展
Object.is(obj1,obj2)
:很像===
console.log(Object.is(120, 120));// true
console.log(Object.is(NaN, NaN));// true
console.log(NaN === NaN);// false
Object.assign(obj1,obj2)
:用obj2去覆盖obj1,有重名属性则覆盖,没有的属性则添加
const config1 = {
host: "localhost",
port: 3306,
name: "zhangsan",
pass: "root",
test1: "test1"
};
const config2 = {
host: "127.0.0.1",
port: 3309,
name: "lisi",
pass: "root",
test2: "test2"
}
console.log(Object.assign(config1, config2));
Object.setPrototypeOf(obj)
与Object.getPrototypeOf(obj,原型obj)
const school = {
name: "MySchool"
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"]
};
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);
9.17 模块化
模块化的暴露
- m1.js
//方式一:分别暴露
export let school = "华北理工大学";
export function study() {
console.log("我们要学习!");
}
- m2.js
//方式二:统一暴露
let school = "华北理工大学";
function findJob() {
console.log("我们要找工作!");
}
export {school, findJob};
- m3.js
//方式三:默认暴露
export default {
school: "华北理工大学",
change: function () {
console.log("我们要改变自己!");
}
}
模块化的导入
// 引入 m1.js 模块内容
import * as m1 from "./m1.js";
// 引入 m2.js 模块内容
import * as m2 from "./m2.js";
// 引入 m3.js 模块内容
import * as m3 from "./m3.js";
m1.study();
m2.findJob();
m3.default.change();
- 解构赋值形式
// 引入 m1.js 模块内容
import {school, study} from "./m1.js";
// 引入 m2.js 模块内容
import {school as s, findJob} from "./m2.js";
// 引入 m3.js 模块内容
import {default as m3} from "./m3.js";
console.log(school);
study();
console.log(s);
findJob();
console.log(m3);
m3.change();