从 ES6 (ES2015) 开始,JavaScript 版本发布变为年更,即每年发布一个新版本,以年号标识版本 ES6 = ES2015 ES7 = ES2016 一、JavaScript简介 JavaScript一种 动态类型、弱类型、基于原型 的客户端脚本语言,用来给HTML网页增加动态功能。
-
动态类型 在运行时确定数据类型。变量使用之前不需要声明是什么类型,通常变量的类型是被赋值的那个值的类型。
-
弱类型 计算时可以不同类型之间对使用者透明地隐式转换,即使类型不正确,也能通过隐式转换来得到正确的类型。
-
基于原型 新对象继承对象(作为模版),将自身的属性共享给新对象,模版对象称为原型。这样新对象实例化后不但可以享有自己创建时和运行时定义的属性,而且可以享有原型对象的属性。
二、JavaScript由三部分组成
- ECMAScript(核心) ES是JS的子集,它是JS的内容的一部分,一个完整的JS实现是由以下三部分组成:
作为核心,它规定了语言的组成部分:语法、类型、语句、关键字、保留字、操作符、对象
-
DOM(文档对象模型) DOM把整个页面映射为一个多层节点结果,开发人员可借助DOM提供的API,轻松地删除、添加、替换或修改任何节点。 PS:DOM也有级别,分为DOM1、DOM2、DOM3,拓展不少规范和新接口。
-
BOM (浏览器对象模型) 支持可以访问和操作浏览器窗口的浏览器对象模型,开发人员可以控制浏览器显示的页面以外的部分。 PS:BOM未形成规范
ES5
ES5是ECMAScript第五个版本(第四版因为过于复杂废弃了),增加特性如下:
-
strict模式 新增严格模式,限制一些用法,‘use strict’;
-
Array增加方法 新增API: every、some、forEach、filter、indexOf、lastIndexOf、 isArray、map、reduce、reduceRight、Function.prototype.bind、String.prototype.trim、Date.now方法
-
Object方法
Object.getPrototypeOf
Object.create
Object.getOwnPropertyNames
Object.defineProperty
Object.getOwnPropertyDescriptor
Object.defineProperties
Object.keys
Object.preventExtensions / Object.isExtensible
Object.seal / Object.isSealed
Object.freeze / Object.isFrozen
ES6
ECMAScript6在保证向下兼容的前提下,提供大量新特性
ES6特性如下:
块级作用域
关键字let, 常量const
对象字面量的属性赋值简写(property value shorthand)
// __proto__
__proto__: theProtoObj,
// Shorthand for ‘handler: handler’
handler,
// Method definitions
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ 'prop_' + (() => 42)() ]: 42
};
赋值解构
let { first: f, last: l } = singer; // 相当于 f = "Bob", l = "Dylan"
let [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25");
let [x, y] = [1, 2, 3]; // x = 1, y = 2
函数参数 - 默认值、参数打包、 数组展开(Default 、Rest 、Spread)
function findArtist(name='lu', age='26') {
...
}
//Rest
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
//Spread
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
箭头函数 Arrow functions
1、简化了代码形式,默认return表达式结果。 2、自动绑定语义this,即定义函数时的this。 如上面例子中,forEach的匿名函数参数中用到的this。
字符串模板 Template strings
`Hello ${name}, how are you ${time}?`
// return "Hello Bob, how are you today?"
Iterators(迭代器)+ for…of
迭代器有个next方法,调用会返回:
(1).返回迭代对象的一个元素:{ done: false, value: elem }
(2).如果已到迭代对象的末端:{ done: true, value: retVal }
console.log(n);
}
// 打印a、b、c
生成器 (Generators)
Class
Class,有constructor、extends、super,但本质上是语法糖(对语言的功能并没有影响,但是更方便程序员使用)。
constructor(name) {
this.name = name;
}
perform() {
return this.name + " performs ";
}
}
class Singer extends Artist {
constructor(name, song) {
super.constructor(name);
this.song = song;
}
perform() {
return super.perform() + "[" + this.song + "]";
}
}
let james = new Singer("Etta James", "At last");
james instanceof Artist; // true
james instanceof Singer; // true
james.perform(); // "Etta James performs [At last]"
Modules
ES6的内置模块功能借鉴了CommonJS和AMD各自的优点:
1、具有CommonJS的精简语法、唯一导出出口(single exports)和循环依赖(cyclic dependencies)的特点。
2、类似AMD,支持异步加载和可配置的模块加载。
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
Module Loaders:
// Dynamic loading – ‘System’ is default loader
System.import('lib/math').then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Directly manipulate module cache
System.get('jquery');
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
Map , Set , WeakMap, WeakSet
四种集合类型,WeakMap、WeakSet作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。
- Sets
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true
- Maps
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
- WeakMap
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
- Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
//Because the added object has no other references, it will not be held in the set
Math, Number ,String, Array , Object APIs
一些新的API
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
`[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })`
Proxies
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情。
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
可监听的操作: get、set、has、deleteProperty、apply、construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible。
Symbols
Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。
var key = Symbol("key");
var key2 = Symbol("key");
key == key2 //false
Promises
Promises是处理异步操作的对象,使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观(类似jQuery的deferred 对象)。
return new Promise(function (resolve, reject) {
// setTimeouts are for effect, typically we would handle XHR
if (!url) {
return setTimeout(reject, 1000);
}
return setTimeout(resolve, 1000);
});
}
// no url, promise rejected
fakeAjax().then(function () {
console.log('success');
},function () {
console.log('fail');
});
原文链接:https://blog.csdn.net/weixin_45406712/article/details/109215180
## ES7,Array.prototype.includes
includes方法是用来检测数组中是否包含某个元素,返回相对应得布尔类型值
- 跟indeOf()方法有点类似
let school = ['清华大学','北京大学','浙江大学'];
console.log(school.includes('清华大学')); // true
ES7,求幂运算()**
类似方法:1.自定义递归函数,2.Math.pow()方法
let num01 = Math.pow(2,10); //Math.pow()方法
let num02 = 2 ** 10; // 幂运算(**)
console.log(num01,num02); // 1024,1024
ES8,async函数,await表达式
(ES8async函数)
>>>>>Async 和 Awaiit 是 Promise 的扩展,JavaScript 是单线程的,使用 Promise 之后可以使异步操作的书写更简洁,而 Async 使 Promise 像同步操作。
async函数的返回值是Pormise对象
Promise对象的结果由async函数执行的返回值所决定
// 在申明函数的前面加"async"
async function test() {
// 只要返回的不是一个Promise类型对象,则返回的结果是成功的Promise对象的,哪怕只有“return”
return 'javascript';
throw new Error('error');
}
- 如果返回的是一个Promise对象
// 在申明函数的前面加"async"
async function test() {
return new Promise((resolve,reject) => {
// Promise对象的状态由函数内部return语句决定,即由resolve和reject的最终结果所决定
resolve('success!');
reject('errro!');
});
}
- 这时候调用then()方法
// 在申明函数的前面加"async"
async function test() {
return new Promise((resolve,reject) => {
//resolve('success!');
reject('errro!');
});
}
test().then(value => {
console.log(value); //如果状态为resolve则异步返回value
},reason => {
console.warn(reason); //如果状态为reject则异步返回value
});
(ES8await表达式)
>>>>>Await 放置在 Promise 调用之前,强制后面的代码等待,直到 Promise 对象 resolve,得到 resolve 的值作为 await 表达式的运算结果
await必须写在async函数中
await右侧的表达式一般为Promise对象
await返回的是Promise成功的值
await的Promise失败了,则会抛出异常,需要通过try-catch捕获结果
- 成功的值
let pro = new Promise((resolve,reject) => {
resolve('接受!');
});
async function test() {
let result = await pro;
console.log(result); // 返回的就是Promise成功的值
}
test();
- 失败的值(用try-catch捕获结果)
let pro = new Promise((resolve,reject) => {
reject('失败!');
});
async function test() {
try {
let result02 = await pro;
console.log(result02); //失败!
} catch(e) {
console.log(e);
}
}
test();
(ES8async函数与await表达式结合运用)
在根目录创建3个文本文件
// 单独写在js文件中,用终端直接运行
let fs = require('fs');
function readFile01() {
return new Promise((resolve,reject) => {
fs.readFile('./11.txt',(err,data) => {
if(err) reject(err);
resolve(data);
});
});
}
function readFile02() {
return new Promise((resolve,reject) => {
fs.readFile('./22.txt',(err,data) => {
if(err) reject(err);
resolve(data);
});
});
}
function readFile03() {
return new Promise((resolve,reject) => {
fs.readFile('./33.txt',(err,data) => {
if(err) reject(err);
resolve(data);
});
});
}
async function result() {
let read01 = await readFile01();
let read02 = await readFile02();
let read03 = await readFile03();
console.log(read01 + '\r');
console.log(read02 + '\r');
console.log(read03 + '\r');
}
result();
终端结果
- 两者结合发送Ajax请求
// 使用ES6 Promise的then方法
// 发送get请求,返回Promise对象
function XML(url) {
let x = new XMLHttpRequest();
return new Promise((resolve,reject) => {
x.open('get',url);
x.send();
x.onreadystatechange = () => {
if(x.readyState === 4) {
if(x.status >= 200 < 300) {
resolve(x.response);
}else {
reject(x.status);
}
}
}
});
}
let result = XML('./22.txt').then(value => {
console.log(value); // (控制台输出文本结果)
},reason => {
});
\
// 使用async和await结合使用
// 发送get请求,返回Promise对象
function XML(url) {
let x = new XMLHttpRequest();
return new Promise((resolve,reject) => {
x.open('get',url);
x.send();
x.onreadystatechange = () => {
if(x.readyState === 4) {
if(x.status >= 200 < 300) {
resolve(x.response);
}else {
reject(x.status);
}
}
}
});
}
async function result() {
let result = await XML('./22.txt');
console.log(result);
}
result(); // (控制台输出文本结果)
ES8,对象方法扩展
包含【Object.value、Object.entries、Object.getOwnPropertyDescriptors】*
//---
let school = {
name:'TsingHua',
subject:['math','chinese']
}
// 获取对象所有的键值
console.log(Object.keys(school)); // ['name', 'subject']
console.log(Object.values(school)); // ['TsingHua', Array(2)]
console.log(Object.entries(school)); // [Array(2), Array(2)]
// 方便创建Map
let result = new Map(Object.entries(school));
console.log(result.get('subject')); // Object.entries返回的是一个数组['math','chinese']
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for-in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。
// getOwnPropertyDescriptors返回的是对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school));
返回的是一个对象,里面也有键有值
\
展开这个对象,会看到键对应的值不一样,是因为Object.Create创建对象时属性描述对象的一个结构
// Object.getOwnPropertyDescriptors()返回的就是以下对象,可以更深层次地对对象进行克隆
let obj = Object.create(null, {
name:{
value:'TsingHua',
writable:true,
configurable:true,
}
});
ES9,扩展运算符和Rest参数
>>>>>Rest参数与spread扩展运算符在ES6中已经加入到原生js里面,但是ES6中只针对于数组,在ES9中它们也支持对象
// Rest参数语法允许我们将一个剩余参数表示为一个数组
function foo(a, b, ...rest) {
return [a,b,...rest];
}
let result = foo(1, 2, 3, 4, 5);
console.log(result); //[1, 2, 3, 4, 5]
// ES9的Rest参数支持对象
let university01 = {
name:'清华大学',
};
let university02 = {
address:'BeiJing',
};
let university03 = {
subject:'ComputerScience'
};
let result = {...university01,...university02,...university03};
console.log(result); //{name: '清华大学', address: 'BeiJing', subject: 'ComputerScience'}
跟数组一样,Rest参数只能在声明的结尾处使用
展开操作符则是将数组转换成可传递给函数的单独参数
ES9,正则扩展,命名捕获分组
正则扩展,命名捕获分组---分组匹配到的结果进行命名,对结果进行处理>>>>>
// 没有进行命名捕获分组的处理
//声明一个字符串
let Link = ' <a href="https://www.baidu.com">百度</a>';
//我们可以提取Link字符串中的网址以及标签里面的文本信息
let reg01 = /<a href = "(.*)">(.*)</a>/;
let result01 = reg01.exec(Link01);
console.log(result01);
控制台截图
- 第0个元素是正则整个匹配到的结果
第1个元素是正则是第一个“(.)”匹配到的结果
第2个元素是正则是第一个“(.)”匹配到的结果
其中2和3都可以称为捕获>>>得到如下结果
console.log(result01[1]); //https://www.baidu.com
console.log(result01[2]); //百度
------------------------------------------分割线--------------------------------------------
// 运用分组
let Link02 = '<a href="https://www.baidu.com"></a>';
// -----规定语法-----
//在未运用分组的基础上,在需要捕获的括号内部添加 “?<捕获标识符>.*”
let reg02 = /<a href="(?<url>.*)">(?<text>.*)</a>/
const result02 = reg02.exec(Link02);
console.log(result02.groups.url);
控制台截图
- 上面的0、1、2都还在,在“groups”中多了上面运用分组所捕获的数据
console.log(result02.groups.url); //https://www.baidu.com
console.log(result02.groups.text); //百度
这样一来我们提取正则所捕获的数据会更加地方便和便于维护
ES9,正则扩展,反向断言
反向断言,用于判断匹配结果是否正确
// 先使用正向断言
// 提取 “TsinghuaUniversity2030”
let content = '清华大学TsinghuaUniversity2030级';
// 正向断言
let regExctart = /\w+(?=级)/;
let result = regExctart.exec(content);
console.log(result[0]); //TsinghuaUniversity2030
捕获到的结果就是“级”前面的字符串
// 再使用反向断言
// 提取 “TsinghuaUniversity2030”
let content = '清华大学TsinghuaUniversity2030级';
// 反向断言
let regExctart = /(?<=学)\w+/;
let result = regExctart.exec(content);
console.log(result[0]); //TsinghuaUniversity2030
结果是与正向断言结果一致
ES9,正则扩展,dotAll模式
JavaScript正则表达式中点(.)是一个特殊字符,它可以匹配除了一下条件的任意字符。
四个字节的UTF-16字符
换行符(\n)
回车符(\r)
行分隔符
段分隔符
为了使点(.)可以匹配任意字符,ES2018引入新的修饰符s。这种模式被称为dotAll模式,根据字面意思便是dot(.)匹配一切字符。
const re = /foo.bar/s
// 判断是否启用dotAll模式
console.log(re.dotAll) // true
正则表达式中,点(.)是一个特殊字符,代表除了换行符以外的任意单个字符。
<ul>
<li>
<a>《JavaScript数据结构》</a>
<p>出版日期:2020-10-24</p>
</li>
<li>
<a>《JavaScript算法》</a>
<p>出版日期:2021-10-24</p>
</li>
</ul>
往常的做法在捕捉的时候要把多余的空格添加上去
dot正好可以更方便的处理这个问题
let reg = /<li>.*?<a>(.*?)</a>.*?<p>(.*?)</p>/gs;
let result;
while(result = reg.exec(bookStr)) {
console.log(result);
}
提取内容的话也非常方便
let data = []; //定义一个数组
while(result = reg.exec(bookStr)) {
data.push(result[1],result[2])
}
console.log(data);
提取内容的话也非常方便
ES10,对象扩展方法Object.fromEntries
Object.fromEntries()方法把键值对列表转换为一个对象,该方法创建对象,参数接收二维数组和Map>>>>>
// 定义一个二维数组
let content = Object.fromEntries([ ['name', 'javascript算法'],
['data', '2021-10-24']
]);
console.log(content);
// 定义一个Map对象
let mapObj = new Map();
// 传入参数
mapObj.set('name', 'javascript算法');
let result = Object.fromEntries(mapObj);
console.log(result);
控制台结果
- 用回ES8的Object.entries方法
let arr = Object.entries({
name: 'javascipt算法'
});
console.log(arr);
控制台结果
entries()方法可以把对象转化为数组,Object.fromEntries则把二维数组转化为对象
>>>>>Object.fromEntries和ES8里面的entries()方法为相互的逆运算(互逆)
ES10,字符串扩展方法【trimStart,trimEnd】
trimStart、trimEnd可以清楚多余的空白>>>>>
//声明一个字符串
let arr = ' JavaScript '; //前后都有空白
console.log(arr);
未清除状态
console.log(arr.trimStart()); //清楚前面的空白
清楚前面的空白
console.log(arr.trimEnd()); //清楚后面的空白
清楚后面的空白
trimStart清除左侧空白,trimEnd清楚右侧空白
ES10,数组方法扩展flat、flatMap
flat()方法可以将多维数组展平成一维数组>>>>>
let arrFlat01 = [1,2,3,[4,5,6]];
console.log(arrFlat01.flat()); //[1, 2, 3, 4, 5, 6]
flat()里面传递的参数为数字,数组的深度
let arrFlat02 = [1,2,3,[4,5,6,[7,8,9]]]; //仅要一层深度
console.log(arrFlat02.flat(1)); // [1, 2, 3, 4, 5, 6, Array(3)]---Array(3)包含就是数组[7,8,9]
let arrFlat03 = [1,2,3,[4,5,6,[7,8,9]]]; //需要两层深度
console.log(arrFlat03.flat(2)); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
flatMap()方法把原来的数组处理后返回一个新的一维数组。首先使用映射函数映射每个元素,用数组包裹起来,然后将结果压缩成一个新数组。它与map连着深度值为1的flat几乎相同,但是flatMap通常在合并成一种方法的效率稍微高一些>>>
- map()和flatMap()示例
let content = [1,2,3,4,5,6];
let result = content.map(item => [item * 10]);
console.log(result);
map()示例
- 这个时候如果想把上面的map()示例结果变成一维数组来使用,可以使用flatMap()
let content = [1,2,3,4,5,6];
let result = content.flatMap(item => [item * 10]);
console.log(result);
flatMap()示例
ES10,Symbol.prototype.description
description是一个只读属性,它会返回Symbol对象的可描述的字符串>>>>>>
let content = Symbol('清华大学');
let result = content.description;
console.log(result); //清华大学
ES11,私有属性
私有属性只允许在类的内部调用,且不允许在在类中。私有属性通过 # 申明
class School {
// 定义公有属性
name;
// 定义私有属性
#
income;
constructor(name, income) {
this.name = name;
this.#income = income;
}
}
let TsingHua = new School('清华大学', '1w');
console.log(TsingHua); // School{name: '清华大学', #income: '1w'}
//打印输入#incom这个私有属性
console.log(TsingHua.#income); //Private field '#income' must be declared in an enclosing class.
私有属性是无法访问得到的,在类的外部访问不到私有属性
// 我们要在class的外部进行定义
intro() {
console.log(this.#income);
}
// 调用
TsingHua.intro(); // 1w
ES11,Promise.allSettled方法
Promise.allSettled方法返回一个在所有给定的promise都已经fulfilled或reject后的promise,并带有一个对象数组,每个对象表示对应的promise结果>>>>>
let content = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('内容一');
});
})
let content02 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('内容一');
});
});
Promise.allSettled()返回的结果始终是成功的
// 调用 Promise.all()方法
let result = Promise.all([content, content02]);
console.log(result);
Promise.all()方法,只要有一失败则直接返回失败
按使用场景来区分:
如果想每个异步任务都想得到同一个结果,使用Promise.allSettled方法
如果想每个异步任务要求每个都成功才能够继续往下执行,使用Promise.all()方法
ES11,String.prototype.matchAll()方法
String.prototype.matchAll()方法,用来得到正则批量匹配得到的结果
// String.prototype.matchAll()方法
let str =
`<ul>
<li>
<a>《JavaScript数据结构》</a>
<p>出版日期:2020-10-24</p>
</li>
<li>
<a>《JavaScript算法》</a>
<p>出版日期:2021-10-24</p>
</li>
</ul>`;
let reg = /<li>.*?<a>(.*?)</a>.*?<p>(.*?)</p>/sg;
let result = str.matchAll(reg);
console.log(result);
从“next”可看出返回的结果是可迭代对象
// 运用for-of进行遍历得到结果
for (let i of result) {
console.log(i);
}
// 运用扩展运算符得到结果
let arr = [...result];
console.log(arr);
得到了整体匹配的结果
ES11,可选链操作符
可选链操作符--->(?.)的组合,允许读取位于连接对象链深处的属性的值,即可读取对象类型的参数,短路返回值是undefined,且与函数调用一起使用时,如果给定的函数不存在,则返回undefined
function language(content) {
let useLauguage = content && content.one.Good && content.one.purpose;
//可以获取到对应的值
console.log(useLauguage);
}
language({
one: {
Good: 'javascript',
purpose: 'HTML'
},
tow: {
Good: 'c++',
purpose: 'app'
}
});
//
function language(content) {
// 注释原来的条件判断,直接获取
// let useLauguage = content && content.one.Good && content.one.purpose;
let useLauguage = content.one.Good;
console.log(useLauguage);
}
//直接调用报错:Cannot read properties of undefined (reading 'one').
language();
// language({
// one:{
// Good:'javascript',
// purpose:'HTML'
// },
// tow:{
// Good:'c++',
// purpose:'app'
// }
// })
- 运用可选链操作符
function language(content) {
let useLauguage = content?.one;
console.log(useLauguage);
}
language({
one:{
Good:'javascript',
purpose:'HTML'
},
tow:{
Good:'c++',
purpose:'app'
}
});
正常获取
//
function language(content) {
let useLauguage = content?.one;
console.log(useLauguage);
}
// 就算直接调用也不会报错,控制台显示undefined
language();
ES11,动态import
在此之前,我们都是使用静态的import引入页面中所需要的功能模块。动态import的作用就算按需要来加载需要引入的功能模块>>>>>
\
1.首先在主页部分创建一个按钮实现点击按钮弹出提示框
//HTML部分
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="app.js" type="module" charset="utf-8"></script>
</head>
<body>
<button type="button" id="btn">我是一个按钮</button>
</body>
</html>
2.运用动态import直接调用已经声明且暴露的函数(直接输出传入的“module”)
// app.js部分
let btn = document.getElementById('btn');
btn.onclick = () => {
// 动态import
import('./module01.js').then(module => {
module.hello();
})
}
3.声明暴露函数
// module.js部分
export function hello() {
alert('Hello!!!!!!');
}
//动态引入import()变量失效问题
//webpack的现在的实现方式不能实现完全动态,所以可以通过字符串模板来提供部分信息给webpack
const modelpath = '/demo';
import(`@/pages${modelpath}`).then(item => {})
ES11,BigInt类型
BinInt是一种内置对象,它提供了一种方法表示大于2的53次方-1的整数。这原本是Javascript中可以用Number表示的最大数字。BigInt可以表示任意大的整数>>>>>
// 在一个整数字面量后面加“n”的方式定义一个BigInt
let num = 10n;
console.log(num); //10n
console.log(typeof num); //bigint
// 不支持浮点类型的数字
let num = 10.1n;
console.log(num); //Invalid or unexpected token
// 函数形式
let num = BigInt(10);
console.log(num); //10n
console.log(typeof num); //bigint
大数值运算---对比Javascript最大安全整数计算Number.MAX_SAFEE_INTEGER
// 超出Number.MAX_SAFE_INtEGER会出现问题
let maxNum = Number.MAX_SAFE_INTEGER;
console.log(maxNum); //9007199254740991
console.log(maxNum + 1); //9007199254740992
console.log(maxNum + 2); //9007199254740992
console.log(maxNum + 3); //9007199254740993
console.log(maxNum + 4); //9007199254740996
// 用BigInt
let maxNum = Number.MAX_SAFE_INTEGER;
console.log(BigInt(maxNum)); //9007199254740991n
console.log(BigInt(maxNum) + BigInt(1)); //9007199254740992n
console.log(BigInt(maxNum) + BigInt(2)); //9007199254740993n
console.log(BigInt(maxNum) + BigInt(3)); //9007199254740994n
console.log(BigInt(maxNum) + BigInt(4)); //9007199254740995n
由于在Number与BigInt之间进行转换会损失精度,因此建议仅在值大于2的53次方时shi用BigInt类型,并且不在这两种类型之间进行想换转换
ES11,globalThis
globalThis包含全局的this值,类似于全局对象global object>>>>>
在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 window、self 或者 frames 取到全局对象,但是在 Web Workers 中,只有 self 可以。在 Node.js 中,它们都无法获取,必须使用 global。
在松散模式下,可以在函数中返回 this 来获取全局对象,但是在严格模式和模块环境下,this 会返回 undefined。 You can also use Function('return this')(), but environments that disable eval(), like CSP in browsers, prevent use of Function in this way.
globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self 这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis。
简言之,如果说想对全局对象进行操作,可以直接忽略环境直接使用globalThis,就可以指向全局对象***
HTML文件输出控制台
使用Node.js运行index.js
ES12,**String.prototype.replaceAll---**replaceAll()
- replaceAll()返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉,替换规则可以是字符串或者正则表达式。
- 而replace()只能是替换字符串中匹配到的第一个实例字符,而不能进行全局多项匹配替换,唯一的办法是通过正则表达式进行相关规则匹配替换。
// replaceAll() 方法
let str = 'Java是世界上最好的编程语言!Java爱了爱了!';
let transformStr = str.replaceAll('Java','JavaScript');
console.log(transformStr); //JavaScript是世界上最好的编程语言!JavaScript爱了爱了!
// replace() 方法
let str02 = 'Java是世界上最好的编程语言!Java爱了爱了!';
let transformStr02 = str.replace('Java','JavaScript');
console.log(transformStr02); //JavaScript是世界上最好的编程语言!Java爱了爱了!
//注意,replaceAll在使用正则表达式的时候,如果非全局匹配(/g),则replaceAll()会抛出一个异常
let str = 'Java是世界上最好的编程语言!Java爱了爱了!';
console.log(str.replaceAll(/Java/,'JavaScript')); //TypeError
ES12,Promise.any
- 当Promise列表中的任意一个promise成功resolve则返回第一个resolve的结果状态
- 如果所有的promise均reject,则抛出异常表示所有请求失败
Promise.any([
new Promise((resolve, reject) => setTimeout(reject, 500, 'Java是世界上最好的编程语言!')),
new Promise((resolve, reject) => setTimeout(resolve, 1000, 'JavaScript是世界上最好的编程语言!')),
new Promise((resolve, reject) => setTimeout(resolve, 2000, 'Php是世界上最好的编程语言!')),
])
.then(value => console.log(value)) // JavaScript是世界上最好的编程语言!
.catch (err => console.log(err));
//还有这种情况
Promise.any([
Promise.reject('Error 1'),
Promise.reject('Error 2'),
Promise.reject('Error 3')
])
.then(value => console.log(value))
.catch (err => console.log(err))
//输出AggregateError: All promises were rejected
Promise.any与Promise.race十分容易混淆,务必注意区分,Promise.race 一旦某个promise触发了resolve或者reject,就直接返回了该状态结果,并不在乎其成功或者失败
ES12,WeakRefs
- 使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
- 当我们通过(const、let、var)创建一个变量时,垃圾收集器GC将永远不会从内存中删除该变量,只要它的引用仍然存在可访问。WeakRef对象包含对对象的弱引用。对对象的弱引用是不会阻止垃圾收集器GC恢复该对象的引用,则GC可以在任何时候删除它。
- WeakRefs在很多情况下都很有用,比如使用Map对象来实现具有很多需要大量内存的键值缓存,在这种情况下最方便的就是尽快释放键值对占用的内存。
- 目前,可以通过WeakMap()或者WeakSet()来使用WeakRefs
let map = new Map();
function doSomething(obj) {
return obj;
}
function useObject(obj) {
doSomething(obj);
let called = map.get(obj) || 0;
called++;
if (called > 1000) {
console.log('当前调用次数已经超过1000次了');
}
map.set(obj, called);
}
以上虽然可以实现我们的功能,但是会发生内存溢出,因为传递给doSomething函数的每个对象都永久保存在map中,并且不会被GC回收,因此我们可以使用WeakMap
因为是弱引用,所以WeakMap、WeakSet的键值对是不可枚举的
WeakSet和WeakMap相似,但是每个对象在WeakSet中的每个对象只可能出现一次,WeakSet中所有对象都是唯一的
let ws = new WeakSet();
let foo = {}
let bar = {}
ws.add(foo);
ws.add(bar);
ws.has(foo); //true
ws.has(bar); //true
ws.delete(foo); //删除foo对象
ws.has(foo); //false 已删除
ws.has(bar); //仍存在
WeakSet与Set相比有以下两个区别
WeakSet只能是对象集合,而不能是任何类型的任意值
WeakSet弱引用,集合中对象引用为弱引用,如果没有其他对WeakSet对象的引用,则会被GC回收
最后,WeakRef实例有一个方法deref,返回引用的原始对象,如果原始对象被回收,则返回undefined
let cache = new Map();
let setValue = (key, obj) => {
cache.set(key, new WeakRef(obj));
};
let getValue = (key) => {
const ref = cache.get(key);
if (ref) {
return ref.deref();
}
};
let fibonacciCached = (number) => {
let cached = getValue(number);
if (cached) return cached;
let sum = calculateFibonacci(number);
setValue(number, sum);
return sum;
};
对于缓存远程数据来说,这可能不是一个好主意,因为远程数据可能会不可预测地从内存中删除。在这种情况下,最好使用LRU之类的缓存>>>>>
ES12,逻辑运算符和赋值表达式
逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的
复合赋值运算符有:操作运算符:+= -= *= /= %= **=
位操作运算符:&= ^= |=
按位运算符:<<= >>= >>>=
//表达式:a op= b
//等同于:a = a op b
//表达式:a op= b
//等同于:a = a op (a = b)
//a ||= b
//等价于
//a = a || (a = b)
//a &&= b
//等价于
//a = a && (a = b)
//a ??= b
//等价于
//a = a ?? (a = b)
//为什么不再是跟以前的运算公式a = a op b一样呢,而是采用a = a op (a = b)。
因为后者当且仅当a的值为false的时候才计算赋值,
只有在必要的时候才执行分配,而前者的表达式总是执行赋值操作
//??=可用来补充/初始化缺失的属性
let school = [{
name: '清华大学',
subject: '/'
},
{
subject: '/other'
},
]
for (let schoolName of school) {
schoolName.name ??= '默认排名';
}
console.table(school)
//(index) title path
//0 "主会场" "/"
//1 "默认标题" "/other
&&=:当LHS值存在时,将RHS变量赋值给LHS
||=:当LHS值不存在时,将RHS变量赋值给LHS
??= :当LHS值为null或者undefined时,将RHS变量赋值给LHS >>>>>
ES12,数字分隔符
数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
let money01 = 1_000_000_000;
//等价于
let money02 = 1000000000;
let totalFee01 = 1000.12_34;
//等价于
let totalFee02 = 1000.1234;
//同样支持在八进制数中使用
let num01 = 0o123_456
//等价于
let num02 = 0o123456
console.log(money01,money02); //1000000000 1000000000
console.log(totalFee01,totalFee02); //1000.1234 1000.1234
console.log(num01,num02); //42798 42798
本文章主用于技术研发学习,感谢知乎猪哥的文章,让我充满电, 文章原文链接 知乎猪哥ES7~ES12新特性基础笔记整理 - 猪哥的文章 - 知乎 zhuanlan.zhihu.com/p/412899392