XM的小窝


 

JavaScript ES6-ES11 中的一些新语法

JavaScript ES6 中一些新增的语法

let

let申明的变量只在所处于的块级作用域中有效
在一个大括号中,使用let关键字申明的变量才具有块级作用域,var关键字事不具备这个特点的

if (true) {
    let ddd = 'ddd';
    var varvar = 'ddd1s';
    console.log(ddd); //ddd
}
console.log(varvar); //正常输出
console.log(ddd); //报错,未定义

let的作用

防止循环变量变成全局变量
for (var i1 = 0; i1 < 10; i1++);
console.log(i1); //可以输出
for (let i = 0; i < 10; i++);
console.log(i); //报错,未定义
使用var会有变量提升,而使用let就不存在变量提升
console.log(a);
let a = 10; //报错,变量没有提升
console.log(a1);
var a1 = 10; //有变量提升,可以使用
使用let关键字申明的变量具有暂时性死区的特性:
var num = 10;
if (true) {
    console.log(num); //这样会报错,以为let绑定了这个if作用域,会报未定义错误
     let num = 20;
 }

经典题型

观察下面的代码,判断输出是啥?

var arr = [];
 for (var i11 = 0; i11 < 10; i11++) {
     arr[i11] = function() {
         console.log(i11);
     }
 }
 arr[0](); 
 arr[1]();

答案是:10 10
两次输出的结果都是10,变量i是全局的循环完成之后i11==10,所以最终两个都是10

再观察下面的代码,判断输出是啥?

var arr = [];
for (let i = 0; i11 < 10; i++) {
    arr[i] = function() {
        console.log(i);
    }
}
arr[0]();
arr[1]();

这种情况,函数调用时候会分别向自己所属的块级作用域中查找i的值,所以输出 0和1
每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时候是输出自己上一级(循环产生的块级作用域)下的i值

const

const的作用

声明常量,常量就是值(内存地址)不能变化的量
和let一样,使用const关键字申明常量具有块级作用域

if (true) {
    const a = 10;
}
console.log(a); //报错,变量未定义

使用方法

使用const 关键字

使用const 关键字必须赋初值:const aaa = 111;

常量赋值后,不能修改:const aaa = 111;

对于引用类型来说,const绑定的是引用类型的内存地址,而不是里面的内容:

const arrayd = [1, 2, 3];
arrayd.push('ddd');
console.info(arrayd); //[1, 2, 3, 'ddd']
arrayd[1] = 11111;
console.info(arrayd); //[1, 11111, 3, 'ddd']
arrayd = [2, 1, 3, 1] //错误,这样改变了常量绑定的内存地址

也就是说 const 对于引用类型来说,绑定的就是引用类型的内存地址,但是里面的内容可以随意更改

使用const创建一个对象
const obj = {
    name: "kano",
    age: 10,
    Say: function() {
        console.log("我是" + this.name + "你好呀");
    }
}
obj = {
    name: "ddd"//错误
}

使用const创建对象可以保护对象指向的地址不被篡改

总结

let const var 的区别:

  1. 使用var声明的变量,其作用域所在的函数内,且存在变量提升的对象

  2. 使用let申明的变量,其作用域为该语句所在的代码块内,不存在变量提升

  3. 使用const 申明的是常量,在后面出现的代码中,不能再修改该常量的值

Number.EPSILON 与 新增属性

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

Number.EPSILON 属性表示 1 与Number可表示的大于 1 的最小的浮点数之间的差值。

EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16

先来看一段代码:

console.log(0.3 + 0.4 === 0.7)//由于精度问题,可能会为false

所以,就需要用EPSILON来帮忙了

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

其他的新增属性:

  • Number.isFinite 检测一个数是否为有限数

    console.log(Number.isFinite(100))//true
    console.log(Number.isFinite(100/0))//false
    console.log(Number.isFinite(Infinity))//false
  • 二进制和八进制

    let bin = 0b11010;
    let octal = 0b777;
  • Number.isNaN 检测一个数值是否为NaN

    console.log(Number.isNaN(111));//false
  • Number.parseInt Number.PaseFloat 字符串转为整数

    console.log(Number.parseFloat('2.222aaaa'))///2.222
  • Number.isInteger 判断一个数是否为整数

    console.log(Number.isInteger(1.1))//false
  • Math.trunc 将数字的小数部分抹掉

    console.log(Math.trunc(1.222))//1
  • Math.sign 判断一个数到底为正数,复数 还是0

    console.log(Math.sign(100)) //1
    console.log(Math.sign(0)) //0
    console.log(Math.sign(-100))//-1

对象方法扩展

Object.is

意思是判断两个值是否完全相等,和 === 运算符差不多,但有区别,例如

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

Object.assign

作用是合并对象(浅拷贝)

const kano1 {
    name : 'kanokano1';
}
const kano2 {
    name : 'kanokano2';
}
Object.assign(kano1,kano2)//后面覆盖掉前面的同名属性

object.setPrototypeOf

作用是设置原型对象(并不建议这么设置)

const kano{
    name: "kano"
}
const kano1{
    name:"kano1"
}
Object.setPrototypeOf(kano,kano1);//设置kano的原型对象为kano1
Object.getPrototypeOf(kano1);//获取kano1的原型对象

原始数据类型 Symbol

Symbol 的基本使用

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,是一种类似于字符串的数据类型。
简介: ES5的对象属性名都是字符串,这就很容易造成属性名的冲突,比如一个项目很庞大,又不是一个人开发的,就有可能会造成变量名冲突,如果有一个独一无二的名字就好了,这样就能从根本上防止属性名冲突。这就是ES6引入Symbol的原因。

Symbol 特点

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

//创建Symbol
let a = Symbol();
let aa = Symbol('kanokano.cn');
let aaa = Symbol('kanokano.cn');
console.log(aa === aaa); //false

//Symbol.for 创建
let s4 = Symbol.for('kanokano');
console.log(s4,typeof s4); //Symbol(kanokano) 'symbol'

给对象添加 Symbol 类型的属性

//向对象中添加方法
let game = {.....};
//声明一个对象
let methods = {
    up: Symbol(),
    down:Symbol()
}
game[methods.up] = function(){
    console.log('我可以快速上升');
}
game[methods.up] = function(){
    console.log('我可以快速下降');
}

let game1 = {
    name: "斗地主",
    [Symbol('say')] :function(){
        console.log(111)
    }
}

解构

解构赋值

ES6中允许从数组中提取值,按照对应的位置,对变量赋值,对象也可以实现解构

数组解构

let [a, b, c] = [1, 2, 3]; //分别给a b c 赋值为 1 2 3

也可以这样写

let ary = [1, 2, 3];
let [a, b, c] = ary;

如果变量没有对应的值,那就为undefined

let [a, b, c] = [1, 2]; //c = undefiend

对象解构

对象解构允许我们使用变量的名字匹配对象的属性 匹配成功后将对象属性的值赋值给变量

let kano = {
    name: "kano",
    age: 18
};
let {name,age} = kano;
console.log(name); //kano
console.log(age);  //18

对象解构的另一种写法:

let kano = {
    name: "kano",
    age: 18
};
let {name:myName,age:myAge} = kano
console.log(myName)//kano
console.log(myAge)//18

这里name指kano中的name属性,myName指的是替代这个属性的值的变量

注:修改myName不会影响原对象的name

对象的简化写法

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

let name = 'kano';
let Yeah = function(){
    console.log(' 好耶 !');
}

//直接写变量,函数名
const TheBestKano = {
    name,
    Yeah,
    //也可以不用写冒号,直接写函数体
    DD(){
        log('是小鹿包 不是DD!!!');
    }
}

迭代器

简介

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

  • ES6 创造了一种新的遍历命令for...of循环 这个循环主要是使用迭代器进行循环操作的
  • 原生具备迭代器接口的数据(可用for of 遍历)
    • array
    • Arguments
    • Set
    • Map
    • String
    • TypedArray
    • NodeList
  • 工作原理
    • 创建一个指针对象,指向当前数据结构的起始位置
    • 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
    • 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
    • 每调用next方法返回一个包含value对象和double属性的对象

迭代器使用

const kano = ['k','a','n','o'];
let iterrator = kano[Symbol.iterator]();//使用迭代器
//调用对象next方法
console.log(iterrator.next())//k
console.log(iterrator.next())//a
console.log(iterrator.next())
console.log(iterrator.next())
console.log(iterrator.next())

使用 for of 遍历

遍历数组

const kano = ['k','a','n','o'];
for(let val of kano){
    console.log(val); //k a n o
}

for(let val in kano){
    console.log(val);  // 0 1 2 3
}

迭代器的应用

//迭代器的一种实现
[Symbol.iterator](){
    let index = 0;
    return {
        next: () => { //使用箭头函数,取消作用域的this指向
            if(index < that.menber.length) {
                //下标自增
                index++;
                //返回结果
                return result;
            }else{
                return {value: undefined, done:true };
            }
        }
    }
}

生成器

生成器函数在可以在执行时能暂停,暂停后又能从暂停处继续执行。

function *

yield

yield 关键字用来暂停和恢复一个生成器函数function *

语法

[rv] = yield [expression]

expression

定义通过迭代器协议从生成器函数返回的值。如果省略,则返回undefined

rv

返回传递给生成器的next()方法的可选值,以恢复其执行。

function * 这种声明方式,会定义一个生成器函数,返回一个 Generator 对象

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');
//next方法可以传入实参,传入的实参就越是前一个yield语句的整体返回结果
console.log(iterator.next());//value: 111 done: false

console.log(iterator.next('BBB'));//value: 222 done: false
console.log(iterator.next('CCC'));//value: 333 done: false
console.log(iterator.next("DDD"));//value: undefined done: true

next方法可以传入实参,传入的实参就是前一个yield语句的整体返回结果

当我们调用第一个next方法后,输出的参数为AAA

第二次调用 输出的为BBB

第三次为 CCC

第四次为 DDD

生成器函数实例

当我们想使用异步函数的时候,我们按照以前已有的思想,就是设置一个定时器,setTimeout,通过定时器设置的时差来控制函数的执行顺序

但这样会导致定时器越嵌越多,最终造成一种现象,称之为 回调地狱 代码缩进越来越靠后,可读性越来越差

//回调地狱
setTimeout(() => {
    console.log(1111);
    setTimeout(() => {
        console.log(2222);
        setTimeout(() => {
            console.log(3333);
        }, 3000)
    }, 2000)
}, 1000)

生成器函数就解决了这样的问题:

function one() {
    setTimeout(() => {
        console.log(111);
        //自动向后遍历
        iterator.next();
    }, 1000);
}

function two() {
    setTimeout(() => {
        console.log(222);
        //自动向后遍历
        iterator.next();
    }, 1000);
}

function three() {
    setTimeout(() => {
        console.log(333);
        //自动向后遍历
        iterator.next();
    }, 1000);
}

//生成器函数
function* gen() {
    yield one();
    yield two();
    yield three();
}

//调用生成器函数
let iterator = gen();
//触发遍历
iterator.next();

解析:

首先创建三个单独的函数,分别存放各自的定时器函数,内部存放有触发遍历的next方法,再创建一个生成器函数gen
调用生成器函数
使用next方法手动触发第一次遍历,后面的遍历都会自动向后执行,因为我们在每个定时器中依次调用了next方法

生成器函数实例2

模拟获取用户数据,订单数据,商品数据

function getUsers(){
    setTimeout(()=>{
        let data = '用户数据';
        //使用next方法将data作为实参变成第1个yield的返回值
        iter.next(data);
    },1000);
}
function getOrder(){
   setTimeout(()=>{
        let data = '订单数据';
               //使用next方法将data作为实参变成第2个yield的返回值
        iter.next(data);
    },1000);
}
function getGoods(){
   setTimeout(()=>{
        let data = '商品数据';
               //使用next方法将data作为实参变成第3个yield的返回值
        iter.next(data);
    },1000);
}
function* gen(){
    let users = yield getUsers();//接住返回值
    console.log(users);
    let Order = yield getOrder();
    console.log(Order);
    let Goods = yield getGoods();
    console.log(Goods);
}
let iter = gen();
//触发异步调用
iter.next();

该实例充分利用了next方法可以传入实参作为yield的返回值的特性,实现了异步请求数据的传递,在实际使用中,如果我们需要等待上一个接口完成并返回结果的时候再执行下一步请求,就可以使用生成器函数进行配合操作,同时也避免了回调地狱问题

箭头函数

介绍

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

() => {} 这就是一个匿名箭头函数,只不过啥也没干

箭头函数的性质

箭头函数不绑定this关键字,箭头函数中的this指向的是函数声明时所在作用域下的this的值
箭头函数不能作为构造实例化对象
箭头函数不能使用 arguments 变量

举个栗子:

var obj = {
    name: "kano"
}
function fn() {
    console.log(this);
    return () => {
        console.log(this);
    }
}
const resultFn = fn.call(obj); //使用call方法让fn里面的this改变指向
resultFn();

let Kano = (name) => { //错误的,箭头函数不能作为构造器使用
    this.name = name;
}

let fff = ()=>{ // 错误的,箭头函数不能使用 arguments 变量
    arguments[0];
}

结果是两个this都指向obj

结论: 箭头函数不绑定this,箭头函数没有自己的关键字

经典例题

观察以下代码,看看输出的是啥?

var obj = {
    age: 22,
    say: () => {
        alert(this.age)
    }
}
obj.say();

结果是:输出undefined
因为箭头函数没有自己的this,而对象是不能产生作用域的。所以this指向的是window

ES6模块化

模块化的优点

  • 防止命名冲突
  • 代码复用
  • 高维护性

模块化规范产品

ES6 之前的模块化有:

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

ES6模块化语法

模块功能主要由两个命令构成 : exprot 和 import

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

注意:在浏览器中,import 语句只能在声明了 type="module"script 的标签中使用。

export模块化可分为 分别暴露 统一暴露 和 默认暴露

例子(分别暴露)

JS部分(kano.js):

export let name = 'kano';
export function sing(){
    console.log('I can sing')
}
例子(统一暴露)

JS部分(kano1.js):

let name = 'kano';
function sing(){
    console.log('I can sing')
}
//统一暴露
export {name,sing}
例子(默认暴露)

JS部分(kano2.js):

export default {
    name: 'kano',
    sing: function() {
        console.log('I can sing')
    }
}

JS入口文件引入

import * as kanoMOD from "./kano.js";
import * as kanoMOD1 from "./kano1.js";
import * as kanoMOD2 from "./kano2.js";

使用JS入口文件引入的模组,在html文档中使用方法如下:

<script src="./index.js" type="module"></script>
HTML引入
.....
<script type="module">
    /*引入 上面的js文件内模块的内容*/
    /*通用的导入模组的方式*/
    import * as kanoMOD from "./kano.js";
    import * as kanoMOD1 from "./kano1.js";
    import * as kanoMOD2 from "./kano2.js";
    console.log(kanoMOD);
    console.log(kanoMOD1);
    console.log(kanoMOD2);
    console.log("---------------");
    /*使用解构赋值的形式*/
    import {name ,sing} from "./kano.js";
    import {name as name1 ,sing as sing1} from "./kano1.js";
    import {default as kano} from "./kano2.js";
    console.log(name);
    console.log(name1);
    console.log(kano);
    console.log('---------------');
    /*简便形式,针对于默认暴露*/
    import kanoMOD3 from "./kano2.js";
    console.log(kanoMOD3);
</script>
.....
  • kanoMOD是一个module类型,包含了指定文件内标记为暴露(export)的所有实体

以上方法看似很好用,但有些浏览器并不能兼容这种模组引入方式,为了解决兼容性问题,可以使用一个JavaScript编译器---Babel ,,不过我还没系统学习完NodeJS,学完了再回来填坑把

Babel(咕咕咕)

剩余(rest)参数

介绍

剩余参数允许我们将一个不定数量的参数表示为一个数组,和arguments差不多

语法:...args args为参数

使用方法

例子: 使用...args作为剩余参数的标识

const sum1 = (...args) => {
    let total = 0;
    args.forEach(val => total += val)
    return total;
};
console.log(sum1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
console.log(sum1(1, 2));

剩余参数和解构的配合使用:

let aa1 = ["张三", "李四", "王五"];
let [s1, ...s2] = aa1; //s2为数组形式,接受aa1剩余的变量

Array扩展方法

扩展运算符

扩展运算符( spread )是三个点(...),可以将一个数组转为用逗号分隔的参数序列。
扩展运算符可以将数组或者对象转为用逗号分割的参数序列,和剩余参数是反着来的

栗子
let aa = ['a', 'b', 'c'];
console.log(...aa);
console.log('a', 'b', 'c');

...aa 其实就是 'a', 'b', 'c' 但因为逗号会 被log函数解析为参数列表的分隔符,所以最终输出 a b c

扩展运算符拆解字符串
var str = "kanokano";
console.log(...str); //k a n o k a n o
扩展运算符运用在合并数组操作上
let arr1 = [1, 2, 3, 4, 5];
let arr2 = [6, 7, 8, 9];
arr1 = [...arr1, ...arr2]; //合并
//或者 arr1.push(...arr2);
扩展运算符+max方法求最大值
var max2 = Math.max(...arr1);
console.log(max2);
console.info(arr1); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
利用扩展运算符将伪数组转换为真正的数组
var divs = document.getElementsByTagName('div'); //返回的是一个伪数组
console.log(divs);
var divary = [...divs]; //转换为真正的数组
divary.push("kanokanokanokano"); //可以使用数组中的push方法了
console.log(divary); //数组

特别注意:如果扩展运算符内处理的是类型数据的话,转换的时候使用的是浅拷贝

构造函数方法 :Array.from()

Array.from()方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

语法: Array.from(arrayLike[, mapFn[, thisArg]])

  • arrayLike:想要转换成数组的伪数组对象或可迭代对象
  • mapFn :如果指定了该参数,新数组中的每个元素会执行该回调函数。
  • thisArg :可选参数,执行回调函数 mapFn 时 this 对象

栗子:

//thisArg :可选参数,执行回调函数 mapFn 时 this 对象。
let fakearray = { //伪数组
    '0': "kano",
    '1': "kano1",
    '2': "kano2",
    '3': "kano3",
    'length': 4
};
console.log(fakearray);
//转换为真数组
fakearray = Array.from(fakearray);
console.log(fakearray);

Array 之 find()方法扩展

var facebook = [{ //数组里面装了三个对象
    id: 1,
    name: "张三"
}, {
    id: 2,
    name: "kano"
}, {
    id: 3,
    name: "王五"
}];
console.log(facebook.find(val => val.id == 2)); //如果找不到就返回undefined

Array 之 findIndex 方法

这个方法只会返回第一次遇到的符合条件的元素的下标

let ary = [1, 2, 143, 123];
console.log(ary.findIndex(item => item > 100)); //返回2

Array 之 includes 方法

表示某个数组是否包含指定的值,返回布尔值

[1, 2, 3].includes(2); //true

String 的扩展方法

模板字符串

模板字符串可以解析变量,函数
模板字符串需要用 `` 而不是引号
模板字符串可以换行

let name = 'kano';
function dd() {
    return "我是函数";
}
let sayHello = `hello,我是${name}
        ${dd()}dddd`;

startWith() 和 endsWith()

startWith():表示参数字符串是否在源字符串的头部,返回一个布尔值
endsWith():表示参数字符串是否在源字符串的尾部,返回一个布尔值

"hello woeld!".startsWith('hello') //true
"hello woeld!".startsWith('!') //true

repeat()

repeat方法表示将源字符串重复n次,返回一个新字符串

console.log('kano'.repeat(10)); //kanokanokanokanokanokanokanokanokanokano
console.log('kano'.repeat(0)); //输出空
console.log('kano'.repeat()); //输出空

Set数据结构

ES6提供了新的数据结构Set,它类似于数组,但是成员的值都是唯一的, 没有重复的值。

const s = new Set();
console.log(s.size); //查看数据结构的长度 0

set数据结构自动去重

const s11 = new Set([1, 1, 2, 2, 3, 3, 4, 4]);
console.log(s11.size); //长度为4 说明自动去重了
利用set数据结构做数组去重

思路就是,使用set数据结构返回一个不重复的伪数组,再使用扩展运算符打散为逗号分隔的元素,再转换为数组

const badarr = [1, 2, 1, 2, 1, 31, 4, 1, 5, 15, 1, 6, 54, 2, 4, 5, 3, 43, 5];
const goodarr = [...(new Set(badarr))];
console.log(goodarr);

Set数据结构中的实例方法

add(value) :添加某个值,返回Set结构本身

delete(value) :删除某个值,返回一个布尔值,表示删除是否成功

has(value) :返回一个布尔值,表示该值是否为Set的成员

clear() :清除所有成员,没有返回值

使用例:
const sss = new Set();
sss.add(1).add(2).add(3) //添加数据,因为返回的是set本身,所以可以链式调用
sss.delete(3) //删除set结构中为3的值,如果没有的话,返回false,成功返回true
sss.has(3) //返回 false
sss.clear(); //清除set结构中所有的值

set数据结构中的遍历

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

const sss = new Set();
sss.add(1).add(2).add(3)
sss.forEach(val => console.log(val))

Map数据结构

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

Map的属性和方法:

  • size 返回Map的元素个数
  • set 增加一个元素,返回当前map
  • get 返回键名对象的键值
  • has 检测Map中是否包含某个元素,返回boolean值
  • clear 清空集合, 返回undefined

创建一个空Map

//创建一个空map
let mp = new Map();

map属性和方法的使用

//声明map
let mp = new Map();
//添加元素
mp.set('name', 'kano');
mp.set('change', function() {
    console.log('我是函数');
})
let key = {
    age: 18
}
//size
console.log(mp.size);

//删除
mp.delete('age');
//获取
console.log(mp.get('name'));
console.log(mp);

//清空
mp.clear();

//遍历
for (let item of mp) {
    console.log(mp);
}

Getter 与 Setter

get语法将对象属性绑定到查询该属性时将被调用的函数。

当尝试设置属性时,set语法将对象属性绑定到要调用的函数。

class Person {
    constructor(name) {
        this.name = name;
    }
    get Name() {
        if (this.name == 'kano') {
            return this.name + ' yyds';
        } else {
            return this.name;
        }
    }
    set Name(val) {
        this.name = val;
    }
}
let kano = new Person('kano');
console.log(kano.Name);
kano.Name = 'dd';
console.log(kano.Name);

ES6之Promise(不完整,到时候再补充)

Promise介绍

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

一个promise请求会有以下几种状态:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。

Promise 还有一个好处就是 支持链式调用,传统回调函数会嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行条件,造成回调地狱,这样的代码既不方便阅读,也不便与异常处理,这就催生了promise的出现

Promise 的使用

先简单的看一个Promise的实例方法:

//实例化 Promise 对象
const pm = new Promise(function(resolve,reject){
    setTimeout(function(){
        //
        let data = '数据库中的用户数据';
        //
    },1000);
});
//调用 pm 对象的 then 方法
pm.then(function(value){
    ... ///成功的结果
}, function(reason){
    ... ///失败的原因
})

ES7中新增的语法

includes

**includes()** 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

const arr = ['kano',2,'dd'];
console.log(arr.includes('dd'));//true
console.log(arr.includes('kanokano'));//false

ES8中新增的语法

async 和 await

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

async 函数

  1. async h函数的返回值为promise对象
  2. promise 对象的结果由async函数执行的返回值决定

例子

//async 函数
async function fn(){
    //第一种情况,返回的结果不是promise类型的对象,函数执行后返回的结果就是成功的promise对象
    //返回一个字符串
    return 'hello~';
    //第二种情况,返回一个异常
    //函数返回的结果就是一个失败的Promise
    throw new Error('好像出了点问题呢~');
    //第三种情况,返回的结果是一个promise对象
    return new Promise((resolve,reject)=>{
        resolve('我是成功的数据');
        reject('我是失败的数据');
    })
}
const result = fn();
console.log(result);
//调用 then 方法
result.then(val=>{
    console.log(val);
},reason=>{
    console.warn(reason);
})

await 表达式 - 异步编程的完美解决方案

规则

  1. await 必须写在 async 函数中
  2. await 右侧的表达式一般为promise对象
  3. await返回的是primise成功的值
  4. 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();

async 和 await 组合实现 Ajax 请求

// 发送AJAX请求,返回的结果是Promise对象
function sendAJAX(url) {
    return new Promise((resolve,reject) => {
        //1。创建对象
        const xhr = new XMLHttpRequest();
        //2.初始化
        xhr.open('GET',url);
        //3。发送
        xhr.send();
        //4.事件绑定
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >=200 && xhr.status < 300){
                    //请求成功
                    resolve(xhr.response);
                }else {
                    //请求失败
                    reject(xhr.status);
                }
            }
        }
    })
}
//then测试
let result = sendAJAX('https://kanokano.cn').then(value => {
    console.log(value);
});
//async 与 await 测试
async function main(){
    //发送ajax请求
    let result await sendAJAX('https://kanokano.cn');
    //再来一个
    let result1 await sendAJAX('https://kano.run');
    console.log(result1)
}
main();

对象方法扩展(ES8)

Object.values 和 Object.entries
  • Object.values()方法返回一个给定对象的所有可枚举属性值的数组
  • Object.entries() 方法返回一个给定对象自身可遍历属性[key,value] 的数组
//声明对象
const singer = {
    name: "kano",
    age: 18,
    cities: ['上海','东京','广州']
}
//获取对象所有的键
console.log(Object.keys(singer));
//获取对象所有的值
console.log(Object.values(singer));
//entries
console.log(Object.entries(singer));

作用

可以用来创建一个Map:

const map = new Map(Object.entries(singer))
console.log(map.get('cities'))

Object.getOwnPropertyDescriptors

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

console.log(Object.getOwnPropertyDescriptors(singer))
//输出 对象属性的描述对象
/*
{
    "name": {
        "value": "kano",
        "writable": true,
        "enumerable": true,
        "configurable": true
    },
    "age": {
        "value": 18,
        "writable": true,
        "enumerable": true,
        "configurable": true
    },
    "cities": {
        "value": [
            "上海",
            "东京",
            "广州"
        ],
        "writable": true,
        "enumerable": true,
        "configurable": true
    }
}
*/

上面的输出和我们使用Object.create()方法中操作的属性是一样的:

const obj = Object.create(null, {
    name: {
        //设置值
        value : ’kano‘,
        //属性特性
        writeable: true,
        configurable:true,
        enumerable: true
    }
})

ES9中新增的语法

扩展运算符和rest参数

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',
    pwd: 'admin'
});

const s1 ={
    a: '普通攻击'
}
const s2 ={
    e: '元素战技'
}
const s3 ={
    q: '元素爆发'
}
const kokomi ={...s1, ...s2, ...s3};
console.log(kokomi);

正则扩展

命名捕获分组
//声明一个字符串
let str = '<a href="http://baidu.com">百度</a>';
// 提取 url 与 【标签文本】
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
//执行
const result = reg.exec(str);
console.log(result);
//输出比普通正则返回的对象中多了一个groups属性
/*
groups:
text: "百度"
url: "http://baidu.com"
*/
console.log(result.groups.text);
console.log(result.groups.url);

像上面一样,在正则表达式内加入命名捕获分组 ?<url> ?<text>

这样就可以在exec后返回的对象中产生一个groups属性,跟方便我们提取有关的字符串

正向断言(先行断言)
//声明字符串
let str = 'kanokano1224dddd';
const reg = /\d+(?=d)/;
const res = reg.exec(str)
console.log(res);

这里的?=就是正向断言,意思是,要满足等号后面的条件,前件才为真

翻译一下就是:仅当这串数字后面跟着d才匹配这串数字

反向断言(后行断言)
let str = 'kanokano1224dddd';
const reg = /(?<=kano)\d+/;
const res = reg.exec(str)
console.log(res);

这里的?<=kano就是反向断言,意思是,要满足等号前面的条件,后件才为真

dotAll 模式

dot 也就是 " . " 元字符 可以匹配除换行符以外的任意单个字符

const str = `
<ul>
    <li>
        <a>标题1</a>
        <p>kano1</p>
    </li>
    <li>
        <a>标题2</a>
        <p>kano2</p>
    </li>
    <li>
        <a>标题3</a>
        <p>kano3</p>
    </li>
    <li>
        <a>标题4</a>
        <p>kano4</p>
    </li>
</ul>`
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
let res;
while(res = reg.exec(str)){
    console.log(res);
}

注意,这里的 ?的意义是:

如果 ? 紧跟在任何量词 *、 +、? 或 {} 的后面,将会使量词变为非贪婪(匹配尽量少的字符),和缺省使用的贪婪模式(匹配尽可能多的字符)正好相反。例如,对 "123abc" 使用 /\d+/ 将会匹配 "123",而使用 /\d+?/ 则只会匹配到 "1"。

ES10中新增的语法

Object.fromEntries

这个对应es8里面的Object.entries()

和Object.entries()相反,Object.fromEntries是将二维数组转换为对象

//二维数组
const result = object.fromEntries([
    ['name','kano'],
    ['age',18]
]);

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

trimStart与trimEnd

和语义一样,是trim的扩展细分

  • trimStart 去掉字符串前端的空格

  • trimEnd 去掉字符串后端的空格

//trim 
let str = '   kano    ';
console.log(str.trim())//’kano‘
console.log(str.trimStart())//’kano     ’
console.log(str.trimEnd())//‘    kano’

flat 与flatMap

Flat是平的意思

flat是将多维数组转换成一维数组

//flat
const arr = [1,2,4,[4,[5,6]]];
//参数为深度 是一个数字
console.log(arr.flat(2));

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。

//flatMap
var arr1 = [1, 2, 3, 4];

arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

Symbol.prototype.description

//创建Symbol
let s = Symbol('kano');
console.log(s.description);//kano

ES11中新增的语法

私有属性

class Person {
    //共有属性
    name;
    //私有属性
    #age
    //构造方法
    constructor(name,age){
        this.name = name;
        this.#age = age;
    }
    intro(){
        console.log(this.name,this.#age);
    }
}
//实例化
const kano = new Person('kano',18);
kano.intro();

注意,私有属性不能定义在类的外面

Promise.allSettled

Promise.allSettled()方法返回一个在所有给定的 promise 都已经fulfilledrejected后的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果。

也就是,返回一个成功的promise ,里面装着给定的promise的执行结果

当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。

于此类似的Primose.all方法,则是在p1,p2都成功的时候返回的Promise才是成功的,否则为失败的Promise

//申明两个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 result1 = Promise.all([p1,p2]);
console.log(result)//fulfilled
console.log(result1)//rejected

String.prototype.matchAll

这个方法挺实用的,用于数据的批量提取

方法返回的是一个可迭代对象

const str = `
<ul>
    <li>
        <a>标题1</a>
        <p>kano1</p>
    </li>
    <li>
        <a>标题2</a>
        <p>kano2</p>
    </li>
    <li>
        <a>标题3</a>
        <p>kano3</p>
    </li>
    <li>
        <a>标题4</a>
        <p>kano4</p>
    </li>
</ul>`;
//声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
//调用方法
const res = str.matchAll(reg);
console.log(res)//返回的是一个可迭代对象
/*
for(let item of res){
    console.log(item);
}
*/
//使用扩展运算符展开
const arr = [...res];
console.log(arr);

可选链操作符

可选链操作符,即 ?.

作用是判断前面的对象是否存在有效,有效则执行里面的属性操作

function main(config){
    const dbHost = config?.db?.host;
    console.log(dbHost);
}
main({
    db:{
        host:'192.168.1.1',
        username:'root'
    },
    cache:{
        host:'127.0.0.1',
        username:'admin'
    }
})

bigint类型

也叫大整形,在普通整形基础后加一个n

用于大数值运算

//大整形
let n = 123n;
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+11);
console.log(BigInt(max)+BigInt(11));

绝对全局对象globalThis

console.log(globalThis)
/*
[object Window]
*/
点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注