# ESAMScript

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

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

Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个,所有标准列表查看 http://www.ecma-international.org/publications/standards/Standard.htm

历史版本:https://www.ecma-international.org/publications-and-standards/standards/ecma-262/

版本 年份 新特性
第 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 扩展对象、数组方法

# ES6

# 1. let

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  1. 不允许重复声明
  2. 块儿级作用域
  3. 不存在变量提升(不允许在声明变量之前使用变量)
  4. 不影响作用域链

应用场景:以后声明变量使用 let 就对了。

// 遍历并绑定事件
// 可以
for(var i=0;i<items.length;i++){
  items[i].onclick = function(){
    this.style.background = 'pink';
  }
}

// 不可以
for(var i=0;i<items.length;i++){
  items[i].onclick = function(){
    items[i].style.background = 'pink';
  }
}

// 可以
for(let i=0;i<items.length;i++){
  items[i].onclick = function(){
    items[i].style.background = 'pink';
  }
}

# 2. const

const 关键字用来声明常量,const 声明有以下特点:

  1. 声明必须赋初始值
  2. 标识符一般为大写
  3. 不允许重复声明
  4. 值不允许修改
  5. 块儿级作用域

注意:对象属性修改和数组元素变化不会触发 const 错误。

应用场景:声明对象类型使用 const,非对象类型声明选择 let。

# 3. 变量的解构赋值

ES6 允许按照一定模式(变量同名),从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。

//数组的解构赋值
const ARR = ['张学友', '刘德华', '黎明', '郭富城'];
let [zhang, liu, li, guo] = ARR;
console.log([zhang, liu, li, guo]);

//对象的解构赋值
const LIN = {
  name: '林志颖',
  tags: ['车手', '歌手', '小旋风', '演员']
};
let {name, tags} = LIN;
console.log({name, tags})

//复杂解构
let WANGFEI = {
  name: '王菲',
  age: 18,
  songs: ['红豆', '流年', '暧昧', '传奇'],
  history: [
    {name: '窦唯'},
    {name: '李亚鹏'},
    {name: '谢霆锋'}
  ]
};
let {songs: [one, two, three], history: [first, second, third]} = WANGFEI;
console.log({songs: [one, two, three], history: [first, second, third]});

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式。

# 4. 模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:

  1. 字符串中可以出现换行符
  2. 可以使用 ${xxx} 形式输出变量
//定义字符串
let str = `<ul>
            <li>沈腾</li>
            <li>玛丽</li>
            <li>魏翔</li>
            <li>艾伦</li>
          </ul>`;
// 变量拼接
let star = '王宁';

// 取值
let result = `${star}在几年前离开了开心麻花`;
console.log(str)
console.log(result)

应用场景:字符串结构复杂和需要变量拼接的时候使用模板字符串。

# 5. 简化对象写法

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

let name = 'hedon';
let slogon = '团结就是力量';
let improve = function(){
  	console.log("hello world");
}

//属性和方法简写
let hedonInfo = {
    name,
    slogon,
    improve,
    changeInfo(){
      console.log("change the world");
    }
}

# 6. 箭头函数

ES6 允许使用「箭头」(=>)定义函数。

//通用写法
let fn = (arg1, arg2, arg3) => {
   return arg1 + arg2 + arg3;
}

注意点:

  1. 如果形参只有一个,则小括号可以省略
  2. 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
  3. 箭头函数 this 是静态的,指向声明时所在作用域下的 this 的值
  4. 箭头函数不能作为构造函数实例化
  5. 不能使用 arguments
//省略小括号
let fn2 = num => {
  	return num * 10;
}

//省略花括号
let fn3 = score => score * 20;

//this 指向声明时所在作用域中 this 的值
let getName1 = () => {
  	console.log(this.name);
}

function getName2() {
  	console.log(this.name);
}

//设置 windows 对象的 name 属性
window.name = "hedon";
const SCHOOL = {
  	name: "SCHOOL name"
};

//直接调用
getName1(); //hedon
getName2(); //hedon

//call 方法调用
getName1.call(SCHOOL); //hedon(不变)
getName2.call(SCHOOL); //SCHOOL name(变了)

应用场景:箭头函数不会更改 this 指向,用来指定与 this 无关的回调函数会非常合适,如:定时器,数组的方法回调;不适合与 this 有关的回调,如:事件回调,对象的方法。

# 7. 函数参数赋初值

ES6 运行给函数参数赋初始值。

// 具有默认值的参数,一般位置都靠后
function add(a,b,c=10) {
  	return a + b + c;
}

# 8. rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments。

//arguments
function add() {
  	console.log(arguments);
}
add(1,2,3,4,5);

/**
  * rest 参数必须是最后一个形参
  * ...args 就是 rest 参数
  * rest 参数其实就是一个变长数组
  */
function minus(a,b, ...args){
 		console.log(a,b,args);
}
minus(100,1,2,3,4,5,19);

应用场景:rest 参数非常适合不定个数参数函数的场景。

# 9. 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};

//数组合并
const kuaizi = ['王太利', '肖央'];
const fenghuang = ['曾毅', '玲花'];
const kuaizifenghuang = [...kuaizi, ...fenghuang];

//深拷贝
const oldArr = ['a', 'b', 'c'];
const newArr = [...oldArr];

//将伪数组转为真正的数组
const divs = document.querySelectorAll('div'); //这是一个对象
const divArr = [...divs]; //转为数组

# 10. Symbol

# 10.1 Symbol 使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。

前六种:number、string、null、undefined、boolean、object。

USONB:

​ u: undefined

​ s: string, symbol

​ o: object

​ n: null

​ b: boolean

Symbol 特点:

  1. Symbol 的值是唯一的,用来解决命名冲突的问题
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
//创建 Symbol
let s = Symbol();

//添加标识的 Symbol
let s2 = Symbol("Hedon");
let s2_2 = Symbol("Hedon");
console.log(s2 === s2_2); // false

//使用 Symbol for 定义
let s3 = Symbol.for('Hedon');
let s3_2 = Symbol.for('Hedon');
console.log(s3 === s3_2); // true 
console.log(s2 === s3);   //false

//向对象中添加方法 up down
let game = {
  	name:'俄罗斯方块',
  	up: function(){},
  	down: function(){}
};

//声明一个对象
let methods = {
    up: Symbol(),
    down: Symbol()
};
game[methods.up] = function(){
    console.log("我可以改变形状");
}
game[methods.down] = function(){
    console.log("我可以快速下降!!");
}

应用场景:适合唯一性场景。

# 10.2 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 环境排除。
//演示 Symbol.hasInstance
class Person{
  	static [Symbol.hasInstance](param){
    		console.log(param);
    		console.log("我被用来检测类型了");
    		return false;
  	}
}
let o = {};
console.log(o instanceof Person); //{}, "我被用来检测类型了"

//演示 Symbol.isConcatSpreadable
const arr = [1,2,3];
const arr2 = [4,5,6];
console.log(arr.concat(arr2)) //[1,2,3,4,5,6]
arr2[Symbol.isConcatSpreadable] = false; //设置为不展开
console.log(arr.concat(arr2)); //[1,2,3,Array(3)]

# 11. 迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。

原生具备 iterator 接口的数据(可用 for of 遍历):

  • Array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • NodeList

工作原理:

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象
// for of 使用
const xiyou = ['唐僧', '孙悟空', '猪八戒', ' 沙僧'];
for(let v of xiyou) {
  	console.log(v);
}
let iterator = xiyou[Symbol.iterator]();
console.log(iterator);

// 自定义遍历规则
const banji = {
  	name: "终极一班",
  	stus:[
      'xiaoming',
      'xiaoming',
      'xiaotian',
      'knight'
    ],
    [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);
}

# 12. 生成器

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

function *gen(arg) {
    //第一段代码
    console.log(arg)
    let y = yield 111;
    //第二段代码
    console.log(y)
    yield 222;
    //第三段代码
    console.log(y)
    yield 333;
    //第四段代码
}

// 获取迭代器对象
let iteration = gen('AAA');
console.log(iteration.next())  			//AAA 111 false
console.log(iteration.next('BBB')) 	//BBB 222 false
console.log(iteration.next()) 			//BBB 333 false
console.log(iteration.next()) 			// true
  1. * 的位置没有限制
  2. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
  3. yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  4. next 方法可以传递实参,作为 yield 语句的返回值

# 13. Promise

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数, 用来封装异步操作并可以获取其成功或失败的结果。

  1. Promise 构造函数: Promise (excutor) {}
  2. Promise.prototype.then 方法
  3. Promise.prototype.catch 方法
// Promise 对象状态
// 1. 初始化
// 2. 成功
// 3. 失败
const p = new Promise(function(resolve, reject){
    setTimeout(function(){
        //获取数据
        let data = '数据库中的数据';
        // resolve
        resolve(data); //Promise 转为成功状态

        // let data = '读取数据失败';
        // reject
        // reject(data); //Promise 转为失败状态
    },1000)
})

// 调用 Promise 对象的 then 方法
p.then(function(value){
    //成功
    console.log(value);
}, function(resaon) {
    //失败
    console.log(resaon);
})

# 13.1 Promise 封装读取文件

//引入 fs 模块
const fs = require("fs");

//使用 Promise 封装
const p = new Promise(function(resolve, reject){
    fs.readFile("Set.html", (err, data) => {
        //如果失败
        if (err) {
            reject(err);
        }
        //如果成功
        resolve(data);
    })
})

p.then(function(value){
    //成功
    console.log(value.toString());
}, function(reason){
    //失败
    console.log(err);
})

# 13.2 Promise 封装 Ajax 请求

  • 传统用法

    //接口地址:https://api.apiopen.top/getJoke
    let url = "https://api.apiopen.top/getJoke"
    
    // 1. 创建对象
    const xhr = new XMLHttpRequest();
    // 2. 初始化
    xhr.open("GET", url);
    // 3. 发送请求
    xhr.send();
    // 4. 绑定事件,处理响应结果
    xhr.onreadystatechange = function() {
    //判断状态
    //等于4说明已经进入最后一个状态了,即所有的响应都已经到达了。
    if (xhr.readyState === 4) {
        //判断响应状态码 2xx 成功
        if (xhr.status >= 200 && xhr.status < 300 ){
          //成功
          console.log(xhr.response);
        } else {
          //如果失败
          console.error(xhr.status);
        }
      }
    }
    
  • Promise 封装

    将结果处理抽离出来,避免回调地狱。

    const p = new Promise(function(resolve, reject) {
        //接口地址:https://api.apiopen.top/getJoke
        let url = "https://api.apiopen.top/getJoke"
    
        // 1. 创建对象
        const xhr = new XMLHttpRequest();
        // 2. 初始化
        xhr.open("GET", url);
        // 3. 发送请求
        xhr.send();
    
        // 4. 绑定事件,处理响应结果
        xhr.onreadystatechange = function() {
            //判断状态
            //等于4说明已经进入最后一个状态了,即所有的响应都已经到达了。
            if (xhr.readyState === 4) {
                //判断响应状态码 2xx 成功
                if (xhr.status >= 200 && xhr.status < 300 ){
                    //成功
                    resolve(xhr.response);
                } else {
                    //如果失败
                    reject(xhr.status);
                }
              }
        }
    })
    
    
    p.then(function(value){
        //成功
        console.log(value)
    }, function(reason){
        //失败
        console.error(reason)
    })
    

# 13.3 Promise.prototype.then

then 方法的返回结果也是一个 Promise 对象(所以 then 方法可以链式调用),这个对象的状态由回调函数的执行结果来决定

  1. 回调函数中返回的结果是非 Promise 类型数据,那么状态为成功,返回值为对象的成功的值
  2. 回调函数中返回的结果是一个 Promise 类型数据,那么状态由结果中的 Promise 状态来决定
  3. 抛出错误,那么状态为失败,错误的值为抛出来的值
const p = new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('用户数据');
  },1000)
});

const result = p.then((value)=>{
    console.log(value);
    //1. 非 Promise 类型数据
    // return 123;
    //2. 返回一个 Promise 对象
    return new Promise((resolve, reject)=>{
        //返回 Promise 对象的状态就决定了 result 的状态
        resolve('ok');
    })
},(reason)=>{
  	console.warn(reason);
})

// then 方法的返回结果也是一个 Promise 对象
// 这个对象的状态由回调函数的执行结果来决定
// 1. 回调函数中返回的结果是非 Promise 类型数据,那么状态为成功,返回值为对象的成功的值
// 2. 回调函数中返回的结果是一个 Promise 类型数据,那么状态由结果中的 Promise 状态来决定
// 3. 抛出错误,那么状态为失败,错误的值为抛出来的值
console.log(result);

读取多个文件,然后一起输出:

const js = require("fs");

// 读取第一个,传到第二个
const p = new Promise((resolve, reject) => {
    js.readFile("let.html", (err, data) => {
        resolve(data);
    })
})

// 读取第二个,合并第一个,传到第三个
p.then((value)=>{
    return new Promise((resolve, reject) => {
        js.readFile("consts.html", (err, data) => {
            resolve([value, data]);
        })
    })
},(reason)=>{
}).then((value)=> {
  //读取第三个,合并第三个,传到输出
    return new Promise((resolve, reject) => {
        js.readFile("Promise.html", (err, data) => {
            value.push(data);
            resolve(value);
        })
    })
},(reason)=>{
}).then((value)=>{
  //输出
    console.log(value.join("\r\n"));
}, (reason)=>{
})

# 13.3 Promise.prototype.catch

  • 处理 Promise 中的异常状态。
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('出错啦!');
    }, 1000);
})

p.catch((reason) => {
  	console.error(reason);
})

# 14. Set

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用「扩展运算符」和「for...of...」进行遍历,集合的属性和方法:

  1. size:返回 Set 的元素个数
  2. add:增加一个元素,重复则不增加
  3. delete:删除一个元素
  4. has:检测是否包含某个元素
  5. clear:清空集合
//创建一个非空集合
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());

# 15. Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用「扩展运算符」和「for...of...」进行遍历。

Map 的属性和方法:

  1. size:返回 Map 的元素个数
  2. set: 增加一个新元素或修改原有元素的值
  3. get:返回当前 Map 返回键名对象的键值
  4. has:检测 Map 中是否包含某个元素,返回 boolean 值
  5. clear: 清空集合,返回 undefined

# 16. class 类

ES6 提供了更接近传统语言的写法,引入了 class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

语法:

  1. class 声明类
  2. constructor 定义构造函数初始化
  3. extends 继承父类
  4. super 调用父级构造方法
  5. static 定义静态方法和属性
  6. 父类方法可以重写

# 16.1 构造函数

  • ES5 写法

    //ES5 写法
    
    //构造函数,本身也是一个对象
    function Phone(brand, price) {
        this.brand = brand;
        this.price = price;
    }
    
    //添加方法
    Phone.prototype.call = function() {
      	console.log("我可以打电话!");
    }
    
    //实例化对象
    let iPhone = new Phone("Apple", 999);
    iPhone.call();
    console.log(iPhone);
    
  • ES6 写法

    // ES6 写法
    class Phone {
        //构造方法:名字固定,new 的时候自动执行
        constructor(brand, price) {
            this.brand = brand;
            this.price = price;
        }
        // 普通方法:方法必须使用该语法,不能使用 ES5 的对象完整形式
        call(){
          	console.log("这是 call");
        }
        method(){
          	console.log("这是 method...")
        }
        //call:function(){
        //     不支持
        //}
    }
    
    let iPhone = new Phone("Apple", 9999);
    iPhone.call();
    iPhone.method();
    console.log(iPhone);
    

# 16.2 静态成员

  • ES5

    // 下面属性和方法是加在 Phone 对象上的,实例化的时候是拿不到的 => 静态成员
    Phone.name = '手机';
    Phone.change = function(){
      	console.log("我可以改变世界");
    }
    
    // 下面属性和方法是加在 Phone 实例对象上的,实例化的时候是可以拿到的
    Phone.prototype.name = '手机';
    Phone.prototype.change = function(){
      	console.log("我可以改变世界");
    }
    
  • ES6

    class Phone {
      	//属于类,不属于实例对象
      	static name = 'Hedon';
    		static change() {
          	console.log('我可以改变世界');
        }
    }
    

# 16.3 继承

  • ES5 写法

    //手机
    function Phone(brand, price){
      	this.brand = brand;
      	this.price = price;
    }
    
    Phone.prototype.call = function(){
      	console.log('我可以打电话');
    }
    
    //智能手机
    function SmartPhone(brand, price, color, size) {
      	Phone.call(this,bran,price);
      	this.color = color;
      	this.size = size;
    }
    
    //设置子级构造函数的原型
    SmartPhone.prototype = new Phone;
    //校正(optional)
    SmartPhone.prototype.constructor = SmartPhone
    
    //声明子类方法
    SmartPhone.prototype.photo = function() {
      	console.log('我可以拍照');
    }
    
  • ES6 写法

    // ES6 写法
    class Phone {
        //构造方法:名字固定,new 的时候自动执行
        constructor(brand, price) {
            this.brand = brand;
            this.price = price;
        }
        // 普通方法:方法必须使用该语法,不能使用 ES5 的对象完整形式
        call(){
          	console.log("这是 call");
        }
    
        method(){
          	console.log("这是 method...")
        }
    }
    
    class SmartPhone extends Phone {
        constructor(brand, price, color, size) {
            super(brand, price);
            this.color = color;
            this.size = size;
        }
    
        //子类自己的方法
        child() {
          	console.log("我是子类");
        }
    
        //可以重写父类的方法
        call() {
          	super.call()
          	console.log("我是子类,我为自己打call")
        }
    }
    
    let sp = new SmartPhone("Apple", 100, "Yellow", "bing");
    sp.call();
    sp.method();
    sp.child();
    

# 16.4 get 和 set

  • ES6

    class Phone {
        get Price() {
            console.log("价格属性被读取了...")
            return "iloveu"
        }
    }
    
    let p = new Phone();
    console.log(p.Price)  // 价格属性被读取了...    iloveu
    

# 17. 数值扩展 Number

# 17.1 Number.EPSILON 是 JavaScript 表示的最小精度

console.log( 0.1 + 0.2 === 0.3);  //false
function equal(a, b) {
  	return Math.abs(a-b) < Number.EPSILON
}
console.log(equal(0.1+0.2, 0.3)) // true

# 17.2 二进制和八进制

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

# 17.3 Number.isFinite() 与 Number.isNaN()

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

Number.isNaN() 用来检查一个值是否为 NaN。

# 17.4 Number.parseInt() 与 Number.parseFloat()

ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。

即使是数字后面有字符串,它也会自动截取,比如:

console.log(Number.parseInt("1111哈哈哈哈")) // 1111

# 17.5 Number.isInteger

Number.isInteger() 用来判断一个数值是否为整数。

# 17.6 Number.sign

Number.sign() 用来判断一个数是正数(返回1)、负数(返回-1)还是零(返回0)。

# 18. 对象扩展 Object

ES6 新增了一些 Object 对象的方法

  1. Object.is 比较两个值是否严格相等,与 === 行为基本一致(+0 与 NaN)
  2. Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
  3. _proto_、setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型

# 19. 模块化

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

模块化的优势有以下几点:

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性

ES6 之前的模块化规范有:

  1. CommonJS => NodeJS、Browserify
  2. AMD => requireJS
  3. CMD => seaJS

ES6 模块化语法:

  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能

# 19.1 分别暴露

写一个 export.js:

export let name = 'hedon';
export function say(){
    console.log("hello world");
}

写一个 import.html 来引入 export.js:

<script type="module">
    //引入 export.js 模块,路径必须是双引号
    import * as m1 from "./export.js";
    console.log(m1.name); //hedon
    m1.say(); //hello world
</script>

# 19.2 统一暴露

let name = 'hedon'
function sayHello() {
    console.log("hello world");
}

export{ name, sayHellosay }

# 19.3 默认暴露

export default {
    name: 'Hedon',
    call: function(){
        console.log("哈哈哈")
    }
}

//使用
import * as m3 from "./exportDefault.js";
m3.default.call();

# 19.4 常用导入

import * as m3 from "./exportDefault.js";

# 19.5 解构赋值导入

import {name, say} from "./export.js";
import {name as name2 , say} from "./export.js";  //同名可以起别名
console.log(name);
say();

# 19.6 默认导入

import {default as m3} from "./exportDefault.js"; //必须起别名

# 19.7 简便导入

import m3 from "./exportDefault.js"; //只能针对默认暴露

# 19.8 浏览器方式

<!--
app.js 
//入口文件

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

<!--浏览器文件-->
<script src="./app.js" type="module">
  	//...
</script>

# 19.9 引入 NPM 包

先下载 npm 包:

npm i jquery

引入:

import $ from "jquery";
$('body').css('background', 'pink');

# ES7

# 1. Array.prototype.includes

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

const mingzhu = ['西游记', '红楼梦', '三国演义', '水浒传'];
console.log(mingzhu.includes('西游记')); //true
console.log(mingzhu .includes('金瓶梅')); //false

# 2. 幂运算

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

# ES8

# 1. async 和 await

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

async 函数的返回值为 Promise 对象,Promise 对象的结果由 async 函数执行的返回值决定:

  1. 返回非 Promise 类型的对象,结果都是成功的 Promise 对象
  2. 返回成功的 Promise 对象,结果就是这个成功的 Promise 对象
  3. 返回失败的 Promise 对象,结果就是这个失败的 Promise 对象
  4. 抛出异常,结果就是失败的 Promise 对象

await 表达式:

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

//1. await 要放在 async 函数中
async function main() {
    //2. await 右侧的表达式一般为一个 Promise对象
    try{
        //3. await 返回结果的 Promise 对象的值
        let result  = await p;
        console.log(result)
    }catch (e){
        //4. 如果 Promise 失败,需要用 try..catch.. 来捕获异常
        console.log(e);
    }
}

main();

# 1.1 async 与 await 结合读取文件

//1. 引入 fs 模块
const fs = require("fs");

//读取 file1.html 文件
function readFile1 (){
    return new Promise((resolve, reject) => {
        fs.readFile("es8/file1.txt", (err, data) => {
            if (err) {
                reject(err);
            }
            resolve(data);
        });
    })
}

//读取 file2.html 文件
function readFile2 (){
    return new Promise((resolve, reject) => {
        fs.readFile("es8/file2.txt", (err, data) => {
            if (err) {
                reject(err);
            }
            resolve(data);
        });
    })
}

//读取 file3.html 文件
function readFile3 (){
    return new Promise((resolve, reject) => {
        fs.readFile("es8/file3.txt", (err, data) => {
            if (err) {
                reject(err);
            }
            resolve(data);
        });
    })
}

//声明一个 async 函数
async function main(){
    let file1 = await readFile1();
    let file2 = await readFile2();
    let file3 = await readFile3();
    console.log(file1.toString() + file2.toString() + file3.toString())
}

main();

# 1.2 async 和 await 结合发送 Ajax 请求

// 发送 Ajax 请求,返回结果是 Promise 对象
function sendAjax(url) {
    return new Promise((resolve, reject) => {
        //1. 创建对象
        const x = new XMLHttpRequest();
        //2. 初始化
        x.open("GET", url);
        //3. 发送请求
        x.send();
        //4. 绑定事件
        x.onreadystatechange = function() {
            //到4表示所有响应已经回来了
            if(x.readyState === 4) {
                if(x.status >= 200 && x.status < 300) {
                  	resolve(x.response);
                }
                reject(x.status);
            }
        }
    })
}

let jokeUrl = "https://api.apiopen.top/getJoke";

async function main(){
    try {
        let result = await sendAjax(jokeUrl);
        console.log(result);
    }catch (e) {
      	console.error(e);
    }
}

main();

# 2. Object.values 和 Object.entries

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

# 3. Object.getOwnPropertyDescriptors

该方法返回指定对象所有自身属性的描述对象。

# ES9

# 1. Rest/Spread 属性

Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组, 在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符。

 
function connect({host, port, ...user}) { 
  console.log(host); //127.0.0.1
  console.log(port); //3306
  console.log(user); //{username: "root", password: "root", type: "master"}
}
connect({
    host: '127.0.0.1', 
    port: 3306, 
    username: 'root', 
    password: 'root', 
    type: 'master'
});

# 2. 正则捕获分组

ES9 允许命名捕获组使用符号 ?<name>,这样获取捕获结果可读性更强

let str = '<a href="https://hedon954.github.io/noteSite/">Hedon</a>';
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result);

//结果
0: "<a href=\"https://hedon954.github.io/noteSite/\">Hedon</a>"
1: "https://hedon954.github.io/noteSite/"
2: "Hedon"
groups:
  text: "Hedon"
  url: "https://hedon954.github.io/noteSite/"
index: 0
input: "<a href=\"https://hedon954.github.io/noteSite/\">Hedon</a>"

# 3. 正则反向断言

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

//声明字符串:它是变换的
let str = "JS222222你知道哈哈哈哈44444啦啦啦";

//需求:把后面数字提前出来

//正向断言(根据后边的内容进行断言)
const reg = /\d+(?=啦)/;
const result = reg.exec(str);
console.log(result);
/**
  *  0: "44444"
     groups: undefined
     index: 15
     input: "JS222222你知道哈哈哈哈44444啦啦啦"
  */

//反向断言(根据前面的内容进行断言)
const reg2 = /(?<=哈)\d+/;
const result2 = reg2.exec(str);
console.log(result2);
/**
  *  0: "44444"
     groups: undefined
     index: 15
     input: "JS222222你知道哈哈哈哈44444啦啦啦"
  */

# 3. 正则表达式 dotAll 模式

区别:

  • /./s 有了后面的 s 之后,. 将能够匹配任何字符,包括换行符。
  • /./gs 匹配全局
// 需求:提取出电影名称和上映时间,存储到对象当中
let str = ` <ul>
                <li> 
                	<a>肖生克的救赎</a>
                	<p>上映日期: 1994-09-10</p> 
                </li>
                <li> 
                	<a>阿甘正传</a>
                	<p>上映日期: 1994-07-06</p> 
                </li>
            </ul>
`;

// 传统写法
// 缺点,一当换行越来越多,将疯狂 \s
let reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/;
// 执行正则
let result = reg.exec(str);
console.log(result);
/**
  * 0: "<li> \n      <a>肖生克的救赎</a>\n    <p>上映日期: 1994-09-10</p>"
  * 1: "肖生克的救赎"
  * 2: "上映日期: 1994-09-10"
  */


// 有了 dotAll 之后
// 区别:  /./s   有了后面的 s 之后,. 将能够匹配任何字符,包括换行符
// 优点,不需要 \s 了
//只匹配第一个
reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/s;
//匹配全部
//reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
result = reg.exec(str);
console.log(result);

# ES10

# 1. Object.fromEntries

用来创建一个对象。但是参数比较特殊,需要传一个二维数组或者一个 Map。

// 二维数组
const result = Object.fromEntries([
  	['名称','hedon'],
  	['职业','程序员'],
]);
console.log(result);
/**
 * 名称: "hedon"
 * 职业: "程序员"
 * __proto__: Object
 */

// Map
const m = new Map();
m.set('name', 'hedon');
m.set('job', 'programmer');

const result2 = Object.fromEntries(m);
console.log(result2);
/**
 * name: "hedon"
 * job: "programmer"
 * __proto__: Object
 */

# 2. trimStart 和 trimEnd

用来截掉字符串左侧或右侧的空白字符。

let str = '    iloveu   ';

str = str.trimStart();
str = str.trimEnd();

# 3. Array.prototype.flat 与 flatMap

flag:将多维数组降为低维数组,比如三维降二维。

flagMap:f对原数组的每个成员执行一个函数,相当于执行 Array.prototype.map(),然后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。

// 降数组维度
let arr = [1,2,3,4,5,[6,7]];
console.log(arr.flat()); // [1, 2, 3, 4, 5, 6, 7]
console.log(arr.flat(1)); //1表示降1层(默认值),2表示降2层

// flatMap
const arr2 = [1,2,3,4,5];
const res = arr2.flatMap((item)=>{
  	return item * 10;
})
console.log(res); //[10, 20, 30, 40, 50]

# 4. Symbol.prototype.description

用来获取 Symbol 的字符串描述。

let s = Symbol('hedon');
console.log(s.description);

# ES11

# 1. 私有属性

class 中属性前面加上 # 就可以声明为私有属性。

        class Person {
            //公有属性
            name;

            //私有属性
            #age;
            #weight;

            //构造方法
            constructor(name, age, weight) {
                this.name = name;
                this.#age = age;
                this.#weight = weight;
            }
        }

        // 实例化
        const girl = new Person('小红', 18, '45kg');
        console.log(girl.name);
        console.log(girl.#age); //Uncaught SyntaxError: Private field '#age' must be declared in an enclosing class

# 2. Promise.allSettled

接收一个 Promise 数组,返回一个成功 Promise 对象,这个对象是对收到的这个 Promise 数组的结果的集合。

//创建两个 Promise 对象
const p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
      	resolve('第一个 Promise对象,成功');
    });
})

const p2 = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        reject('第二个 Promise 对象,失败');
    })
})

//合并
const result = Promise.allSettled([p1, p2]);
console.log(result);
  • 与 Promise.all([p1, p2]) 的区别:
    • all() 只要遇到 reject 就会停止,而 allSettled() 会执行全部的 Promise
    • all() 返回的是成功的 Promise 的结果的数组
    • allSettled() 返回的是每个 Promise 成功与否及其结果(将其组成对象)的一个数组

# 3. 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>
`;

// 必须要 gs
let reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg;

// 返回的是一个 iterator 对象
const result = str.matchAll(reg);

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

# 4. 可选链操作符

对象?:问号用来判断前面的对象是否存在。

// 获取 dbHost
function main(config) {
    //传统方式:判断 config 是否有传,其中一个断了就会报错。
    const result = config && config.db && config.db.host;
    console.log(result);

    //可选链操作符
    //? 表示判断前面的对象是否有,有才去读取下一个属性,这样没有的话会返回 undefined,不会报错。
    const result2 = config?.db?.host;
    console.log(result2);
}

main({
    db:{
        host: '192.168.0.1',
        port: '3306',
    },
    cache: {
        host: '192.168.0.2',
        username: 'admin',
    },
});

# 5. 动态 import 导入

实现模块的按需下载和按需加载。

//静态导入
import * as mi form './module1.js';

//动态导入
function hello(){
  	// import() 表示动态导入,返回的值是一个 Promise 对象
  	import('./module1.js').then(module => {
      	console.log(module);
    })
}

# 6. BigInt 类型

用来做更大数值的运算。

//在普通数字后面加一个 n
let num = 512;
let bigNum = 512n;

//将普通整数转为 BigInt
let n = 123;
let bigN = BigInt(n);

# 7. 全局对象 globalThis

在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 windowself 或者 frames 取到全局对象,但是在 Web Workers (opens new window) 中,只有 self 可以。在 Node.js 中,它们都无法获取,必须使用 global

在松散模式下,可以在函数中返回 this 来获取全局对象,但是在严格模式和模块环境下,this 会返回 undefined。 You can also use Function('return this')(), but environments that disable eval() (opens new window), like CSP (opens new window) in browsers, prevent use of Function (opens new window) in this way.

globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self 这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis

  • 没有 globalThis

    var getGlobal = function () {
      if (typeof self !== 'undefined') { return self; }
      if (typeof window !== 'undefined') { return window; }
      if (typeof global !== 'undefined') { return global; }
      throw new Error('unable to locate global object');
    };
    
    var globals = getGlobal();
    
    if (typeof globals.setTimeout !== 'function') {
      // 此环境中没有 setTimeout 方法!
    }
    
  • 与 globalThis

    if (typeof globalThis.setTimeout !== 'function') {
      //  此环境中没有 setTimeout 方法!
    }
    
上次更新: 8/1/2021, 11:46:37 AM