JavaScript(ES6)
作用域
let
块级作用域,暂时性死区(不进行变量提升,在作用域头部到声明以前,不允许使用),特例:for循环的小括号和花括号都是一个新的作用域
const
原始类型值不允许修改 引用类型地址不允许修改
立即执行函数简写,一对花括号
解构赋值
数组
let [a,[[b],c]] = [1,[[0],2]];
console.log(a,b,c);
let [,,third] = [1,2,3];
console.log(third);
let [head,...tail] = [1,2,3,4,5];
console.log(head,tail);
//默认值,变量值是 === undefined的话才会使用默认值,默认值可以是变量函数等
let [a,b,c = 6] = [1,2,undefined];
console.log(a,b,c);
对象
// 对象匹配,按照key匹配,key可以相同,多次匹配
let {a,b} = {a:1, b:2};
console.log(a,b);
//默认值
let {a = 3} = {};
console.log(a) // a = 3
字符串
//字符串结构赋值
let [a,b,c] = '123';
console.log(a,b,c);
包装类
//包装类解构赋值
let {toString : s} = 123;
console.log(s === Number.prototype.toString);
函数
//函数参数的解构赋值
function f([a,b]){
return a+b;
}
console.log(f(['hello', ' world']));
function f2({a = 0,b = 0} = {}){
console.log(a,b);
}
f2({a:1,b:2});
f2({a:1});
f2(); //传入undefined,如果解构赋值 = {} 就是在不传参时有效,否则报错
从模块或者API中获取数据
//从模块或者API中获取数据
const {PI,cos,sin} = Math;
console.log(PI,cos,sin);
特例
// 特例
let [a] = [1];
let b;
({b} = {b:1});
//数组当对象,数组下标当key
let {0:a,1:b} = [1,2];
console.log(a,b);
拓展
字符串拓展
新增API
string.includes() //是否包含某个子串
string.startWith() / string.endWith() //是否以xx开头结尾
string.repeat(次数) //字符串重复多少次
string.padStart(num,str) / string.padEnd(num,str) //把字符串以str补充至要求的位数
模板字符串
数值拓展
进制表示法
二进制 :0bxxx 八进制 :0oxxx 十六进制 :0xaaa
最小常量
Number.EPSILON (如果两个数之间的差值小于该最小常量,则可以认为两个数相等
可以用作判断 0.1 + 0.2 === 0.3 等问题)
安全整数
判断安全整数 Number.isSafeInteger()
最大安全整数 Number.MAX_SAFE_INTEGER
最小安全整数 Number.MIN_SAFE_INTEGER
内置对象Math拓展
Math.trunc() //去掉小数部分
Math.cbrt() //开立方根
Math.hypot() //对所有数据求平方和再开方
Math.expml( x ) // 对数 返回的是 e^x - 1
Math.log1p( x ) //对数 返回的是 log(1 + x)
Math.log10(x) //以10为底的对数
Math.log2(x) //以2为底的对数
sinh()、cosh()、tanh()、asinh()、acosh()、atanh() //双曲函数
指数运算 Math.pow(x,y) 简写 => Math.pow(x ** y)
函数拓展
箭头函数
函数体只有一行语句,不用加花括号,且return可省略,如有多行语句,照旧
箭头函数不初始化this,this是在箭头函数定义时确定,不是在调用时确定
箭头函数不能使用arguments对象,可以用rest(…args)参数代替
利用不断返回对象嵌套实现管道机制
函数形参
函数参数默认值注意的两个点:
1、如果传值,但是这个等价于false,此参数一定会被覆盖
2、不利于未来代码优化
//理由:空字符串等价于0(false),所以会把y变成2
function create(x,y){
x = x || 1;
y = y || 2;
console.log(x,y);
}
create('hello',''); // hello 2
//理由:传入的参数不是undefined,所以默认值无效
function create2(x = 1,y = 2){
console.log(x,y);
}
create2('hello',''); // hello _
不能声明和形参同名的变量
当形参有了默认值以后,形参不能重复声明
函数形参小心暂时性死区 -> 自己等于自己,自己等于块作用域里声明的变量,忽视声明顺序颠倒等于 也是不行的
函数形参按顺序读入,如果想要跳过某个参数给后面参数传参,只能填null、undefined
函数形参默认值的计算时惰性的
let x = 9;
function f4(y = x + 1){
console.log(y);
}
x = 10;
f4(); // 11
函数的属性
函数的length:函数需要的必须参数的个数
//如果遇到有默认值的参数,则后面的所有忽略不计
console.log((function(a,b,c){}).length); // 3
console.log((function(a,b,c = 0){}).length); // 2
console.log((function(a,b,c = 0,d){}).length); // 2
函数的name
function f5(){};
let f6 = function(){};
let f7 = (function(){}).bind({});
console.log(f5.name); // f5
console.log(f6.name); // f6
console.log(f7.name); // bound _
this绑定
call(this,item,item)
apply(this,[item,item])
bind(this)(item,item)
数组拓展
拓展运算符(…)可展开可收拢
数组可以对字符串解构赋值,所以字符串也可以使用拓展运算符拓展成数组
let array = [...'hello'];
console.log(array); // ['h','e','l','l','o']
Array.from(target,自动调用map回调,绑定this) 把类数组转换成真正的数组,可以接收类数组,有length的对象,set,map
let obj = {
'0':1,
'1':2,
'2':3,
length:3
}
let array2 = Array.prototype.slice.call(obj);
let array3 = Array.from(obj);
Array.of()把零散的数据变成数组
let a = 5;
let arr1 = [1,2,3];
let arr2 = new Array(1,2,3);
let arr3 = Array.of(1,2,3);
let arr4 = Array.of(a); // 【5】
console.log(arr3);
实例的find(回调)、findindex(回调),回调有三个参数,返回第一个满足条件的数据
实例的fill(data,start,end) 填充数据
//该函数有三个参数。
arr.fill(value, start, end)
//value:填充值。
//start:填充起始位置,可以省略。
//end:填充结束位置,可以省略,实际结束位置是end-1。
var arr = ['0','1','2','3','4'];
arr.fill(1) // => 所有数据都要初始化为1 [1,1,1,1,1]
arr.fill('填进去',3) // 从索引3开始填充数据 [0, 1, 2, "填进去", "填进去"]
arr.fill('新值',2,4) // 从索引2开始到索引4之前 [0, 1, "新值", "新值", 4]
实例的keys()、values()、entries()循环遍历
let arr6 = [1,2,3,4];
let entries = arr6.entries();
console.log(entries.next());
分别对应下标和值,可以一步步走下去
实例的includes() 数组是否包含某个元素 : 比indexOf好的地方:语义化 、 可以处理NAN
对象拓展
属性名表达式
Object.js()判断相等不相等,== ===多少有点问题
Object.assign(target,source,source…)
只会拷贝本身,不会拷贝prototype上的属性
拷贝是浅拷贝,不是深拷贝
如果有同名属性,后面覆盖前面
target如果不是对象,是string、number、boolean,会先转化为包装类
如果target是undefined或者null,无法转换成包装类,报错
如果source是string,可以计算(把字符串拆开),否则没有影响
应用:
应用于构造函数
class point {
constructor(x,y){
// this.x = x;
// this.y = y;
Object.assign(this,{x,y});
}
}
console.log(new point(1,2));
在原型上添加函数
Object.assign(point.prototype,{
f(){
console.log(111);
}
})
不能用对像解构赋值的时候,用这个API接
Object.keys(),.Object.values(),Object.entries() : 这类都是使用for of
对象拓展运算符(…) : 可以像数组一样展开收拢
数据结构
Symbol
ES6新增的数据类型,创建一种独一无二不可改变的数据类型,主要解决防止全局变量冲突问题
BigInt
ES6新增的数据类型,是一种数字类型的数据,可以存储任意精度格式的整数,使用Bigint可以安全存储和操作大整数,即时这个数超出了Number的安全整数范围
Set
属性和方法
add(value),返回set实例,所以可以连续链式调用
delete(value)删除返回true/false
has(value)是否包含某个值
clear()清除所有成员
size 一共有多少成员
遍历
keys() / values() / entries() / foreach
对于 set 数据结构 ,key和value都是value的值, keys和values的遍历方法拿出来的值都是一样的,是value , 所以可以简写成 for(let item of s){} 所以不论哪种循环,全都是value
属于数组的方法,转换成数组进行遍历 : map、filter、Array.from等
运用set来回切换array运用filter实现并集、交集、差集
Map
定义 : Map和object都是键值对,区别在于object的键会强行转换成字符串,而Map没有这个要求
Map初始化
let array2 = [
['a',4],
['b',3]
]
1、嵌套的二维数组构造Map
console.log(new Map(array2));
2、嵌套数组的set构造Map
let s2 = new Set(array2);
console.log(new Map(s2));
Map转换成数组
let array2 = [
['a',4],
['b',3]
]
let map = new Map(array2);
console.log([...map.entries()]); // [Array,Array]
console.log([...map]); // [Array,Array]
console.log(Array.from(map));
当Map的key是字符串的时候,实现Map和Object的转换
1、Map转换为对象
function mapToObj(map){
let obj = {};
for(let [key,value] of map){
obj[key] = value;
}
return obj;
}
2、对象转换成Map
function objToMap(obj){
let map = new Map();
for(let key in obj){
map.set(key,obj[key]);
}
return map;
}
Map的key碰撞
增加[key,value],如果key是引用类型,看地址,如果地址一样将覆盖,如果是NaN自动用object.is判断,一样的话覆盖
常用属性和方法
set(key,value) 增加
get(key) 获取
delete(key)
has(key)
clear()
size
遍历
keys() / values() / entries() / foreach
entries() 使用这个遍历同时获取key,value的话,entries可以省略,直接写Map名即可
转换成数组进行遍历,Array.from/filter/map常用
总结:
快速获取key或者value值,map和set都可以 : […x.keys()] 类似,获得一个只包含key或者value的数组
WeakSet、WeakMap
1、跟Map/Set使用基本一致
2、区别:WeakSet所有的值必须是引用类型,WeakMap所有的key必须是引用类型
3、不会影响JS垃圾收集,而且不可遍历
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
let divs = document.querySelectorAll('div');
let map = new Map();
let data = {a:1,b:2};
//把DOM元素存到map数据结构中方便存取
map.set(divs[0],data);
//在map中存在引用,内存无法被释放,所以DOM元素无法自杀释放内存
divs[0].remove();
//又想方便存取,又想该释放就释放,只能使用WeakMap/WeakSet,因为不影响JS垃圾收集
let weakmap = new WeakMap();
weakmap.set(divs[0],data);
divs[0].remove();
4、作用:防止内存泄漏
Promise
解决回调地狱的问题
- 特点:
- 进行时 pending
- 已成功 fulfilled
- 已失败 rejected
- 最开始是pending状态
- 一种转化:从pending状态转换为fulfilled状态
- 另一种转化:pending状态转化为rejected状态
- 和事件的区别:事件在出发后绑定函数,不会触发,promise状态发生改变后绑定函数仍然会触发,promise无法取消
1、promise初始化步骤:
- 构造函数初始化,初始this.value this.reason resolve函数 reject函数
- executor函数是promise的构造函数参数,有两个参数(resolve,reject)
- executor是用户自定义的同步函数,是决定promise状态如何变化的函数,
- executor以同步或者异步的方式调用resolve/reject
- 如果调用resolve函数,promise由初始状态pedding -> fulfilled
- 如果调用reject函数,promise由初始状态pedding -> rejected
2、then
- then是异步的,作用是为promise对象状态改变所触发的回调函数 onFulfilled、onRejected
- resolve()/reject() 可以向 onFulfilled onRejected 传递参数
3、在executor函数中根据resolve/reject传入的参数决定当前promis对象的状态,从而决定then的调用抉择
- 情况1:如果调用resolve/reject传入的参数是原始类型或者非promise对象,promise的状态变为Fulfilled/Rejected
- 情况2:如果调用resolve/reject传入的参数是promise对象,那么,当前的promise对象一直保持pedding状态,
直到给入的参数promise对象状态发生改变,此时原promise的对象状态发生同样改变,并且拿到参数promise对象
的数据(与参数promise对象的状态一致)
4、then回调的返回,影响then所返回Promise对象的状态
- 对于任何一个then,都会返回一个新的promise对象,且与resolve/reject参数中的promise或者
onFulfilled、onRejected返回的promise不是同一个对象,是一个全新的Promise对象 - 情况1:onFulfilled/onRejected被调用,返回了非promise对象(有返回值,没返回值),
then返回的promise对象:状态变为Fulfilled,返回值会传递给then返回的promise对象 - 情况2:onFulfilled/onRejected被调用,返回了promise对象
then返回的promise对象:与回调返回的promise对象状态一致,回调返回的promise对象的数据 会传递给then返回的promise对象 - 情况3:then没有设定onFulfilled/onRejected,then返回的promise对象与调用then的promise对象状态一致,
会传递调用then的promise对象数据给then返回的promise对象
5、then的链式调用,根据第4点的三种情况决定then返回promise对象的状态
6、catch(onRejected) 等价于 then(null,onRejected)
best practice: 通常then只设定成功回调,在最后catch()处理失败回调
7、reject的三种方法(跟then只写成功回调,在最后执行catch类似,常用方法二三)
//方法一:
let p = new Promise((resolve,reject)=>{
reject('failed');
})
p.catch(err=>{
console.log(err);
})
//方法二:
let p = new Promise((resolve,reject)=>{
try{
throw new Error('failed');
}catch(e){
reject(e);
}
})
p.catch(err=>{
console.log(err);
})
//方法三:
let p = new Promise((resolve,reject)=>{
reject(new Error('failed'));
})
p.catch(err=>{
console.log(err);
})
8、Promise的API
promise.all([p1,p2,p3...])
返回值是新的promise对象
1.没有任何一个px是Rejected状态的,有PX是pending状态,则返回的promise状态是pending
2.所有px都是fulfilled状态 , 返回的promise对象是fulfilled
3.存在状态是Rejected的,返回的状态是Rejected
promise.rate([p1,p2,p3...])
返回值是第一个px的状态,并且获得它的数据
9、Promise.resolve()的使用
//1、Promise.resolve(原始类型)
//返回值:返回一个新的Promise对象,状态fulfilled,原始类型作为data返回
var p = Promise.resolve('failed');
p.then((data) => console.log(data));
//2、Promise.resolve(Promise)
// 返回值:就是传入的Promise对象
var p = new Promise((resolve,reject) => {
resolve('succeed');
});
var p2 = Promise.resolve(p);
console.log(p === p2); // true
//3、 Promise(thenable对象)
// thenable对象: 有一个then函数,其实是exector
// 返回值:使用then函数作为exector初始化新的promise对象
var thenable = {
then(resolve,reject){
resolve('succeeed');
}
}
var p = Promise.resolve(thenable);
p.then(data => console.log(data));
//4、Promise(非thenable对象)
// 返回值:返回一个新的promise对象,状态fulfilled, 非thenable对象作为data返回
var obj = {
name:'占山',
age: 15
}
var p = Promise.resolve(obj);
p.then(data => console.log(data));
10、Promise.reject()
//1、Promise.reject(原始类型)
//返回值:返回一个新的Promise对象,状态rejected,原始类型作为data返回
var p = Promise.reject('failed');
p.catch((data) => console.log(data));
11、promise串行
//完成20个任务,成功下一个,失败继续尝试
//串行
function perAction(){
return (new Promise((resolve, reject) => {
setInterval(() => {
Math.random() > 0.5 ? resolve() : reject();
}, 1000);
}))
}
function doAction(index,total){
if(index < total){
perAction().then(()=>{
console.log(`${index + 1} task is succeed!`);
index++;
doAction(index,total);
}).catch(()=>{
console.log(`${index + 1} task is failed!`);
doAction(index,total);
})
}
}
doAction(0,20);
promise并行
//并行完成20个任务,成功下一个,失败继续尝试
//配置:每一个任务
function perAction(i){
return (new Promise((resolve, reject) => {
Math.random() > 0.5 ? resolve() : reject();
}))
}
//开始并行
//配置:是否所有任务全部成功运行
function doActionSucceed(total){
//配置:任务失败 递归
function _taskReRun(i,callback){
let p = perAction(i);
p.then(()=> {
callback(i);
console.log(`${i} succeed`);
}).catch(() => {
_taskReRun(i,callback);
console.log(`${i} failed`);
});
}
return (new Promise((resolve,reject) => {
let map = new Map();
for(let i=0; i<total; i++){
//第二个参数传个箭头函数作为callback传给taskReRun函数
_taskReRun(i,i => {
map.set(i,true);
if(map.size == total){
resolve();
}
});
}
}))
}
doActionSucceed(20).then(data => console.log('all finish'));
12、async/await(串行)
- async 修饰函数定义
- 函数会天然返回promise对象,如果不是,会自动调用promise.resolve()
- 一旦一个函数使用async,那么调用它的方式一定是异步的
- 返回的promise对象:在async修饰的函数结束后一定是fulfilled,
如果任意一个await rejected ,那么返回的promise对象状态变为rejected
- await 在函数调用前
- 被调用函数应当返回一个promise对象
- 在函数内部,await会阻塞函数的执行,直到被调用函数返回的promise状态发生变化
async await 实现串行
//串行,如果成功,持续串行,如果失败,跳出
//配置:随机成功或者失败
function randomPromise(task){
return (new Promise((resolve,reject) => {
console.log(`------------task ${task} ----------------`);
Math.random() > 0.5 ? resolve(`task ${task} succeed`) : reject(`task ${task} failed`);
}))
}
//配置:串行,失败即跳出
async function returnPromiseWithAsync(total){
for(let i=0; i<total; i++){
await randomPromise(i);
}
}
returnPromiseWithAsync(10).then(value => console.log(value)).catch(reason => console.log(reason));
async await 实现串行
//串行,如果成功,持续串行,如果失败,rerun
//配置:随机成功或者失败
function randomPromise(task){
return (new Promise((resolve,reject) => {
console.log(`------------task ${task} ----------------`);
Math.random() > 0.5 ? resolve(`task ${task} succeed`) : reject(`task ${task} failed`);
}))
}
//配置:如果一个任务失败,rerun 递归
function taskReRun(task){
return randomPromise(task).catch(() => taskReRun(task));
}
//配置:开始串行
async function returnPromiseWithAsync(total){
for(let i=0; i<total; i++){
await taskReRun(i);
}
}
returnPromiseWithAsync(10).then(value => console.log(value));
async await 实现并行
//并行,最后 all finish
//配置:随机成功或者失败
function randomPromise(task){
return (new Promise((resolve,reject) => {
console.log(`------------task ${task} ----------------`);
Math.random() > 0.5 ? resolve(`task ${task} succeed`) : reject(`task ${task} failed`);
}))
}
//配置:如果一个任务失败,rerun 递归
function taskReRun(task){
return randomPromise(task).catch(() => taskReRun(task));
}
//配置:开始串行
async function returnPromiseWithAsync(total){
let map = new Map();
//每执行一次任务,加入到map集合种,无论是否状态改变
for(let i=0; i<total; i++){
let p = taskReRun(i);
map.set(i,p);
}
//一个个检查,所有都完成了,函数退出
for(let i=0; i<total; i++){
await map.get(i);
}
}
returnPromiseWithAsync(10).then(value => console.log('all finish'));
使用try catch 实现async await串行
//串行,如果成功,持续串行,如果失败,rerun
//配置:随机成功或者失败
function randomPromise(task){
return (new Promise((resolve,reject) => {
Math.random() > 0.5 ? resolve(`task ${task} succeed`) : reject(`task ${task} failed`);
}))
}
//配置:如果一个任务失败,rerun 递归
function taskReRun(task){
return randomPromise(task).then(() => {
console.log(`task ${task} succeed`);
}).catch(() => {
console.log(`task ${task} failed`);
taskReRun(task);
});
}
//配置:开始串行
async function returnPromiseWithAsync(total){
try{
for(let i=0; i<total; i++){
await taskReRun(i);
}
console.log('all finish');
}catch(e){
console.log('some failed');
}
}
returnPromiseWithAsync(20);
使用 try catch 实现 async await并行
//并行,最后 all finish
//配置:随机成功或者失败
function randomPromise(task){
return (new Promise((resolve,reject) => {
console.log(`------------task ${task} ----------------`);
Math.random() > 0.5 ? resolve(`task ${task} succeed`) : reject(`task ${task} failed`);
}))
}
//配置:如果一个任务失败,rerun 递归
function taskReRun(task){
return randomPromise(task).catch(() => taskReRun(task));
}
//配置:开始串行
async function returnPromiseWithAsync(total){
try{
let map = new Map();
//每执行一次任务,加入到map集合种,无论是否状态改变
for(let i=0; i<total; i++){
let p = taskReRun(i);
map.set(i,p);
}
//一个个检查,所有都完成了,函数退出
for(let i=0; i<total; i++){
await map.get(i);
}
console.log('all finish');
}catch(e){
console.log('some failed');
}
}
returnPromiseWithAsync(10)
Class
- 用ES5写法构造对象,不用new顶多挂到window上,可以运行。用class的写法,构造对象时必须要使用new,否则报错
- function存在变量提升,class不存在变量提升
使用Class定义构造函数的两种写法
class Person {
//写成这个样子的函数/变量是写在prototype上的
constructor(name,age){
Object.assign(this,{name,age});
}
say(){
console.log('hello world');
}
}
let obj = new Person('张三', 25);
console.log(obj);
//以立即执行函数的形式构造对象
let obj = new class {
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log('hello world');
console.log(Person.name); // Person
}
}('张三', 25);
ES6的Class 与 ES5构造函数 的异同
//相同点
console.log(Person === Person.prototype.constructor); //true
console.log(p.constructor === Person.prototype.constructor); //true
console.log(p.say === Person.prototype.say); //true
//不同点
//用class方法定义的原型函数,不可枚举,有ES5定义的原型函数可以枚举
console.log(Object.keys(Person.prototype)); // []
访问实例原型的异同
//ES5写法
console.log(obj.__proto__);
//ES6写法
console.log(Object.getPrototypeOf(obj));
Class表达式
- Person名分两种情况
- 情况一:Person作为class内部的名字,只在内部有效,外部无法调用
- 情况二:Person名字可省略
const myClass = class person{
constructor(name,age){
this.name = name;
this.age = age;
}
say(){
console.log('hello world');
console.log(Person.name); // Person
console.log(myClass.name); // Person
}
}
Class中的this问题
class Person{
constructor(name,age){
this.name = name;
this.age = age;
//方法一:
this.say = this.say.bind(this);
}
//方法二:改用箭头函数,箭头函数调用不初始化this,使用定义地方的this
say(){
console.log(this.name);
}
}
function f(callback){
callback();
}
let obj = new Person('张三', 25);
f(obj.say); //函数调用会初始化this,如果解决obj.say的this问题
Class的get / set
class Person{
constructor(name,age){
this.name = name;
this.age = age;
};
set name(data){
name = data;
}
get name(){
return name;
}
}
Class的静态方法和属性(通过类名、构造函数名访问静态方法/属性)
class Person{
//静态属性或者方法
static num = 5;
static f(){
console.log('f');
}
constructor(name,age){
this.name = name;
this.age = age;
}
set name(value){
name =value;
}
get name(){
return name;
}
}
//外部静态属性/方法
Person.num = 5;
Class继承
- super介绍
- super() 代表基类构造函数
- super()必须在构造函数中调用。 ES5:先有this,后调用基类构造函数。 ES6:this是super是初始化的
- 在派生类构造函数完成以前,必须调用基类构造函数
- 在调用super()以前,不能使用this
- super作用
- super() 当作调用基类构造函数使用
- super 充当对象使用(在普通函数中)
- 如果super后面的是属性,在读取值时,充当基类原型对象
- 如果super后面的是属性, 在赋值的时候,充当派生类this作用
- 如果super后面的是方法, 调用的是基类原型的方法,绑定的this是派生类的this
- 不能确定super是以对象还是调用构造函数的方法使用时,报错
class Person{
constructor(){
this.name = '张三';
this.age = 15;
this.say = this.say.bind(this); //给say函数绑定this,以至于后期把obj.say作为参数传递丢失this
}
say(){
console.log('hello world');
}
}
class Student extends Person{
constructor(id,sex){
//1、super()当作调用基类构造函数使用
super();
//2、super() 代表基类构造函数
// 2-1、super()必须在构造函数中调用。 ES5:先有this,后调用基类构造函数。 ES6:this是super是初始化的
// 2-2、在派生类构造函数完成以前,必须调用基类构造函数
// 2-3、在调用super()以前,不能使用this
this.id = id;
this.sex = sex;
}
}
var obj = new Student(001,'male');
console.log(obj.say);
console.log(obj.name);
console.log(obj.sex);
模块
ES6:import / export(静态)
export / impor: 必须写在顶级和静态的,不能写在某个执行体内部
脚本拿到import的时候先静态处理,从目标文件中抓取相应的东西,把所有东西提升到顶部,然后才开始运行,所以就算import写在脚本最后,前面的执行体也可以拿到数据,一般建议写开头
//文件名:document.js :
var a = 5;
function f(){
console.log('hello');
}
//必须写在脚本全局位置
//1、
export {a,f};
//2、
export {a as a1, f as f1}; //以别名方式返出给外界
//3、
var a = 5;
//使用default,接收那边可以使用任何名称, export default只能用一次
//如果target是变量的话,不允许在 export default 上直接定义, 例如:export default var a = 5 这样写
export default a; // 等价于 export {a as default}
// 或者
export default function f(){console.log('hello')}; // 此时target是函数表达式
export {a,f};
//文件名:index.js
//必须写在脚本全局位置
//1、
import {a,f} from './document.js'
//2、
import {a1,f1} from './document' //如果后缀是js / mjs , 可以省略
// 把document.js传出来的所有东西挂载在A对象上, A对象不可拓展不可删除不可修改
// A底下的如果是个对象,那么这个对象类内部的(key,value)就可以更改
import * as A from './document'
//3、
import aaa,{a,f} from './document';
import ffff,{a,f} from './document';
CommonJS:module.export / require
- 动态的,运行时加载,被加载的是一个对象
- node.js广泛使用这种方法
//文件一:
var a = 5;
function f(){
console.log('hello');
}
module.exports = {
a : a,
f : f
}
//文件二:
var A = require('./文件一');
- CommonJS其实对脚本内对象的拷贝,如果是原始类型,传出去后不会再改变数值,如果是引用类型那么会改变。 在文件二中调用f方法,A.a的值不会改变,如果A.a想要同步发生改变,就以 get的方式把a传递出去
//文件一:
var a = 5;
function f(){
a++;
}
module.exports = {
get a(){
return a;
},
f
}
//文件二:
var A = require('./a.js');
console.log(A.a); //5
A.f();
console.log(A.a); //6