ES6_11知识概括

60 阅读26分钟

@TOC

ES介绍

什么是 ECMA:

  • ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该 组织改名为 Ecma 国际。

什么是 ECMAScript:

  • ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。

什么是 ECMA-262:

  • Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个。
  • 所有标准列表查看:链接

ECMA-262 历史:

  • ECMA-262(ECMAScript)历史版本查看网址
版本年份描述
第 1 版1997 年制定了语言的基本语法
第 2 版1998 年较小改动
第 3 版1999 年引入正则、异常处理、格式化输出等。IE 开始支持
第 4 版2007 年过于激进,未发布
第 5 版2009 年引入严格模式、JSON,扩展对象、数组、原型、字符串、日期方法
第 6 版2015 年模块化、面向对象语法、Promise、箭头函数、let、const、数组解构赋值等等
第 7 版2016 年幂运算符、数组扩展、Async/await 关键字
第 8 版2017 年Async/await、字符串扩展
第 9 版2018 年对象解构赋值、正则扩展
第 10 版2019 年扩展对象、数组方法
ES.next动态指向下一个版本
  • 注:从 ES6 开始,每年发布一个版本,版本号比年份最后一位大 1

谁在维护 ECMA-262:

  • TC39(Technical Committee 39)是推进 ECMAScript 发展的委员会。
  • 其会员都是 公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39 定期 召开会议,会议由会员公司的代表与特邀专家出席

为什么要学习 ES6:

  • ES6 的版本变动内容最多,具有里程碑意义
  • ES6 加入许多新的语法特性,编程实现更简单、高效
  • ES6 是前端发展趋势,就业必备技能

桌面端浏览器对ES2015的支持情况:

  • Chrome:51 版起便可以支持 97% 的 ES6 新特性。
  • Firefox:53 版起便可以支持 97% 的 ES6 新特性。
  • Safari:10 版起便可以支持 99% 的 ES6 新特性。
  • IE:Edge 15可以支持 96% 的 ES6 新特性。Edge 14 可以支持 93% 的 ES6 新特性。(IE7~11 基本不支持 ES6)

移动端浏览器对ES2015的支持情况:

  • iOS:10.0 版起便可以支持 99% 的 ES6 新特性。
  • Android:基本不支持 ES6 新特性(5.1 仅支持 25%)

服务器对ES2015的支持情况:

  • Node.js:6.5 版起便可以支持 97% 的 ES6 新特性。(6.0 支持 92%)

如何使用ES6的新特性,又能保证浏览器的兼容?

  • 针对 ES6 的兼容性问题,很多团队为此开发出了多种语法解析转换工具,把我们写的 ES6 语法转换成 ES5,
  • 相当于在 ES6 和浏览器之间做了一个翻译官。比较通用的工具方案有 babel,jsx,traceur,es6-shim 等

ES6

let 关键字:

  • let 关键字用来声明变量,使用 let 声明的变量有几个特点: ①不允许重复声明块儿级作用域 不存在变量提升不影响作用域链
  • 应用场景:以后声明变量使用 let 就对了

const 关键字:

  • const 关键字用来声明常量,const 声明有以下特点: ①声明必须赋初始值标识符一般为大写不允许重复声明值不允许修改块儿级作用域
  • 注意: 对象属性修改和数组元素变化不会出发 const 错误(因为对象和数组为引用地址)
  • 应用场景:声明对象类型使用 const,非对象类型声明选择 let

变量的解构赋值:

  • ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
//数组的解构赋值
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 {songs: [one, two, three], history: [first, second, third]} = 
wangfei;
  • 注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

函数参数默认值:

  • ES6 允许给函数参数赋值初始值
  • 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)
//函数参数默认值
function add(a,c=10,b) {
	return a + b + c;
}
let result = add(1,2);
console.log(result);

//与解构赋值结合
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
})

模板字符串:

  • 模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点: ①字符串中可以出现换行符可以使用 ${xxx} 形式输出变量
// 定义字符串
let str = `<ul>
		  <li>沈腾</li>
		 <li>玛丽</li>
		 <li>魏翔</li>
		 <li>艾伦</li>
		 </ul>`;
// 变量拼接
let star = '王宁';
let result = `${star}在前几年离开了开心麻花`;
  • 注意:当遇到字符串与变量拼接的情况使用模板字符串

简化对象写法:

  • ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = '尚硅谷';

let slogon = '永远追求行业更高标准';
let improve = function () {
 console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {
	 name,
	 slogon,
	 improve,
	 change() {
		 console.log('可以改变你')
	 }
};
  • 注意:对象简写形式简化了代码,所以以后用简写就对了。

Throw 和 Try to Catch:

  • try 语句使您能够测试代码块中的错误。
  • catch 语句允许您处理错误。
  • throw 语句允许您创建自定义错误。 ①从技术上讲您能够抛出异常(抛出错误)。 ②异常可以是 JavaScript 字符串、数字、布尔或对象
  • finally 使您能够执行代码,在 try 和 catch 之后,无论结果如何。
  • JavaScript 抛出错误: ①当发生错误时,JavaScript 通常会停止并产生错误消息。 ②技术术语是这样描述的:JavaScript 将抛出异常(抛出错误)。 ③JavaScript 实际上会创建带有两个属性的 Error 对象:name 和 messageError 对象的name属性用于设置或返回错误名,message属性用于设置或返回错误消息(一条字符串)。 ⑤error 的 name 属性可返回六个不同的值: <1>EvalError 已在 eval() 函数中发生的错误 <2>RangeError 已发生超出数字范围的错误 <3>ReferenceError 已发生非法引用 <4>SyntaxError 已发生语法错误 <5>TypeError 已发生类型错误 <6>URIError 在 encodeURI() 中已发生的错误 ⑥非标准的 Error 对象属性,Mozilla 和 Microsoft 定义了非标准的 error 对象属性: <1>fileName (Mozilla) <2>lineNumber (Mozilla) <3>columnNumber (Mozilla) <4>stack (Mozilla) <5>description (Microsoft) <6>number (Microsoft)

箭头函数:

  • ES6 允许使用「箭头」(=>)定义函数。
/**
 - 1. 通用写法
*/
let fn = (arg1, arg2, arg3) => {
 return arg1 + arg2 + arg3;
}
  • 箭头函数的注意点: ①如果形参只有一个,则小括号可以省略 ②函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果 ③箭头函数 this 指向声明时所在作用域下 this 的值箭头函数不能作为构造函数实例化不能使用arguments
/**
 - 2. 省略小括号的情况
*/
let fn2 = num => {
 	return num * 10;
};
/**
 - 3. 省略花括号的情况
*/
let fn3 = score => score * 20;
/**
 - 4. this 指向声明时所在作用域中 this 的值
*/
let fn4 = () => {
 	console.log(this);
}
let school = {
 	name: '尚硅谷',
 	getName(){
 		let fn5 = () => {
 			console.log(this);
 		}
 		fn5();
 	}
};
  • 注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适
  • 此外: ①箭头函数没有this,它会从外层函数作用域中找。 <1>箭头函数中所使用的this来自于函数作用域链。 ②作用域链是针对变量来说的,而this也是一个变量。又因为每个对象都有一个this,所以对于对象来说不用找。 <1>作用域分函数和全局,没有对象作用域这一说。(不是java) ③普通函数有this,它的this如果定义在对象中,this就是对象。如果定义在全局作用域就是window。如果直接调用(没有调用者)也是window。 <1>能找到调用者就指向调用者,找不到就指向window。 ④对于需要使用object.method()方式调用的函数,使用普通函数定义,不要使用箭头函数。对象方法中所使用的this值有确定的含义,指的就是object本身。其他情况下,全部使用箭头函数。 <1>箭头函数适合与this无关的回调,例如定时器的回调,数组方法的回调。 <2>箭头函数不适合与this有关的回调,例如事件回调,对象的方法。

rest 参数:

  • ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
/**
 - 作用与 arguments 类似
*/
function add(...args){
 console.log(args);
}
add(1,2,3,4,5);
/**
 - rest 参数必须是最后一个形参
*/
function minus(a,b,...args){
 console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);
  • 注意:rest 参数非常适合不定个数参数函数的场景

spread 扩展运算符:

  • 扩展运算符(spread)也是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
/**
* 展开数组
*/ 
let tfboys = ['德玛西亚之力','德玛西亚之翼','德玛西亚皇子'];
function fn(){
 console.log(arguments);
}
fn(...tfboys)
/**
* 展开对象
*/
let skillOne = {
 q: '致命打击',
};
let skillTwo = {
 w: '勇气'
};
let skillThree = {
 e: '审判'
};
let skillFour = {
 r: '德玛西亚正义'
};
let gailun = {...skillOne, ...skillTwo,...skillThree,...skillFour};

Symbol:

  • ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
  • Symbol特点: ①Symbol的值是唯一的,用来解决命名冲突的问题 ②Symbol值不能与其他数据进行运算 ③Symbol定义的对象属性不能使用 for…in 循 环遍历,但是可以使用Reflect.ownKeys 来获取对象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1, typeof s1);
//添加标识的 Symbol
let s2 = Symbol('尚硅谷');
let s2_2 = Symbol('尚硅谷');
console.log(s2 === s2_2);
//使用 Symbol for 定义
let s3 = Symbol.for('尚硅谷');
let s3_2 = Symbol.for('尚硅谷');
console.log(s3 === s3_2);//true
  • 注: 遇到唯一性的场景时要想到 Symbol
  • 在使用Symbol做变量名或方法名时要加上[],因为Symbol('尚硅谷')容易被以为是方法。
  • 除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
内置值作用
Symbol.hasInstance当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。
Symbol.species创建衍生对象时,会使用该属性
Symbol.match当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。
Symbol.replace当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。
Symbol.search当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值。
Symbol.split当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值。
Symbol.iterator对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器
Symbol.toPrimitive该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol. toStringTag在该对象上面调用 toString 方法时,返回该方法的返回值
Symbol. unscopables该对象指定了使用 with 关键字时,哪些属性会被 with环境排除。

迭代器:

  • 遍历器(Iterator)就是一种机制。
  • 它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator接口,就可以完成遍历操作。
  • 这个接口也就是一个Symbol属性。准确说是一个方法---> [Symbol.iterator]()
  • ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。
  • 原生具备 iterator 接口的数据(可用 for of 遍历) ①Array ②Arguments ③Set ④Map ⑤String ⑥TypedArray ⑦NodeList
  • 工作原理 ①创建一个指针对象,指向当前数据结构的起始位置第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员每调用 next 方法返回一个包含 value 和 done 属性的对象
  • 注: 需要自定义遍历数据的时候,要想到迭代器。
//声明一个对象
const banji = {
    name: "终极一班",
    stus: [
        'xiaoming',
        'xiaoning',
        'xiaotian',
        'knight'
    ],
    //方法(方法名为 Symbol.iterator)
    [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);
}

生成器:

  • 生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
function * gen(){

 	yield '一只没有耳朵';
 	
 	yield '一只没有尾巴';
 	
 	return '真奇怪'; 
 }
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
  • 代码说明: ①*的位置没有限制生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码 <1>yield前的代码由调用定义的函数所执行。 ④next 方法可以传递实参,作为 yield 语句的返回值
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');
//逐个获取111,222,333。
console.log(iterator.next());

//next方法可以传入实参作为yield的返回值。
console.log(iterator.next('BBB'));
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD'));

Promise:

  • Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。 ①Promise 构造函数: Promise (excutor) {} ②Promise.prototype.then 方法 ③Promise.prototype.catch 方法
//实例化 Promise 对象
const p = new Promise(function(resolve, reject){
    setTimeout(function(){
    	//成功
        let data = '数据库中的用户数据';
        resolve(data);
		//失败
        let err = '数据读取失败';
        reject(err);
    }, 1000);
});

//调用 promise 对象的 then 方法
p.then(function(value){
	//成功
    console.log(value);
}, function(reason){
	//失败
    console.error(reason);
})
  • 调用 then 方法 then方法的返回结果是 Promise 对象, 对象状态由回调函数的执行结果决定。 ①如果回调函数中返回的结果是 非 promise 类型的属性, 状态为成功, 返回值为对象的成功的值 ②如果回调函数中返回的结果是promise 类型的属性, 状态由promise决定。 ③如果直接throw new Error则为失败。
 const result = p.then(value => {
	 console.log(value);
	 //1. 非 promise 类型的属性
	 return 'iloveyou';
	 //2. 是 promise 对象
	 return new Promise((resolve, reject)=>{
		 //成功
		 resolve('ok');
		 //失败
	 	 reject('error');
	  });
	 //3. 抛出错误
	 //抛出Error对象
	 throw new Error('出错啦!');
	 //抛出字符串
	 throw '出错啦!';
	 
	 }, reason=>{
		 console.warn(reason);
});

//可以链式调用
p.then(value=>{

}).then(value=>{

});

Set:

  • ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯 一的,集合实现了 iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法: ①size 返回集合的元素个数 ② add 增加一个新元素,返回当前集合 ③ delete 删除元素,返回 boolean 值 ④ has 检测集合中是否包含某个元素,返回 boolean 值 ⑤ clear 清空集合,返回 undefined
//创建一个空集合
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());

Map:

  • ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属 性和方法: ①size 返回 Map 的元素个数 ② set 增加一个新元素,返回当前 Map ③ get 返回键名对象的键值 ④ has 检测 Map 中是否包含某个元素,返回 boolean 值 ⑤ clear 清空集合,返回 undefined
//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map([
 ['name','尚硅谷'],
 ['slogon','不断提高行业标准']
]);
//属性和方法
//获取映射元素的个数
console.log(m2.size);
//添加映射值
console.log(m2.set('age', 6));
//获取映射值
console.log(m2.get('age'));
//检测是否有该映射
console.log(m2.has('age'));
//清除
console.log(m2.clear());

class 类:

  • ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对 象的模板。通过 class关键字,可以定义类。基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class写法只是让对象 原型的写法更加清晰、更像面向对象编程的语法而已。
  • 知识点: ①class 声明类 ② constructor 定义构造函数初始化 ③ extends 继承父类 ④ super 调用父级构造方法 ⑤ static 定义静态方法和属性 <1>静态方法和属性只能被对象调用。 <2>静态方法和属性不能被实例调用。 ⑥ 父类方法可以重写
//父类
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();

数值扩展:

  • ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
  • Number.isFinite() 用来检查一个数值是否为有限的,Number.isNaN() 用来检查一个值是否为 NaN
  • ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。
  • Math.trunc:用于去除一个数的小数部分,返回整数部分。
  • Number.isInteger() 用来判断一个数值是否为整数。

对象扩展:

  • ES6 新增了一些 Object 对象的方法
  • Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
  • Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
  • Object.setPrototypeOf 设置原型对象, Object.getPrototypeof获取原型对象。

多线程和异步

异步和多线程的区别是什么呢?

  • 异步和多线程两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为异步和多线程是等同的概念。
  • 但是,异步和多线程还是有一些区别的。而这些区别造成了使用异步和多线程的时机的区别。

异步操作的本质:  

  • 所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。
  • 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。
  • DMA就是直接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。
  • 这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础所以即使在DOS这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。

线程的本质:

  • 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度。

异步操作的优缺点:  

  • 因为异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少共享变量的数量),减少了死锁的可能。
  • 当然异步操作也并非完美无暇。编写异步操作的复杂程度较高,程序主要使用回调方式进行处理,与普通人的思维方式有些出入,而且难以调试。

多线程的优缺点: 

  • 多线程的优点很明显,线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。

适用范围:  

  • 在了解了线程与异步操作各自的优缺点之后,我们可以来探讨一下线程和异步的合理用途。我认为:当需要执行I/O操作时,使用异步操作比使用线程+同步 I/O操作更合适。
  • I/O操作不仅包括了直接的文件、网络的读写,还包括数据库操作、Web Service、HttpRequest以及.netRemoting等跨进程的调用。  
  • 而线程的适用范围则是那种需要长时间CPU运算的场合,例如耗时较长的图形处理和算法执行。但是往往由于使用线程编程的简单和符合习惯,所以很多朋友往往会使用线程来执行耗时较长的I/O操作。
  • 这样在只有少数几个并发操作的时候还无伤大雅,如果需要处理大量的并发操作时就不合适了。

模块化

模块化溯源:

  • 早期 JavaScript 开发 全局污染 和依赖管理混乱。
  • 因此需要进行模块化管理。
  • 而模块化的第一步是打包成模块再暴露出去,因此也就衍生出webpack等打包工具。
  • 打包成一个个模块后,在使用时再引入模块。

模块化:

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

模块化的好处:

  • 模块化的优势有以下几点: ① 防止命名冲突 ② 代码复用 ③ 高维护性

模块化规范产品:

  • ES6 之前的模块化规范有(后面对应的实现方案): ① CommonJS => NodeJS、Browserify ② AMD => requireJS ③ CMD => seaJS

使用模块化html时的变化:

  • 使用模块化html的script标签中不再是使用text/javascript
<script type="text/javascript" src="jquery.js" > </script>。
  • 而是使用module。
<script type="module" src="jquery.js" > </script>。

ES6 模块化语法:

  • 模块功能主要由两个命令构成:export 和 import。 ①export 命令用于规定模块的对外接口 ②import 命令用于输入其他模块提供的功能
//入口文件

//模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";


//不加* as
//只针对默认暴露
import $ from 'jquery';// 
//分别暴露
export let school = '尚硅谷';

export function teach() {
    console.log("我们可以教给你开发技能");
}
//默认暴露
export default {
    school: 'ATGUIGU',
    change: function(){
        console.log("我们可以改变你!!");
    }
}
//统一暴露
let school = '尚硅谷';

function findJob(){
    console.log("我们可以帮助你找工作!!");
}

export {school, findJob};

Commonjs模块化语法

  • CommonJS 的一个模块就是一个脚本文件,通过执行该文件来加载模块。
  • 该模块中,包含 CommonJS 规范的核心变量:exportsmodule.exportsrequire。 ①module 记录当前模块信息。 ②require 引入模块的方法。 ③exports 当前模块导出的属性 <1>exports 和 module.exports 可以负责对模块中的内容进行导出。 <2>require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容。
  • CommonJS 规范规定,每个模块内部, module变量代表当前模块。这个变量是一个对象,它的 exports 属性(即module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。
//我们见过这样的模块引用:
var myModule = require('module');
myModule.sayHello();
//这是因为我们把模块的方法定义在了模块(module)的属性上:

// module.js
module.exports.sayHello = function() {
    console.log('Hello ');
};

// 如果这样写
module.exports = sayHello;

// 调用则需要改为
var sayHello = require('module');
sayHello();

babel详解:

  • babel简单来说把 JavaScript 中 es2015/2016/2017/2046 的新语法转化为 es5,让低端运行环境(如浏览器和node )能够认识并执行。
  • 严格来说,babel 也可以转化为更低的规范。但以目前情况来说,es5 规范已经足以覆盖绝大部分浏览器,因此常规来说转到 es5是一个安全且流行的做法。
  • 总共存在三种方式: ①使用单体文件 (standalone script) ②命令行 (cli) ③构建工具的插件 (webpack 的 babel-loader, rollup 的 rollup-plugin-babel)。
  • 其中后面两种比较常见。第二种多见于 package.json 中的 scripts 段落中的某条命令;第三种就直接集成到构建工具中。
  • 这三种方式只有入口不同而已,调用的 babel 内核,处理方式都是一样的。

ES7

Array.prototype.includes:

  • Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值

指数操作符:

  • 在 ES7 中引入指数运算符「**」,用来实现幂运算,功能与 Math.pow 结果相同

ES8

async 和 await:

  • async 和 await 两种语法结合可以让异步代码像同步代码一样。

async 函数:

  • async 函数的返回值为 promise 对象,
  • promise 对象的结果由 async 函数执行的返回值决定。
//async 函数
async function fn(){
    // 返回一个字符串
    // return '尚硅谷';
    // 返回的结果不是一个 Promise 类型的对象, 返回的结果就是成功 Promise 对象
    // return;
    //抛出错误, 返回的结果是一个失败的 Promise
    // throw new Error('出错啦!');
    //返回的结果如果是一个 Promise 对象
    return new Promise((resolve, reject)=>{
        resolve('成功的数据');
        // reject("失败的错误");
    });
}

const result = fn();

//调用 then 方法
result.then(value => {
    console.log(value);
}, reason => {
    console.warn(reason);
})

await 表达式:

  • await 必须写在 async 函数中
  • await 右侧的表达式一般为 promise 对象
  • await 返回的是 promise 成功的值
  • await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
//创建 promise 对象
const p = new Promise((resolve, reject) => {
    // resolve("用户数据");
    reject("失败啦!");
})

// await 要放在 async 函数中.
async function main() {
    try {
        let result = await p;
        //
        console.log(result);
    } catch (e) {
        console.log(e);
    }
}
//调用函数
main();

Object.values 和 Object.entries:

  • Object.values()方法返回一个给定对象的所有可枚举属性值的数组
  • Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组

Object.getOwnPropertyDescriptors:

  • 该方法返回指定对象所有自身属性的描述对象
const obj = Object.create(null, {
	name: {
		//设置值
		value: '尚硅谷',
		//属性特性
		writable: true,//可编辑
		configurable: true,//可删除
		enumerable: true//可枚举
	} 
});

ES9

Rest/Spread 属性:

  • Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组, 在 ES9 中为对象提供了像数组一样的rest 参数和扩展运算符
function connect({host, port, ...user}) {
	 console.log(host);
	 console.log(port);
	 console.log(user);
}
connect({
	 host: '127.0.0.1',
	 port: 3306,
	 username: 'root',
	 password: 'root',
	 type: 'master'
});

正则表达式命名捕获组:

  • ES9 允许命名捕获组使用符号『?』,这样获取捕获结果可读性更强
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);
console.log(result.groups.text);

正则表达式反向断言:

  • ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
//声明字符串
let str = 'JS5211314 你知道么 555 啦啦啦';
//正向断言
const reg = /\d+(?=啦)/;
const result = reg.exec(str);
//反向断言
const reg = /(?<=么)\d+/;
const result = reg.exec(str);
console.log(result);

正则表达式 dotAll 模式:

  • 正则表达式中点.匹配除回车外的任何单字符,标记『s』改变这种行为,允许行 终止符出现
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>/gs;
//执行匹配
const result = reg.exec(str);
let result;
let data = [];
while(result = reg.exec(str)){
 data.push({title: result[1], time: result[2]});
}
//输出结果
console.log(data);

ES10

Object.fromEntries:

//二维数组
const result = Object.fromEntries([
     ['name','尚硅谷'],
     ['xueke', 'Java,大数据,前端,云计算']
]);

//Map
const m = new Map();
m.set('name','ATGUIGU');
const result = Object.fromEntries(m);

//Object.entries ES8
const arr = Object.entries({
     name: "尚硅谷"
})
console.log(arr);

trimStart 和 trimEnd:

// trim
let str = '   iloveyou   ';

console.log(str);
console.log(str.trimStart());
console.log(str.trimEnd());

Array.prototype.flat 与 flatMap:

//flat 平
//将多维数组转化为低位数组
const arr = [1,2,3,4,[5,6]];
const arr = [1,2,3,4,[5,6,[7,8,9]]];
//参数为深度 是一个数字
console.log(arr.flat(2));  

//flatMap
const arr = [1,2,3,4];
const result = arr.flatMap(item => [item * 10]);
console.log(result);

Symbol.prototype.description:

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

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);

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

const arr = [...result];

console.log(arr);

类的私有属性:

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

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

console.log(girl.name);
console.log(girl.#age);
console.log(girl.#weight);

girl.intro();

Promise.allSettled:

//声明两个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]);

// const res = Promise.all([p1, p2]);

console.log(res);

可选链操作符:

  • ?.表示一层
// ?.表示一层
function main(config){
    // const dbHost = config && config.db && config.db.host;
    const dbHost = config?.db?.host;

    console.log(dbHost);
}

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

动态 import 导入:

  • 导入模块时不是全部导入,而是按需加载,也就是懒加载。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>动态 import </title>
</head>
<body>
    <button id="btn">点击</button>
    <script src="./js/app.js" type="module"></script>
</body>
</html>
// import * as m1 from "./hello.js";
//获取元素
const btn = document.getElementById('btn');

btn.onclick = function(){
    import('./hello.js').then(module => {
        module.hello();
    });
}

BigInt:

  • BigInt为大整形
//大整形
// let n = 521n;
// console.log(n, typeof(n));

//函数
// let n = 123;
// console.log(BigInt(n));
// console.log(BigInt(1.2));

//大数值运算
let max = Number.MAX_SAFE_INTEGER;
console.log(max);
console.log(max + 1);
console.log(max + 2);

console.log(BigInt(max))
console.log(BigInt(max) + BigInt(1))
console.log(BigInt(max) + BigInt(2))

globalThis 对象:

  • JavaScript 语言越来越被广泛地用于各种环境中。除了 Web 浏览器(这是 JavaScript的最常见的宿主环境类型)之外,你还可以在服务器,智能手机甚至机器人硬件中运行 JavaScript 程序。
  • 每个环境都有其自己的对象模型,并提供了不同的语法来访问全局对象。例如,在Web浏览器中,可以通过 window,self 或 frames访问全局对象。但是在 Node.js 中,这些属性不存在,而你必须使用 global。在 Web Worker 中,只有 self 可用。
  • 这些引用全局对象的不同方式使编写能够在多个环境中工作的可移植 JavaScript代码变得非常困难。幸运的是,有一个正在开发中的提案打算通过引入一个名为 globalThis的标准属性来解决这个问题,该属性将在所有环境中可用。