JavaScript ES6

视频教程:https://www.bilibili.com/video/BV1uK411H7on

目录

ES6

ES6基本概念

ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范,而JavaScript就是这一标准的一种实现。
在这里插入图片描述
ES6实际上是一个泛指,指ES2015及后续的版本

let

ES6中新增的用于声明变量的关键字

let具有三个特性:
① let声明的变量只在所处于的块级有效

if (true) {
	let a = 10;
	var b = 20;
}
console.log(a)	// undefined
console.log(b)	// 20

注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

② let声明的变量不存在变量提升

console.log(a)	// undefined
var a = 10;

----
console.log(b)	// ERROR: Uncaught ReferenceError: b is not defined
let b = 10;

③ 暂时性死区

var tmp = 1;
if (true) {
	tmp = 2;
	let tmp;
}
// 运行后报错:Cannot access 'tmp' before initialization
例题一
let arr = [];
for (let i = 0; i < 2; i++) {
    arr[i] = function () {
        console.log(i); 
    }
}
arr[0]();
arr[1]();

此时,运行结果是:0,1

此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.

如果把代码中的两处let换成var,输出结果是:2, 2

const

ES6中新增的用于声明变量的关键字,其用来声明常量,常量就是值(内存地址)不能变化的量。

const具有三个特性:
① const声明的变量只在所处于的块级有效(具有块级作用域)

if (true) {
	const a = 10;
}
console.log(a)	// undefined

② const声明的变量不存在变量提升

console.log(a)	// undefined
const a = 10;

③ 声明变量时必须赋值
const PI; // 报错:Missing initializer in const declaratio

④ 常量赋值后,值不能修改,但是可以修改复杂数据类型的内部元素

const a = 3;
a = 100; // 报错:Assignment to constant variable.(赋值给常量变量)

const arr = [1, 2];
arr[0] = 'a';
arr[1] = 'b';
console.log(arr); // ['a', 'b']
arr = ['a', 'b'] // 报错:Assignment to constant variable.
总结:let、const、var
varletconst
函数级作用域块级作用域块级作用域
变量提升不存在变量提升不存在变量提升
值可以更改值可以更改值不可更改

解构赋值

按照一定的模式,从数组或对象中提取值,将提取出来的值赋给另外的变量

数组解构
let [a, b, c] = [1, 2, 3];
a // 1
b // 2
c // 3

如果解构不成功,变量的值为undefined:

let [foo, bar] = [1];
foo // 1
bar // undefined
------
let [xq] = [1, 2];
xq // 1
对象解构
let person = { name: 'zizi', age: 20 };
let { name, age } = person;
name // 'zizi'
age // 20

注意:解构赋值的新变量名需要和对象的属性名一致,如不一致,需要更改赋值语法。

let person = { name: 'zizi', age: 20 };
let { name: myName, age: myAge } = person;
console.log(myName)	// 'zizi'
console.log(myAge) // 20

箭头函数

箭头函数是ES6中新增的定义函数的方法。

基本语法:() => {}

分情况讨论:
① 当函数体中只有一句代码,且要将这句代码的结果作为返回值,此时可以省略大括号

const sum = (num1, num2) => num1 + num2;
var a = sum(10, 20)	// a = 30

② 如果函数的形参只有一个,可以省略小括号

const fn = v => { console.log(v) }

③ 结合①和②,可以省略小括号和大括号

const fn = v => v;

注意:

  • 不能用箭头函数来作为定义对象类的构造函数
  • 不能在箭头函数内部使用arguments来查看传入的形参
箭头函数的this

箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this

const obj = { name: '张三'} 
function fn () { 
    console.log(this);
    // 返回匿名函数
    // return () => { 
    //     console.log(this)
    // } 
    // 返回箭头函数
    return () => { 
        console.log(this)
    } 
    
} 
const resFn = fn.call(obj); 
resFn();

上述代码,当fn函数返回的是匿名函数时,首先fn内的this被call方法改为指向obj,而resFn变量接收到了一个匿名函数,再通过resFn()调用时,因为是window调用的resFn,因此匿名函数内的this指向window
当fn函数返回的是箭头函数时,因为箭头函数中的this是其定义位置的this,所以箭头函数this指向obj。

注意:
当我们使用ES5语法进行类的定义时(构造函数),如果使用箭头函数来定义方法,有以下两种情况,其this指向不同:

  1. 在构造函数外部使用**原型对象(共享方法)**定义方法时,如使用箭头函数来定义,则方法内部的this永远指向顶层对象(window),除非使用call等方法来强行转换指向。
  2. 在构造函数内部使用**原型对象(共享方法)**定义方法时,如使用箭头函数来定义,则方法内部的this永远指向被定义时所在的对象,而不是使用时所在的对象。

*****例:

function Test(val) {
	this.val = val;
	console.log(this)	// this指向 被创建时的待创建对象(即那个执行完后就被创建了的对象)
	Test.prototype.t1 = () => {
		console.log('t1:', this)
	}
}
Test.prototype.t2 = () => {
	console.log('t2:', this)
}
// 实例化对象
var a1 = new Test('aaa');
var b1 = new Test('bbb');

a1.t1() 	// this指向Test{val: 'bb'}
a1.t2()	// this指向Window{}

b1.t1() 	// this指向Test{val: 'bb'}
b1.t2()	// this指向Window{}

通过注释可以了解到上文提到了两种定义方式的情况,此外,关于箭头函数this指向问题,有一个更深入的探讨(即代码中a1.t1()方法却指向了b1的问题):

首先我们要知道,t1()方法内部的this应该等同于其定义时上下文的this,而在a1b1这些对象被创建时,其上下文的this是指向当前被定义的对象的(即分别指向a1b1,打印结果就是Test { val: 'aaa' }),此时我们明确了t1的this指向。

又因为t1()、t2()方法是共享方法,其内部this指向是不会因为被调用对象的改变而改变的,t1()方法是在构造函数内部定义的,因此每次new Test()时,都会对t1方法进行一次定义,所以t1()方法内部的this指向就决定于被定义时的对象

在例子中,首先创建了a1对象,此时t1的this指向a1对象,再创建了b1对象,此时t1的this指向了b1对象,因此在a1.t1()时,打印的this指向了b1

函数剩余参数 rest

相关:见上文apply方法

剩余参数 允许我们将一个不定数量的参数表示为一个数组

语法:function xx (xx, xx, ...args) {} 此处的args可以为其他命名。

function sum (first, ..args) {
	console.log(first); // 10
	console.log(args); // [20, 30]
}
sum(10, 20, 30)
剩余参数与解构赋值

两者可以配合使用:

此处将剩余参数作为解构赋值数组的最后一个参数,用于接受不定数量的数组的元素。

let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students; 
console.log(s1);  // 'wangwu' 
console.log(s2);  // ['zhangsan', 'lisi']

函数默认参数

ES6中新增了可以为函数的形参设置默认值的方法。

//ES6 允许给函数参数赋值初始值
//1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)
function add(a, b, c=10) {
    return a + b + c;
}
let result = add(1,2);
console.log(result);	// result = 13
默认参数与解构赋值
//2. 与解构赋值结合
// 在想函数传递参数时,直接解析数组/对象并分发给各形参,亦可设置形参默认值
function connect({host="127.0.0.1", username,password, port}){
    console.log(host)
    console.log(username)
    console.log(password)
    console.log(port)
}
connect({
    host: 'atguigu.com',
    username: 'root',
    password: 'root',
    port: 3306
})

内置对象扩展

Number 数值扩展
Number.EPSILON

Number.EPSILON是ES6新增的数值属性,其是JavaScript能够表示的最小精度。

EPSILON值接近于2.2204460492503130808472633361816E-16。

它可以用于浮点数计算和相等判断中:

错误判断:
var a = 0.1 + 0.2 // 0.30000000000000004
a === 0.3 // false
正确判断:
Math.abs(a - 0.3) < Number.EPSILON 	// true
进制

ES6提供了二进制和八进制数的新写法,分别用前缀0b and 0o表示。

let b = 0b1010;	// 10
let o = 0o777;	// 511
let d = 100;	// 100
let x = 0xff;	// 255
Number.isFinite() & isNaN()

这两个方法在ES6之前都作为一个window下的全局方法来提供,在ES6中,将这两个方法也添加到了Number数值对象中,这样更符合使用逻辑。

Number.isFinite()用来检查一个数值是否有有限的;

console.log(Number.isFinite(100));	// true
console.log(Number.isFinite(100/0));	// false
console.log(Number.isFinite(Infinity));	// false

Number.isNaN()用来检查一个数值是否是NaN;

console.log(Number.isNan(100));	// false
console.log(Number.isNaN(NaN));	// true
Number.parseInt() & parseFloat()

这两个方法在ES6之前都作为一个window下的全局方法来提供,在ES6中,将这两个方法也添加到了Number数值对象中,这样更符合使用逻辑。

Number.parseInt(str)用来解析字符串为整数;Number.parseFloat()用来解析字符串为浮点数;

console.log(Number.parseInt('5211314love'));	// 5211314
console.log(Number.parseFloat('3.1415926神奇'));	// 3.1415926
Number.isInteger

该方法用于判断一个数是否是整数。

console.log(Number.isInteger(5));	// true
console.log(Number.isInteger(2.5));	// false
Math.trunc

该方法用于去除一个数的小数部分,返回整数部分。
console.log(Math.trunc(3.5)); // 3

Math.sign

该方法用于判断一个数到底为正数 负数 还是零,如果是正数返回1,负数返回-1,零则返回0。

console.log(Math.sign(100));	// 1
console.log(Math.sign(0));	// 0
console.log(Math.sign(-20000));	// -1
ES7 指数操作符 **

在ES7中引入了指数操作符**,用来实现幂运算,其功能与Math.pow(number, x)一致。

console.log(Math.pow(2, 10))	// 1024
console.log(2 ** 10)	// 1024
Array的扩展方法
扩展运算符(展开语法)

扩展运算符...xx有多个使用方法/作用。


① 扩展运算符可以将数组或者对象转为用逗号分隔的参数序列

let ary = [1, 2, 3];
...ary  // 1, 2, 3
console.log(...ary);    // 1 2 3
// 上下语句等价
console.log(1, 2, 3)	// 1 2 3

这里的log打印结果是1 2 3 ,没有逗号分隔,因为...arg将数组arr的方括号移除,然后运行console.log(...arg),此时1,2,3是作为三个参数传入的console.log。

② 扩展运算符可以应用于合并数组

// 方法一 
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];	// ary3 = [1,2,3,3,4,5]
// 方法二 
ary1.push(...ary2);	// ary1 = [1,2,3,3,4,5],该语句返回值是添加完后数组的元素个数

③ 将类数组或可遍历对象转换为真正的数组

let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];
Array.from()方法

这个方法可以将类数组或可遍历对象转换为真正的数组,和上文的扩展运算符功能之一类似。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

方法还可以接受第二个参数,作用类似于map方法,对每个元素进行处理,将处理后的值放入返回的数组。

let arrayLike = { 
    "0": 1,
    "1": 2,
    "length": 2
}
let newAry = Array.from(aryLike, item => item *2)
// newAry = [2, 4]

上文也可以改写为以下,实现相同功能(原始arrayLike需为数组):

let arrayLike = [1,2];
let newAry = arrayLike.map(x => x * 2)
// newAry = [2, 4]
find()方法

用于找出第一个符合条件的数组成员,如果没有找到则返回undefined。

let ary = [{
    id: 1,
    name: '张三'
}, { 
    id: 2,
    name: '李四'
}]; 
let target = ary.find((item, index) => item.id == 2);
// target = {id: 2, name: '李四'}
findIndex()方法

用于找出第一个符合条件的数组成员的位置索引值,如果没有找到则返回 -1

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2
ES7 includes()方法

用于判断数组是否包含给定的值,返回布尔值。
该方法与es5之前就有的arr.indexOf(xxx)类似,只不过indexOf仅返回索引(无则-1),不返回布尔值。

[1,2,3].includes(2)	// true
[1,2,3].includes(4)	// false
ES10:Array.prototype.flat & flatMap

flat方法将多维数组转换为低维数组,其接受一个数字参数,用于设定降低的维数,默认为1。

const arr1 = [1,2,3,4,[5,6]];
const arr2 = [1,2,3,4,[5,6,[7,8,9]]];

console.log(arr1.flat())	// [1, 2, 3, 4, 5, 6]
console.log(arr2.flat(2))	// [1, 2, 3, 4, 5, 6, 7, 8, 9]

flatMap方法即map方法与flat方法的结合,可在对数组元素进行遍历+操作时,将其返回的元素(数组)降维。

const arr = [1,2,3,4];
const result1 = arr.map(item => [item * 10]);
const result2 = arr.flatMap(item => [item * 10]);
console.log(result1);	// [[10], [20], [30], [40]]
console.log(result2);	// [10, 20, 30, 40]
String的扩展方法
模板字符串

模板字符串用于格式化字符串输出。

用反引号括起来的就是模板字符串
在这里插入图片描述

// 基本使用方法
let name = 'qq';
let x = `hello, my name is ${name}`;
// x = 'hello, my name is qq'

// 模板字符串中还可以换行
let result = { 
    name: 'zhangsan', 
    age: 20,      sex: '男' 
} 
let html = ` <div>
    <span>${result.name}</span>
    <span>${result.age}</span>
    <span>${result.sex}</span>
</div> `;

// 在模板字符串中还可以调用函数
const fn = () => 'this is fn function';
let x = `调用函数 ${fn()}`;
console.log(x); // '调用函数 this is fn function'
startsWith() & endsWith()

str.startsWith(subStr):判断str字符串的头部是否是subStr子串,返回布尔值
str.endsWith(subStr):判断str字符串的尾部是否是subStr子串,返回布尔值

let str = 'Hello world!';
str.startsWith('Hello') // true 
str.endsWith('!')       // true
repeat() 方法

该方法可以将原字符串重复n次,并返回这个新字符串。

'x'.repeat(3) // 'xxx'
'hello'.repeat(2) // 'hellohello'
ES10: trimStart & trimEnd

这两个方法与ES5的trim类似,前者去除字符串前面的空白字符,后者去除字符串后面的空白字符。

let str = '     zzz      ';
str.trimStart() //	'zzz    ';
str.trimEnd()	// '    zzz';
ES11:String.prototype.matchAll()

该方法用来得到正则的批量匹配的结果。

let str = `
<ul>
	<li>
		<a>肖生克的救赎</a>
		<p>上映日期: 1994-09-10</p>
	</li>
	<li>
		<a>阿甘正传</a>
		<p>上映日期: 1994-07-06</p>
	</li>
</ul>
`;
// 正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;
const result = str.matchAll(reg);
// 返回的是可迭代对象,可用扩展运算符展开
console.log(...result);
// ["<li>↵		<a>肖生克的救赎</a>↵		<p>上映日期: 1994-09-10</p>", "肖生克的救赎", "上映日期: 1994-09-10", index: 7, input: "↵<ul>↵	<li>↵		<a>肖生克的救赎</a>↵		<p>上映日期: 1994-09-10<…>阿甘正传</a>↵		<p>上映日期: 1994-07-06</p>↵	</li>↵</ul>↵", groups: undefined] 
// ["<li>↵		<a>阿甘正传</a>↵		<p>上映日期: 1994-07-06</p>", "阿甘正传", "上映日期: 1994-07-06", index: 62, input: "↵<ul>↵	<li>↵		<a>肖生克的救赎</a>↵		<p>上映日期: 1994-09-10<…>阿甘正传</a>↵		<p>上映日期: 1994-07-06</p>↵	</li>↵</ul>↵", groups: undefined]

Object对象 扩展

Object.is

该方法用于比较两个值是否严格相等,于===基本一致。

console.log(Object.is(120, 120));// true
console.log(Object.is(NaN, NaN));// true
console.log(NaN === NaN);// false
Object.assign

Object.assign(obj1, obj2)
该方法用于将两个对象合并,可将obj2对象的所有可枚举属性复制给obj1对象。

const config1 = {
    host: 'localhost',
    port: 3306,
    name: 'root',
    pass: 'root',
    test: 'test'
};
const config2 = {
    host: 'http://atguigu.com',
    port: 33060,
    name: 'atguigu.com',
    pass: 'iloveyou',
    test2: 'test2'
}
console.log(Object.assign(config1, config2));
// 打印结果:
host: "http://atguigu.com"
name: "atguigu.com"
pass: "iloveyou"
port: 33060
test: "test"
test2: "test2"
__proto__: Object
对象定义的简化写法

ES6允许在大括号中直接写入变量和函数作为对象的属性和方法:

let name = '尚硅谷';
let change = function(){
    console.log('我们可以改变你!!');
}

const school = {
    name,
    change,
    improve(){
        console.log("我们可以提高你的技能");
    }
}

console.log(school);
setPrototypeOf、 getPrototypeOf

这两个方法可以用于获取和设置对象的原型对象,即对象实例.__proto__ & 对象.prototype,但是不建议使用这个方法,直接对象.prototype = xxx修改即可。

const school = {
    name: '尚硅谷'
}
const cities = {
    xiaoqu: ['北京','上海','深圳']
}
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
ES8:Object.values & entries & getOwnPropertyDescriptors
  1. Object.values()方法:返回一个给定对象的所有可枚举属性值的数组
  2. Object.entries()方法:返回一个给定对象自身可遍历属性 [key,value] 的数组,随后可利用返回结果快速创建Map对象
  3. Object.getOwnPropertyDescriptors()该方法:返回指定对象所有自身属性的描述对象(数据描述符)
//声明对象
const school = {
    name:"尚硅谷",
    cities:['北京','上海','深圳'],
    xueke: ['前端','Java','大数据','运维']
};

//获取对象所有的键
console.log(Object.keys(school));	// ["name", "cities", "xueke"]
//获取对象所有的值
console.log(Object.values(school));	// ["尚硅谷", Array(3), Array(4)]
//entries
console.log(Object.entries(school));	// [Array(2), Array(2), Array(2)]
										// 0: (2) ["name", "尚硅谷"]
										// 1: (2) ["cities", Array(3)]
										// 2: (2) ["xueke", Array(4)]
//利用entries返回的结果创建 Map 对象
const m1 = new Map(Object.entries(school));	// 0: {"name" => "尚硅谷"}
											// 1: {"cities" => Array(3)}
											// 2: {"xueke" => Array(4)}
console.log(m1.get('cities'));	// ["北京", "上海", "深圳"]

//对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school));	
// cities: {value: Array(3), writable: true, enumerable: true, configurable: true}
// name: {value: "尚硅谷", writable: true, enumerable: true, configurable: true}
// xueke: {value: Array(4), writable: true, enumerable: true, configurable: true}
ES10:Object.fromEntries

该方法和ES8提出的Object.entries方法相反,用于将二维数组或Map对象转换为对象。

const result = Object.fromEntries([
    ['name','尚硅谷'],
    ['xueke', 'Java,大数据,前端,云计算']
]);
console.log(result) // {name: "尚硅谷", xueke: "Java,大数据,前端,云计算"}
ES9:对象的 Rest参数 & spread扩展运算符

在ES6中引入了Rest剩余参数和扩展运算符,不过是针对数组和函数的,并不适用于对象Object,因此在ES9中为对象Object也适配的Rest和扩展运算符。

//rest 参数
function connect({host, port, ...user}){
    console.log(user);	// username + password + type均被接收到
}

connect({
    host: '127.0.0.1',
    port: 3306,
    username: 'root',
    password: 'root',
    type: 'master'
});
//对象合并
const skillOne = {
    q: '天音波'
}

const skillTwo = {
    w: '金钟罩'
}

const skillThree = {
    e: '天雷破'
}
const skillFour = {
    r: '猛龙摆尾'
}

const mangseng = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};

console.log(mangseng)	// {q: "天音波", w: "金钟罩", e: "天雷破", r: "猛龙摆尾"}

Set集合 数据结构

ES6提供了一种新的数据解构Set,它类似于数组,但是成员的值都是唯一的,不可重复。(就是python中的set)

此外,Set也实现了iterator接口,所以我们可以使用扩展运算符for...of进行遍历。

Set本身是一个构造函数,用来生成Set数据解构。
const s = new Set();
const s = new Set([1,2,3,4,5,6,6])

Set的实例方法
  • add(value):添加某个值,返回 Set 结构本身
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示该值是否为 Set 的成员
  • clear():清除所有成员,没有返回值
 const s = new Set();
 s.add(1).add(2).add(3); // 向 set 结构中添加值 	return Set(2) {1, 2, 3}
 s.delete(2)             // 删除 set 结构中的2值 	return true
 s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 return true
 s.clear()               // 清除 set 结构中的所有值 return undefined 此时s = Set(0) {}
Set的遍历

Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行指定的操作,没有返回值。

该方法的使用与数组forEach一致,通过回调函数执行操作,回调函数可接受三个参数,分别是当前元素值、由于set没有键所以index和value值一致、原Set内容,此外forEach接受第二个参数(可选),用于指定回调函数执行时的this指向。

s.forEach((val, index, set) => {console.log(val, index, setInterval)})
// 打印结果
1 1 Set(3) {1, 2, 3}
2 2 Set(3) {1, 2, 3}
3 3 Set(3) {1, 2, 3}

此外,也可以使用for ... of ..来遍历集合Set。

var s = new Set([1,2,3,4,5,6,8,9])
for (let val of s){
	console.log(val);	// 1,2,3,...,9
}

Map 数据结构

ES6提供了Map数据结构,它类似于对象Object,也是键值对的集合,特殊之处在于,其的范围不限于字符串,其他各类型的值(包括对象)均可当作Map的键。

此外,Map也实现了iterator接口,所以我们可以使用扩展运算符for...of进行遍历。

Map的属性和方法
  1. m.size 属性,表示该map的元素个数
  2. m.set('key', value) 方法,为map添加新元素,并返回新map
  3. m.get(key) 方法,获取指定键的键值
  4. m.has(key) 方法,检测map中是否包含某个键,返回布尔值
  5. m.clear() 方法,清空Map内的所有元素,返回undefined

Symbol

symbol是一种基本数据类型(Boolean、Number、String、undefined、null、bigint、symbol),它的作用就是可以生成一个全局唯一的值

基本使用
//创建Symbol
let s = Symbol();
// console.log(s, typeof s);
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
// s2 === s3 false
//Symbol.for 创建
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
// s4 === s5 true
特点
  • Symbol的值是唯一的,可以用来解决命名冲突的问题;(在对象中作为属性名,避免属性名冲突)
  • 替代代码中多次使用的字符串(例如:abc),多次使用的字符串在代码中不易维护,而这时候定义一个对象的属性(属性名用Symbol格式),值为abc,就可以作为全局变量来使用了
  • Symbol值不能和其他数据进行运算
  • 用Symbol在对象中定义的对象属性,不能被for…in循环遍历得到,但是可以用Reflect.ownKeys来获取对象的所有键名
内置值

除了可以定义自己使用的Symbol值以外,还有11个内置的symbol值,指向语言内部使用的方法,这些方法可以称为魔术方法,它们会在特定的场景下自动执行。

在这里插入图片描述
内置值在使用时,都是作为某个对象类型的属性去使用的。

ES10:Symbol.prototype.description

该属性可以得到Symbol的描述字符串。

let s = Symbol('尚硅谷');
console.log(s.description);	// '尚硅谷'

ES9:正则表达式扩展

命名捕获

使用?<name.*>来捕获指定字符并赋值给变量name。

let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
//分组命名
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;

const result = reg.exec(str);

console.log(result.groups.url);	// http://www.atguigu.com

console.log(result.groups.text);	// 尚硅谷
反向断言

ES9支持了反向断言,通过对匹配结果前面的内容进行判断,从而对匹配到的元素进行筛选。

示例

// 字符串
let str = "JS5201314你知道么555啦啦啦";
// 需求:我们只想匹配到555
// 正向断言
const reg = /\d+(?=啦)/; // 前面是数字后面是啦
const result = reg.exec(str);
console.log(result);
// 反向断言
const reg1 = /(?<=么)\d+/; // 后面是数字前面是么
const result1 = reg.exec(str);
console.log(result1);
dotAll模式

一般而言,在正则表达式中.符号用于匹配除回车外的任何单一字符,在ES9中,我们可以通过添加s来设置为dotAll模式,使.也可以匹配到换行符。

// 正则扩展:dotAll 模式
// dot就是. 元字符,表示除换行符之外的任意单个字符
let str = `
<ul>
<li>
<a>肖申克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>
`;
// 需求:我们想要将其中的电影名称和对应上映时间提取出来,存到对象
// 之前的写法
// const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/;
// dotAll 模式
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;

// 匹配结果:[肖申克的救赎、上映日期: 1994-09-10], [阿甘正传,上映日期: 1994-07-06]

ES11:可选链操作符

在读取对象属性值时,使用可选链操作符,可以自动进行判断,如存在该属性则读取,作用是省略对对象是否传入的层层判断结构

语法:.?

function main(obj){
    // const dbHost = obj && obj.db && obj.db.host;
    const dbHost = obj?.db?.host;
	// 如没有该属性,则返回undefined
    console.log(dbHost);	// 192.168.1.100
}

main({
    db: {
        host:'192.168.1.100',
        username: 'root'
    },
    cache: {
        host: '192.168.1.200',
        username:'admin'
    }
})

ES11:BigInt

这是一个新的数据结构,表示更大的整数。

// 大整形,直接在数字后加n即可转为bigint
let n1 = 521n;
console.log(n1, typeof(n1));	// 521n "bigint"

// 函数方式转为bigint
let n2 = 123;
console.log(BigInt(n2));	// 123n
console.log(BigInt(1.2));	// 报错,浮点数不可转换

ES11:globalThis

该属性始终指向全局对象 window

console.log(globalThis);
// Window {......}

ES6 面向对象

面向对象的特性:

  • 封装性
  • 继承性
  • 多态性

ES6语法 class与super

ES6提出了一种新的面向对象语法class,其本质上是语法糖,功能基本都可以用ES5语法实现。

创建 类
class Name {
	constructor(x, y) {
		this.x = x;
		this.y = y;
	}
	fn() {
		console.log('这是对象的fn方法')
	}
}

var xx = new Name(1, 2);
xx.x // 1
xx.y // 2
xx.fn() // '这是对象的fn方法'

类必须使用 new 来实例化对象

构造函数 constructor

constructor()方法是类的构造函数(默认方法),用于传递参数并返回实例对象
当我们通过 new 命令生成对象实例时,会自动调用这个方法。如果在class类定义中,没有写构造函数,class内部会自动给我们创建一个constructor()构造函数。

构造函数定义时不需要加 function

类的共有方法

类中定义方法的时候,不需要在函数前面加function,同时方法之间不能加 逗号 分隔。

class Person {
	constructor(name, age) {
		this.name = name;
		this.age = age;
	}
	say() {
		console.log(this.name + ' 你好')
	}
}

// 创建实例
var ldh = new Person('刘德华', 58);
console.log(ldh.name, ldh.age);
ldh.say();
静态成员 static

类的静态成员在ES6中可以通过static关键字来定义。不能在类的实例上调用静态成员,而应该由类本身调用静态成员。

ES5写法:

function Phone() {};
Phone.name = 'iphone';
Phone.open = function () {
	console.log('打开手机');
}
// 调用静态成员:
console.log(Phone.name)	// 'iphone'
Phone.open()	// '打开手机'

ES6写法:

class Phone{
	static name = 'iphone';
	static open() {
		console.log('打开手机');
	}
}

// 调用静态成员:
console.log(Phone.name)	// 'iphone'
Phone.open()	// '打开手机'
类的继承

子类可以通过继承extends来获得父类的属性和方法。

super关键字:可以访问和调用父类的函数(构造函数、普通函数方法)。


情况一:使用super调用父类的构造函数

class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    sum() {
        console.log(this.x + this.y);

    }
}
class Son extends Father {
    constructor(x, y, grade) {
        super(x, y); // 调用了父类中的构造函数
        this.grade = grade;	// 在super之后再使用this进行子类的构造
    }
}
var son = new Son(1, 2);
var son1 = new Son(11, 22);
son.sum();
son1.sum();

注意:

  • 此时son.sum()语句会调用父类的sum()方法,而sum()方法中的this是指向父类构造函数的,因此如果在子类的构造函数中需要使用super(x, y)来调用父类的构造函数并将值传递给父类构造函数,此时再son.sum()时,sum()方法的x,y就有值了。
  • 子类在构造函数中必须在this之前使用super(必须先调用父类的构造函数,再使用子类构造方法

情况二:使用super调用父类的普通函数

class Father {
    say() {
        return '我是爸爸';
    }
}
class Son extends Father {
    say() {
        console.log(super.say() + '的儿子');
        // super.say() 就是调用父类中的普通函数 say()
    }
}
var son = new Son();
son.say();	// 输出结果:我是爸爸的儿子

继承中的属性或者方法查找原则: 就近原则

  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

总结:注意点

  1. 在ES6中的类class是没有变量提升的,因此必须要先定义类再实例化对象
  2. 要想在类中使用自身的共有属性和方法,必须在使用前加 this.
重写

子类定义中想要重写父类的方法,可以直接定义,覆盖父类的方法即可。

this指向
var that;
var _that;
class Star {
    constructor(uname, age) {
        // constructor 里面的this 指向的是 创建的实例对象
        that = this;
        console.log(this);

        this.uname = uname;
        this.age = age;
        this.btn = document.querySelector('button');
        this.btn.onclick = this.sing;
    }
    sing() {
        // 这个sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
        console.log(this);

        console.log(that.uname); // that里面存储的是constructor里面的this
    }
    dance() {
        // 这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
        _that = this;
        console.log(this);

    }
}

var ldh = new Star('刘德华');
console.log(that === ldh);		// true
ldh.dance();
console.log(_that === ldh);		// true

总结:

  1. 在构造函数内,this指向的就是该实例对象(ldh)
  2. 在类的方法函数中,this指向这个方法被调用时的调用者
  3. sing方法因为是被btn按钮调用,this就指向btn按钮元素
  4. dance方法因为调用者是ldh,this就指向实例对象ldh(和构造函数的this指向一样)
setters与getters

一个 getter 是一个获取某个特定属性的值的方法。一个 setter 是一个设定某个属性的值的方法。你可以为预定义的或用户定义的对象定义 getter 和 setter 以支持新增的属性。定义 getter 和 setter 的语法采用对象字面量语法。

其与ES5中新增的对象方法Object.defineProperty类似。

下面例子描述了getters 和 setters 是如何为用户定义的对象 o 工作的。

var o = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  // 注意,为属性添加set方法时,需传值
  set c(x) {
    this.a = x / 2
  }
};

console.log(o.a); // 7
console.log(o.b); // 8
o.c = 50;
console.log(o.a); // 25
ES11:私有属性的定义

在ES11,我们可以用#在类的内部定义私有属性,私有属性是不能在外部被直接访问到的,只能在类的内部被调用和访问。

class Person{
    // 私有属性
    #age;
    // 构造方法
    constructor(name, age){
        this.name = name;
        this.#age = age;
    }

    intro(){
        console.log(this.name);
        console.log(this.#age);
    }
}

//实例化
const girl = new Person('晓红', 18);

// console.log(girl.name);	// '晓红'
// console.log(girl.#age);	// wrong
girl.intro();	// '晓红'	18

ES6:迭代器 & 生成器

遍历:处理集合中的每个项是很常见的操作,这一操作也被称为迭代。

在ES6之前,JavaScript提供了许多用于迭代的方法,从简单的for循环到map()filter()。而在ES6,迭代器和生成器将迭代的概念直接带入核心语言,并提供了一种机制来自定义for...of循环的行为。

ES6创造了一种新的二遍历命令for...of循环,iterator接口(迭代器)主要供其使用,原生具备iterator接口的数据结构(可以用for…of遍历)

  • Array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • Nodelist

迭代器

在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next() 方法实现 Iterator protocol 的任何一个对象,该方法返回具有两个属性的对象value,这是序列中的 next 值;和 done ,如果已经迭代到序列中的最后一个值,则它为 true 。如果 value 和 done 一起存在,则它是迭代器的返回值。

一旦创建,迭代器对象可以通过重复调用next()显式地迭代。 迭代一个迭代器被称为消耗了这个迭代器,因为它通常只能执行一次。 在产生终止值之后,对next()的额外调用应该继续返回{done:true}

Javascript中最常见的迭代器是Array迭代器,它只是按顺序返回关联数组中的每个值。 虽然很容易想象所有迭代器都可以表示为数组,但事实并非如此。 数组必须完整分配,但迭代器仅在必要时使用,因此可以表示无限大小的序列,例如0和无穷大之间的整数范围。

例子:它允许创建一个简单的范围迭代器,它定义了从开始(包括)到结束(独占)间隔步长的整数序列。 它的最终返回值是它创建的序列的大小,由变量iterationCount跟踪。

function makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let nextIndex = start;
    let iterationCount = 0;

    const rangeIterator = {
       next: function() {
           let result;
           if (nextIndex < end) {
               result = { value: nextIndex, done: false }
               nextIndex += step;
               iterationCount++;
               return result;
           }
           return { value: iterationCount, done: true }
       }
    };
    return rangeIterator;
}

使用这个迭代器的方式:

let it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1 3 5 7 9
 result = it.next();
}

console.log("Iterated over sequence of size: ", result.value); // 共迭代:5次

工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置;
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员;
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员;
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象;

生成器

虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function*语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。 通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。

可以根据需要多次调用该函数,并且每次都返回一个新的Generator,但每个Generator只能迭代一次。

我们现在可以调整上面的例子了。 此代码的行为是相同的,但实现更容易编写和读取。

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

Promise

意义

什么是promise?
它是ES6引入的异步编程的新解决方案,Promise是一个对象,用来封装异步操作,并可以获取起成功或失败的结果。

  1. 用于异步计算
  2. 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
  3. 可以在对象之间传递和操作promise,帮助我们处理队列
异步

为了避免界面冻结,需要引入异步这一概念,先做一般的任务、工作,然后等完成前面的工作后,通过回调或事件,去做之前挂起的任务。
常见的异步操作:

  1. 事件监听 element.addEventListener('click', function() { ...此处就是异步 })
  2. 回调函数 $.ajax('http://...', { success (res) { 此处就是异步 } })

同时,在使用异步回调的过程中,也会遇到一些问题:

  1. 在ES6之前,处理异步任务完全依靠回调函数的形式来处理
  2. 很容易进入到回调地狱中,剥夺了函数 return 的能力
  3. 可以解决异步问题,但是代码可读性差,维护困难
  4. 稍有不慎就会踏入回调地狱,嵌套层次深,不易维护

回调地狱:

回调地狱

使用方式

const p = new Promise(function (resolve, reject) {
	setTimeout(function () {
		// 情形一:成功
		if (success) {
			let data = 'success';
			resolve(data)	// 调用resolve方法即会将Promise对象状态改为成功,执行p.then的第一个回调函数
		} else (
			// 情形二:失败
			let data = 'error';
			reject(data)	// 调用reject方法会将Promise对象状态改为失败,执行p.them的第二个回调函数
		}
	}
});

// 调用Promise,执行异步操作并针对异步操作的结果作出反应
p.then(function (value) {
	console.log('success', value)	// 成功后执行的函数
}, function (reason) {
	console.log('error', reason)	// 失败后执行的函数
});
  • resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
  • promise有三个状态:
    1、pending[待定] 初始状态
    2、fulfilled[实现] 操作成功
    3、rejected[被否决] 操作失败
    当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
    promise状态一经改变,不会再变。
  • Promise对象的状态改变,只有两种可能:
    从pending变为fulfilled
    从pending变为rejected。
    这两种情况只要发生,状态就凝固了,不会再变了。

.then()

  1. 接收两个函数作为参数,分别代表fulfilled(成功)和rejected(失败)
  2. .then()返回一个新的Promise实例,所以它可以链式调用
  3. 当前面的Promise状态改变时,.then()根据其最终状态,选择特定的状态响应函数执行
  4. 状态响应函数可以返回新的promise,或其他值,不返回值则认为它返回了一个null;
  5. 如果返回新的promise,那么下一级.then()会在新的promise状态改变之后执行
  6. 如果返回其他任何值,则会立即执行下一级.then()

错误捕获与处理

方式一:

var p = new Promise((resolve) => {
	setTimeout(() => {
		throw new Error('error!!')
	}, 2000)
})
p.then((value) => {
	console.log(value);
}).catch(error => {
	console.log('error:', error)
}

方式二:

var p = new Promise((resolve, reject) => {
	setTimeout(() => {
		reject('error!!')
	}, 2000)
});
p.then((value) => {
	console.log(value)
}, (reason) => {
	console.log('error:', reason)
});

第一种:throw new Error('错误信息').catch( () => {错误处理逻辑})
第二种:reject('错误信息').then(() => {}, () => {错误处理逻辑})
推荐使用第一种catch方式,更加清晰好读,并且可以捕获前面所有的错误(可以捕获N个then回调错误)

批量执行:all() 方法

Promise.all([p1, p2, p3])用于将多个promise实例,包装成一个新的Promise实例,返回的实例就是普通的promise。

它接收一个数组作为参数,数组里可以是Promise对象,也可以是别的值,只有Promise会等待状态改变当所有的子Promise都完成,该Promise完成,返回值是全部值的数组,当有任何一个失败,该Promise就失败,返回值是第一个失败的子Promise结果。

//切菜
    function cutUp(){
        console.log('开始切菜。');
        var p = new Promise(function(resolve, reject){        //做一些异步操作
            setTimeout(function(){
                console.log('切菜完毕!');
                resolve('切好的菜');
            }, 1000);
        });
        return p;
    }

//烧水
function boil(){
    console.log('开始烧水。');
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('烧水完毕!');
            resolve('烧好的水');
        }, 1000);
    });
    return p;
}

Promise.all([cutUp(), boil()])
    .then((result) => {
        console.log('准备工作完毕');
        console.log(result);
})

任一执行:race()方法

该方法类似于Promise.all()方法,区别在于它只要有任意一个完成就算完成。

let p1 = new Promise(resolve => {
        setTimeout(() => {
            resolve('I\`m p1 ')
        }, 1000)
    });
let p2 = new Promise(resolve => {
    setTimeout(() => {
        resolve('I\`m p2 ')
    }, 2000)
});
Promise.race([p1, p2])
    .then(value => {
        console.log(value)
})

ES11:Promise.allSettled

Promise.all()类似,此方法可以执行多个异步函数,且不论执行结果,都返回一个fulfilled状态的Promise,并在PromiseResult中展示异步函数的执行状态和结果值。

//声明两个promise对象
const p1 = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('商品数据 - 1');
    },1000)
});

const p2 = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('商品数据 - 2');
        // reject('出错啦!');
    },1000)
});

//调用 allsettled 方法
const result = Promise.allSettled([p1, p2]);

console.log(result);
// 返回一个Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: Array(2)
// 0: {status: "fulfilled", value: "商品数据 - 1"}
// 1: {status: "fulfilled", value: "商品数据 - 2"}

ES7:async & await

async函数是使用async关键字声明的函数。 async函数是AsyncFunction构造函数的实例, 并且其中允许使用await关键字。asyncawait关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise

语法

async function name([params]) {
	statements
}
  • name:函数名称
  • params:要传递给函数的参数
  • statements:包含函数主体的表达式,可以使用await机制

返回值:当使用async函数后,该函数的返回值必然是一个Promise

假设返回值是一个不像Promise的返回值return 1,那么实际返回值就会被包装成一个fulfilled状态的Promise,value即return的值(在这里就是1):[[PromiseState]]: "fulfilled"、[[PromiseResult]]: "1"
假设在async函数中抛出错误throw new Error('have error'),那么实际返回值就是一个rejected状态的Promise,value为错误信息;

await + async

async/await的目的为了简化使用基于promise的API时所需的语法。async/await的行为就好像搭配使用了生成器和promise。它们其实是语法糖。

async函数可能包含0个或者多个await表达式。await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。Promise的解决值会被当作该await表达式的返回值。使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。

  • await 必须写在 async函数中
  • await 右侧的表达式一般为 Promise 对象
  • await 表达式的返回值是Promise 成功的解决值
  • await 右侧表达式的Promise如果失败(rejected)了,就会抛出异常,需要用try...catch捕获处理

async函数的函数体可以被看作是由0个或者多个await表达式分割开来的。从第一行代码直到(并包括)第一个await表达式(如果有的话)都是同步运行的。这样的话,一个不含await表达式的async函数是会同步运行的。然而,如果函数体内有一个await表达式,async函数就一定会异步执行。

更重要的是,在await表达式之后的代码可以被认为是存在在链式调用的then回调中,多个await表达式都将加入链式调用的then回调中,返回值将作为最后一个then回调的返回值。

案例
在接下来的例子中,我们将使用await执行两次promise,整个foo函数的执行将会被分为三个阶段。

  1. foo函数的第一行将会同步执行,await将会等待promise的结束。然后暂停通过foo的进程,并将控制权交还给调用foo的函数。
  2. 一段时间后,当第一个promise完结的时候,控制权将重新回到foo函数内。示例中将会将1(promise状态为fulfilled)作为结果返回给await表达式的左边即result1。接下来函数会继续进行,到达第二个await区域,此时foo函数的进程将再次被暂停。
  3. 一段时间后,同样当第二个promise完结的时候,result2将被赋值为2,之后函数将会正常同步执行,将默认返回undefined

注意:promise链不是一次就构建好的,相反,promise链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。

例如,在下面的代码中,在promise链上配置了.catch处理程序,将抛出未处理的promise错误。这是因为p2返回的结果不会被await处理。

async function foo() {
   const p1 = new Promise((resolve) => setTimeout(() => resolve('1'), 1000))
   const p2 = new Promise((_,reject) => setTimeout(() => reject('2'), 500))
   const results = [await p1, await p2] // 不推荐使用这种方式,请使用 Promise.all或者Promise.allSettled 
}
foo().catch(() => {}) // 捕捉所有的错误...

模块化

什么是模块化

模块化就是指将一个大的程序文件,拆分成许多个小的文件,然后将小文件组合起来。

模块化的好处:

  • 防止命民冲突,独立作用域
  • 提高代码复用性和可读性
  • 提高项目代码的维护性

ES6之前的模块化规范有:

  • CommonJS => NodeJS, Browserify
  • AMD => requireJS
  • CMD => seaJS

使用方式

模块化功能主要由两个命令构成:exportimport

  • export 用于规定模块的对外接口(导出模块),即把内容暴露出去
  • import 用于输入其他模块提供的功能(导入模块),即把内容导入进来
方式一

对模块进行逐个的导出和导入。

假设有一个用于导出的文件m.js

export let school = "xxx";
export function teach() {
	console.log('teach class');
}

以及一个接受导入的文件index.html

<!DOCTYPE html>
	<html>
		<head>
			<meta charset="utf-8">
			<title>模块化</title>
		</head>
		<body>
			<script type="module">
				// 引入m.js模块内容
				import * as m from "./js/m.js";
				console.log(m);	// Module...
				console.log(m.school);	// xxx
				m.teach();	// 'teach class'
			</script>
		</body>
</html>
方式二

对模块进行统一的导出(暴露):
m.js的内容

// 统一暴露(导出)
let school = "xxx";
function teach() {
	console.log('teach class');
}
export {school, teach}

此方式在index.html中的导入、调用方法与方式一 一致。

方式三

对模块采取默认导出方式:
m.js的内容

export default{
	school: 'xxx',
	teach: function() {
		console.log('teach class');
	}
}

此方法在index.html中导入和调用时,需要加上default,因为算是导入了一个对象default。

import * as m from "./js/m.js";
console.log(m.default.school);
m.default.teach();
最常用方式:解构赋值形式
// index.html 部分代码

<script type="module">
    // 1. 基本解构赋值形式,之后可以直接使用
    import {school, teach} from "./src/js/m1.js";
    // 2. 可能会出现重名冲突,可以设定别名
    import {school as guigu, findJob} from "./src/js/m2.js";
    // 3. 当导入 export default默认导出模块时,必须使用别名
    import {default as m3} from "./src/js/m3.js";

	// 此时,这些变量都可以直接使用了
	console.log(school, guigu, m3.school)
    // 4. 简便形式  针对默认暴露时可以使用
    import m3 from "./src/js/m3.js";
    console.log(m3);
</script>
ES11:动态import导入

可以动态导入模块,何时使用就何时导入该模块。

// import * as m1 from "./hello.js"; // 传统静态导入
//获取元素
const btn = document.getElementById('btn');

btn.onclick = function(){
	import('./hello.js').then(module => {
		module.hello();
	});
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值