参考书在线阅读:《ES6标准入门(第三版)》

let

1
2
3
4
let a;
let b,c,d;
let e=100;
let f=521, g='iloveyou', h=[];
  1. 变量不能重复声明。(var可以重复声明)
    1
    2
    let a=1;
    //let a=2;//×
  2. 块级作用域
    ES5中的三种作用域:全局,函数,eval(严格模式)
    1
    2
    3
    4
    {
    let doge='wangwang';
    }
    console.log(doge);//取不到,但是var取得到
  3. 不存在变量提升
    1
    2
    3
    4
    5
    6
    7
    8
    9
    console.log(song);
    var song='海上日记';
    //相当于:
    var song;
    console.log(song);//undefined
    var song='海上日记';

    console.log(song);//使用let则会报错
    let song='一程山路';
  4. 不影响作用域链
    1
    2
    3
    4
    5
    6
    7
    {
    let master='miaomiao';
    function playWithCat(){
    console.log(master);
    }
    playWithCat();//可以读取
    }
    以for循环为例:
    1
    2
    3
    4
    5
    for(let i=0;i<items.length;i++){
    items[i].onclick=function(){
    items[i].style.background='pink';
    }
    }
    结构为: for(单次表达式1;条件表达式2;末尾循环体3){中间循环体;}

    相当于:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    var i=0;
    }
    {
    var i=1;
    }
    {
    var i=2;
    }
    i为全局变量,所以循环结束时,i=item.length,但此时items[i]取不到值。
    将var改为let可解决问题:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    let i=0;
    }
    {
    let i=1;
    }
    {
    let i=2;
    }
    这样数组的每一项都能得到修改。
    所以以后for循环尽量用let声明初始值。

const

  1. 一定要赋初始值
  2. 一般常量使用大写(不强制)
  3. 常量值不能修改
  4. 块级作用域
  5. 对于数组和对象的元素修改,不算作对常量的修改,不会报错
    1
    2
    const ANI=['Aria','Digimon','OnePiece'];
    ANI.push('IScream');//可以修改

变量的解构赋值

ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值。

  1. 数组的解构
    1
    2
    3
    4
    5
    6
    const F4=['佐助','罗夏','路飞','白龙'];
    let [sasuke,rorschach,luffy,haku]=F4;
    console.log(sasuke);//佐助
    console.log(rorschach);//罗夏
    console.log(luffy);//路飞
    console.log(haku);//白龙
  2. 对象的解构
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const rorschach={
    name:'罗夏',
    age:28,
    money:function(){
    console.log('我有很多钱');
    }
    };
    let {name, age, money}=rorschach;
    console.log(name);//罗夏
    console.log(age);//28
    console.log(money);//function...
    money();//我有很多钱

模板字符串

ES6引入了新的声明字符串的方式[``]反引号。

  1. 声明
    1
    2
    let str=`我也是一个字符串哦`;
    console.log(str, typeof str);//我也是一个字符串哦 string
  2. 内容中可以直接出现换行符
    普通的引号无法换行,只能用加号+拼接。
    1
    2
    3
    4
    5
    6
    let str=`
    <ul>
    <li>a</li>
    <li>b</li>
    <li>c</li>
    </ul>`;
  3. 变量拼接
    1
    2
    3
    let lovest='水星领航员';
    let out=`我最喜欢的番剧是${lovest}`;
    console.log(out);//我最喜欢的番剧是水星领航员

对象的简化写法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let name='长门';
let change=function(){
console.log('一袋米哦扛几楼');
}
const juezhao={
name,
change,
//improve:function{
// console.log('神罗天征');
//}
//简写为:
improve(){
console.log('神罗天征');
}
}

箭头函数

let fn=(a,b)=>{...};

  1. this是静态的,this始终指向函数声明时所在作用域下的this的值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function a(){
    console.log(this.hh);
    }
    let b=()=>{
    console.log(this.hh);
    }
    window.hh='window的属性';
    const bianliang={
    hh:'bianliang的属性'
    }

    //直接调用:this都是指向window
    a();//window的属性
    b();//window的属性

    //call方法调用:可以改变函数内部this的值
    a.call(bianliang);//bianliang的属性
    b.call(bianliang);//window的属性 (箭头函数的this不被改变)
  2. 不能作为构造函数实例化对象
    1
    2
    3
    4
    5
    6
    let Artist=(name,album)=>{
    this.name=name;
    this.album=album;
    }
    let hikaru=new Artist('宇多田光','First Love');
    console.log(hikaru);//报错
  3. 不能使用arguments(用于保存实参)变量
    1
    2
    3
    4
    let fn=()=>{
    console.log(arguments);
    }
    fn(1,2,3);//报错
  4. 箭头函数的简写
    (1)省略小括号,当形参有且只有一个的时候
    1
    2
    3
    4
    let add=n=>{
    return n+n;
    }
    console.log(add(9));
    (2)省略花括号,当代码体只有一条语句的时候,此时return必须省略,且语句的执行结果就是函数的返回值
    1
    2
    let add=n=>n*n;
    console.log(add(9));
  5. 应用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //需求:点击div 2s后变成粉色
    let ad=document.getElementById('ad');
    //普通函数的写法:
    ad.addEventListener('click',function(){
    let _this=this;//this的常见命名:_this, that, self
    setTimeOut(function(){
    _this.style.background='pink';
    },2000);
    });
    //箭头函数的写法:
    ad.addEventListener('click',function(){
    setTimeOut(()=>{
    this.style.background='pink';//这里的this指代的就是该函数外层作用域的事件源ad
    },2000);
    });
    箭头函数适合与this无关的回调:定时器,数组的方法回调。
    箭头函数不适合与this有关的回调:事件回调,对象的方法。

函数参数的默认值设置

ES6允许给函数参数赋初始值。

  1. 形参初始值:具有默认值的参数,一般位置靠后(潜规则)。
    1
    2
    3
    4
    5
    function add(a,b,c=10){
    return a+b+c;
    }
    let result=add(1,2);
    console.log(result);//13
  2. 与解构赋值结合使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function fn({a,b,c='hhh'}){
    console.log(a);
    console.log(b);
    console.log(c);
    }
    fn({
    a:'111',
    b:'222'
    });

    rest

    ES6引入rest参数,用于获取函数的实参,用来代替arguments。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //ES5获取实参的方式:
    function fn(){
    console.log(arguments);
    }
    fn('哈哈','呵呵','嘿嘿');//打印为对象

    //ES6的rest参数:
    function fn2(...args){
    console.log(args)
    }
    fn2('哈哈','呵呵','嘿嘿');//打印为数组,可以用数组的方法:filter,some,every,map 提高了对参数处理的灵活程度
    如有多个参数,rest参数必须要放到最后。

扩展运算符[…]

扩展运算符能将数组转换为逗号分隔的参数序列。

1
2
3
4
5
6
const girls=['花花','泡泡','毛毛'];
function monster(){
console.log(arguments);
}
monster(...girls);//ES5: monster('花花','泡泡','毛毛');
//数组解构

  1. 数组的合并
    1
    2
    3
    4
    5
    6
    7
    8
    const girls=['Alicia','Akira','Athena'];
    const girls2=['Akari','Aika','Alice'];
    //ES5:
    const ariaSpirit1=girls.concat(girls2);
    console.log(ariaSpirit1);//['Alicia','Akira','Athena','Akari','Aika','Alice']
    //ES6:
    const ariaSpirit2=[...girls,...girls2];
    console.log(ariaSpirit2);//['Alicia','Akira','Athena','Akari','Aika','Alice']
  2. 数组的克隆
    1
    2
    3
    const zm=['A','B','C'];
    const zm2=[...zm];
    console.log(zm2);//['A','B','C'] 浅拷贝
  3. 将伪数组转为真正的数组
    1
    2
    3
    const div=document.querrySelectorAll('div');
    const divArr=[...div];
    console.log(divArr);

Symbol

ES6引进的新的原始数据类型,表示独一无二的值,是一种类似字符串的数据类型。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let s =Symbol();
    console.log(s,typeof s);//Symbol() "symbol"

    let s2=Symbol();
    let s3=Symbol();
    console.log(s2===s3);//false
    //symbol好比身份证,参数是姓名

    let s4=Symbol.for('One Piece');
    console.log(s4, typeof s4);//Symbol(One Piece) "symbol"

    let s5=Symbol.for('One Piece');
    console.log(s4===s5);//true

    目前所学的前端数据类型:
    USONB you are so niubility
    u undefined
    s string symbol
    o object
    n null number
    b boolean

  2. 使用:给对象添加属性和方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //写法一:
    let game={
    name:'扫雷'
    };
    let methods={
    up:Symbol(),
    down:Symbol()
    };
    game[methods.up]=function(){
    console.log(1);
    };
    game[methods.down]=function(){
    console.log(2);
    }

    //写法二:
    let youxi={
    name:'连连看',
    [Symbol('say')]:function(){
    console.log(3);
    },
    [Symbol('run')]:function(){
    console.log(4);
    }
    }

    (不太清楚该功能的具体使用场景,和ES5相比有什么优势…)

  3. 11个Symbol内置值(属性)
    |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接口,就可以完成遍历操作。

  1. ES6创造了一种新的遍历命令for…of循环,iterator接口主要供for…of消费。
  2. 原生具备iterator接口的数据(可用for…of遍历):
    (1)Array
    (2)Arguments
    (3)Set
    (4)Map
    (5)String
    (6)TypedArray
    (7)NodeList
  3. 工作原理
    (1)创建一个指针对象,指向当前数据结构的起始位置;
    (2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员;
    (3)接下来不断调用next方法,指针一致往后移动,直到指向最后一个成员。
  4. 应用:自定义遍历数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    const std={
    name:'Sam',
    score:[
    60,
    80,
    90
    ],
    [Symbol.iterator](){
    let index=0;
    let _this=this;
    return{
    next:function(){
    if(index<_this.score.length){
    const result= {value:_this.score[index],done:false};
    index++;
    return result;
    }else{
    return {value:undefined,done:true}
    }
    }
    };
    }
    }
    //对对象的遍历
    for(let v of std){
    console.log(v);
    }

生成器

生成器其实就是一个特殊的函数。
用来进行异步编程。
ES5使用纯回调函数、node、fs、ajax、mongodb进行异步编程。

  1. 使用生成器函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function * gen(){
    console.log('hello');
    }
    let iterator=gen();
    console.log(iterator);//直接调用显示函数,函数本身不执行

    //在生成器函数可以出现yield语句:yield相当于函数代码的分隔符
    function * gen2(){
    //三个分隔符产生四块代码,由next方法执行
    console.log(111);
    yield '哈哈';
    console.log(222);
    yield '呵呵';
    console.log(333);
    yield '嘿嘿';
    console.log(444);
    }
    let iterator=gen2();
    iterator.next();//111 {value: '哈哈', done: false}

    //在上面的代码执行完后再执行这个:
    iterator.next();
    iterator.next();//222 333 {value: '嘿嘿', done: false}

    //在上面的代码执行完后再执行这个:
    iterator.next();//444 {value: undefined, done: true}
    //遇到yield就暂停,调用next就继续

    //遍历的调用方法:
    for(let v of gen2()){
    console.log(v);
    }//111 哈哈 222 呵呵 333 嘿嘿 444
  2. 生成器函数参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function * gen(arg){
    //三个分隔符产生四块代码,由next方法执行
    console.log(arg);
    let one= yield '哈哈';
    console.log(one);
    let two= yield '呵呵';
    console.log(two);
    let three= yield '嘿嘿';
    console.log(three);
    }
    let iterator=gen('haha');
    iterator.next();//haha {value: '哈哈', done: false}
    //执行完上面再执行下面:
    iterator.next('hehe');//hehe {value: '呵呵', done: false}
  3. 实例一
    1秒后输出111,2秒后输出222,三秒后输出333。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    //ES5的写法:回调地狱
    setTimeout(()=>{
    consol.log(111);
    setTimeout(()=>{
    consol.log(222);
    setTimeout(()=>{
    console.log(333);
    },3000);
    },2000);
    },1000);

    //使用生成器函数:
    function one(){
    setTimeout(()=>{
    console.log(111);
    iterator.next();
    },1000);
    }
    function two(){
    setTimeout(()=>{
    console.log(222);
    iterator.next();
    },2000);
    }
    function three(){
    setTimeout(()=>{
    console.log(333);
    iterator.next();
    },3000);
    }
    function * gen(){
    yield one();
    yield two();
    yield three();
    }
    let iterator=gen();
    iterator.next();

Set

1
2
3
4
5
6
7
8
let s= new Set();//集合
console.log(s,typeof s);//Set(0){} "Object"

let s2=new Set(['a','b','c']);
console.log(s2);//{"a","b","c"}
s2.add('d');
s2.delete('b');
console.log(s2.has('a'));//true
  1. 数组去重:let result=[...new Set(arr)];
  2. 交集:let result=[...new Set(arr)].filter(item=>{new Set(arr2).has(item)});(filter函数体内如果是true就留下重复的,false则去掉)
  3. 并集:let result=[...new Set([...arr,...arr2])];
  4. 差集:let result=[...new Set(arr)].filter(item=>!(new Set(arr2).has(item)))

Map

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

  1. 属性和方法
    (1)size:返回Map的元素个数
    (2)set:增加一个新元素,返回当前Map
    (3)get:返回键名对象的键值
    (4)has:检测Map中是否包含某元素,返回boolean
    (5)clear:清空集合,返回undefined
    (6)delete:删除某个元素

Proxy

Proxy字面翻译为“代理”,从书面理论上来讲,是ES6中全新创建的一种构造函数,用于拦截对象属性的访问、赋值、函数调用的默认事件,从而进行更改。和后端语言的get/set写法类似,更改原来的取值赋值操作,通过一个自定义的过程得到新的结果,并返回出去。
那么这种技术是如何在软件中应用的呢?

比如变量赋值前的校验、记录对象取值赋值的操作日志、监控对象的性能等等,可以增强代码的安全性。
var proxy = new Proxy(target, handler);
先创建一个Proxy对象,该对象为ES6原生提供,Proxy对象有两个参数:第一个是目标对象,第二个对象用来放置拦截方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let my_obj={};
var proxy = new Proxy(my_obj, {
//Proxy对象的第一个参数和get方法的第一个参数本质上是同一个对象,只不过Proxy从外界接收对象,内部传给get。
get:function(target, propKey, receiver){...},//用于拦截“读”请求(my_obj.id 或 my_obj[id]);第一个参数为目标对象,第二个参数为属性名,第三个可选参数与Reflect.get有关
set:function(target, propKey, value, receiver){...},//用于拦截“写”请求(my_obj.id=123 or my_obj[id]=123);不一定要return,直接赋值即可(target[propKey]=value)
apply:function(target, context, args){...},//用于拦截函数的调用、call操作、apply操作,如需在一系列判断后仍执行原函数调用,则在Proxy的apply内,return Reflect.apply(...arguments);三个参数分别为:目标对象,目标对象上下文、目标对象的参数数组
has:function(){...},//下次再学
construct:function(){...},
deleteProperty:function(){...},
defineProperty:function(){...},
getOwnPropertyDescriptor:function(){...},
getPrototypeOf:function(){...},
setPrototypeOf:function(){...},
isExtensible:function(){...},
ownKeys:function(){...},
preventExtensions:function(){...}
});
my_obj.proxy=proxy;//便于通过对象直接调用

Reflect

  1. ES6提供的新的API,将原本部署在Object上的方法复制到Reflect上(今后Object上的方法会逐渐废除),并做了优化,且未来新增的Object上的方法只部署在Reflect对象上。
  2. Reflect对象方法与Proxy对象方法一一对应。
  3. Reflect共有13个静态方法:(具体参数使用时再查)
    · (1)Reflect.get
    . (2)Reflect.set
    · (3)Reflect.apply
    · (4)Reflect.construct (提供一种不写new的构造函数写法)
    · (5)Reflect.defineProperty
    · (6)Reflect.deleteProperty
    · (7)Reflect.has (in运算符)
    · (8)Reflect.ownKeys (=Object.getOwnPropertyNames+Object.getOwnPropertySymbols, 返回字符串数组)
    · (9)Reflect.isExtensible
    · (10)Reflect.preventExtensions
    · (11)Reflect.getOwnPropertyDescriptor
    · (12)Reflect.getPrototypeOf (读取对象的proto属性)
    · (13)Reflect.setPrototypeOf

Promise

Promise,译为“承诺”(只有异步操作能改变Promise对象的状态:Pending->Fulfilled(Resolved) or Pending->Rejected(Resolved),且改变之后不会再改变)。
Promise是ES6引入的异步编程的新解决方案。语法上是一个构造函数,用来封装异步操作可以获取其成功或失败的结果。
Promise可以将异步操作以同步操作的流程表达出来。
缺点:一旦执行,无法中途取消。

  1. Promise构造函数:Promise(excutor){}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const p=new Promise(function(resolve,reject){
    //将异步方法放在Promise实例内
    setTimeout(function(){
    //let data='haha';
    //resolve(data);
    let err='fail';
    reject(err);
    },1000);
    });
    //只要写了.then,实例内的异步方法就会给出回调结果到then内函数里
    p.then(function(value){
    //resolve走这里(then的第一个参数函数)
    console.log(value);
    },function(reason){
    //reject走这里(then的第二个参数函数)
    console.error(reason);
    });
    可以用于封装读取文件;
    可以用于封装ajax请求。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const p=new Promise((resolve,reject)=>{
    const xhr=new XMLHttpRequest();
    xhr.open("GET","https://api.apiopen.top/getJoke");
    xhr.send();
    xhr.onreadystatechange=function(){
    if(xhr.readyState===4){
    //判断状态响应码:200-299
    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);
    });
  2. Promise.prototype.then(fun)
    用于指定回调,为成功和失败结果作处理,对象状态由回调函数的执行结果决定。
    如果回调函数中返回的结果是非promise类型的属性,状态为成功,则返回值为对象的成功的值;
    如果回调函数中返回的结果是promise类型的,外面的promise返回的结果是什么,内层的promise状态就是什么,也可以抛出错误。new p1.then(return new p2...)
    then方法的返回的结果是一个Promise对象:所以可以链式调用p.then(value=>{}).then(value=>{})...避免回调地狱。
  3. Promise.prototype.catch(fun)
    用来指定promise对象失败的回调。
    p.catch(function(){...})
    catch写法优于在.then的第二个参数中定义reject函数,即只要写成:
    1
    2
    3
    var p=new Promise(function(resolve,reject){...})
    .then(function(data){...})
    .catch(function(err){...});
    catch方法返回的也是一个Promise对象,因此也可以继续链式调用(如果是链式catch,下一个方法会捕获上一个方法中的错误)。
  4. Promise.all([…])
    用于将多个Promise实例包装成一个Promise实例。
    All Fulfilled, Fulfilled.
    One Rejected, Rejected.(交集)
    1
    2
    3
    var p=Promise.all([p1,p2,p3])
    .then(function(data){...})//第一个Rejected的进入这里
    .catch(function(err){...});
  5. Promise.race([…])
    One Resolved/Rejected, Resolved/Rejected.(并集)
  6. Promise.resolve(obj)
    转为Promise对象
    · (1) obj为一个Promise对象:返回obj
    · (2) obj为一个普通对象,有then方法:返回Promise对象,并立即执行then方法
    · (3) obj不是对象,或没有then方法:返回Promise对象,从生成时就是Resolved状态,没有异步操作
    · (4) Promise.resolve()直接执行,不带参数:返回Promise对象,在本轮事件循环结束时置为Resolved状态
  7. Promise.reject(msg/obj)
    · (1) 参数为字符串:返回Promise对象,立即执行后状态为Rejected,msg为reject的理由
    · (2) 参数为含有then方法的对象:返回Promise对象,catch得到的参数为obj的then中reject的方法
  8. 自己部署一个done方法(jquery已提供)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Promise.prototype.done=function(onFulfilled, onRejected){
    this.then(onFulfilled, onRejected)
    .catch(function(reason){
    setTimeout(()=>{throw reason},0);
    });
    };
    AsyncFun()
    .then(f1)
    .catch(r1)
    .then(f2)
    .done();//放在回调链的尾端
    done会捕捉到任何错误,并向全局抛出。
    done不返回Promise对象,所以不能再接catch。
  9. 自己部署一个finally方法
    不管Promise对象最后状态如何都会执行的操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Promise.prototype.finally=function(callback){
    let p=this.comstructor;
    return this.then(
    value=>p.resolve(callback()).then(()=>value),
    reason=>p.resolve(callback()).then(()=>{throw reason})
    );
    };
    AsyncFun()
    .then(f1)
    .finally(f2);//f2是一个回调函数,一定会执行
    finally并不是最后一环,done才是。
  10. Promise.try(fun)
    不用区分fun是同步函数还是异步函数,如果是同步函数则同步执行,如果是异步函数则异步执行。
    1
    2
    3
    Promise.try(fun)
    .then(...)
    .catch(...)

Iterator(遍历器) 和 for…of

  1. Iterator是一种接口,可用于遍历操作。
  2. Iterator创造指针对象,调用指针的next方法,指向下一个成员。
  3. Iterator接口部署在Symbol.iterator属性上,原生具备Iterator接口的数据结构:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList对象。
  4. 具有Iterator接口的数据结构,都可以用for…of循环。

class 类

ES6的class可以只看作是一个语法糖,只是让对象的写法更清晰,更像面向对象的编程的语法。

  1. 声明类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //ES5:
    function Phone(brand,price){//构造函数
    this.brand=brand;
    this.price=price;
    }
    Phone.prototype.call=function(){//添加方法
    console.log('打电话');
    }
    let huawei=new Phone('华为',5999);//实例化
    huawei.call();

    //ES6:
    class Phone{
    constructor(brand,price){
    this.brand=brand;
    this.price=price;
    }
    call(){//不支持ES5的对象完整形式
    console.log('打电话');
    }
    }
    let iphonex=new Phone('Apple',10000);

    constructor()方法就算没写,JS也会自动添加。

  2. 静态成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //ES5:
    function Phone{...}
    Phone.name='手机';//往函数对象上添加属性和方法
    Phone.change=function(){...};
    Phone.prototype.size='5.5inch';
    let nokia=new Phone();
    console.log(nokia.name);//undefined 静态成员无法继承
    //实例对象身上没有构造函数对象身上的属性
    console.log(nokia.size);//5.5inch 原型上的属性是有的

    //ES6:
    class Phone{
    static name='手机';//静态成员,实例化对象无法获取
    static change(){...};
    }
  3. 对象继承
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    //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,brand,price);
    this.color=color;
    this.size=size;
    }
    //设置子级构造函数的原型
    SmartPhone.prototype=new Phone;
    SmartPhone.prototype.constructor=SmartPhone;
    //声明子类的方法
    SmartPhone.prototype.photo=function(){...}
    SmartPhone.prototype.playGame=function(){...}
    const chuizi=new SmartPhone('锤子',2499,'黑色','5.5inch');
    console.log(chuizi);//brand√ price√ color√ size√ __proto__:Phone photo√ playGame√ __ptoto__: call√

    //ES6:
    class Phone{
    constructor(brand,price){//构造方法
    this.brand=brand;
    this.price=price;
    }
    call(){
    console.log('打电话');
    }
    }
    class SmartPhone extends Phone{//构造方法
    constructor(brand,price,color,size){
    super(brand,price);//super是父类的constructor,包含了call
    this.color=color;
    this.size=size;
    }
    photo(){...}
    playGame(){...}
    }
    const xiaomi=new SmartPhone('小米',799,'黑色','4.7inch');
    console.log(xiaomi);//子类属性+方法和父类属性+方法都在
  4. 对象继承二:子类对父类方法的重写
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //ES6:
    class Phone{
    constructor(brand,price){
    this.brand=brand;
    this.price=price;
    }
    call(){
    console.log('打电话');
    }
    }
    class SmartPhone extends Phone{
    constructor(brand,price,color,size){
    super(brand,price);
    this.color=color;
    this.size=size;
    }
    photo(){...}
    playGame(){...}
    call(){...}//父类包含这个方法,但是内容和父类不同
    }
    const xiaomi=new SmartPhone('小米',799,'黑色','4.7inch');
  5. get和set
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Phone{
    get price(){
    console.log('价格属性被读取了');
    return '999';
    }
    set price(value){
    console.log('价格属性被修改了');
    }
    let s=new Phone();//实例化一个对象
    s.price='free';
    }

数值扩展

  1. Number.EPSILON:js的最小精度
    Number.EPSILON ≈ 2.220446049250313080808472633361816E-16
    用于判断精度:if Math.abs(a-b)<Number.EPSILON
  2. 二进制、八进制、十六进制
    let b=0b1010;//=十进制的10 二进制用0b开头
    let o=0o777;//=十进制的511 八进制用0o开头
    let x=0xff;//=十进制的255 十六进制用0x开头
  3. Number.isFinite(n):检测一个数是否为有限数
    1
    2
    Number.isFinite(100/0);//false
    Number.isFinite(Infinite);//false
  4. Number.isNaN(n):检测一个变量是否为NaN(not a number)
  5. Number.parseInt(str):字符串转整数 Number.parseFloat(str):字符串转小数
    1
    2
    Number.parseInt('5211314love');//5211314  截断
    Number.parseFloat('3.1415926率');//3.1415926
  6. Number.isInteger(n):判断一个数是否为整数
  7. Math.trunc(n):将数字的小数部分抹掉
  8. Math.sign(n):判断一个数为正数、负数、还是零
    1
    2
    3
    Math.sign(-100);//-1
    Math.sign(0);//0
    Math.sign(33);//1

对象方法扩展

  1. Object.is(a,b):判断两个值是否完全相等,一般情况下相当于===
    1
    2
    Object.is(NaN,NaN);//true
    NaN===NaN;//false
  2. Object.assign(a,b):对象的合并
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const s1={
    a:1,
    b:2,
    c:3
    };
    const s2={
    a:11,
    b:22,
    d:4
    }
    console.log(Object.assign(s1,s2));//{a:11,b:22,c:3,d:4}
    //结果:对象合并,相同属性以后一个为主
  3. Object.getPrototypeOf:获取原型对象 Object.setPrototypeOf:设置原型对象
    1
    2
    3
    4
    const a={name:'aaa'};
    const b={age:18};
    Object.setPrototypeOf(a,b);//将b作为原型对象给a的__proto__属性
    //不建议使用,一般在Object.create时就定义好原型

模块化

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

  1. 优点:防止命名冲突,代码复用,高维护性
  2. 已有的模块化产品:CommonJS=>NodeJS、Browserify,AMD=>requireJS,CMD=>seaJS
  3. 模块化语法:
    export:用于规定模块的对外接口
    import:用于输入其他模块提供的接口
    (1)通用的导入方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //分别暴露
    export let a=1;
    export function b(){...}

    //统一暴露
    c:1;
    function d(){...}
    export {c,d};

    //默认暴露
    export default{
    e:1,
    f:function(){...}
    }
    1
    2
    3
    4
    5
    <script type="module">
    import * as m1 from './src/js/m1.js';

    m1.default.f();
    </script>
    (2)解构赋值形式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //m1.js
    export let a=1;
    export function b(){...}

    //m2.js
    export let a=2;
    export let c='cc';

    //m3.js
    export default{
    let a=3;
    function d(){...}
    }
    1
    2
    3
    4
    5
    <script type="module">
    import {a,b} from './src/js/m1.js';//a,b可以直接使用
    import {a as a1,c} from './src/js/m2.js';//重命名重名变量
    import {default as m3} from './src/js/m3.js';//默认暴露必须要重命名
    </script>
    (3)简便形式,针对默认暴露
    1
    2
    3
    <script type="module">
    import m3 from './src/js/m3.js';
    </script>
    (4)使用模块化方式二
    1
    2
    3
    4
    //main.js
    import * as m1 from './src/js/m1.js';
    import * as m2 from './src/js/m2.js';
    import * as m3 from './src/js/m3.js';
    1
    <script src="./src/js/main.js" type="module"></script>
  4. babel对ES6模块化代码转换
    有的浏览器不一定支持在script标签里增加src属性的方式,所以用 babel 做一个转换,可以将比较新的ES6语法转为有些浏览器只能识别的ES5语法。
    (1)安装工具 babel-cli, babel-preset-env, browserify(webpack)
    (2)npx babel src/js -d dist/js
    (3)打包 npx browserify dist/js/app.js -o dist/bundle.js
  5. ES6模块化引入NPM包
    1
    2
    //先 npm i jquery 安装包
    import $ from 'jquery';//原来的写法:const $ =require('jquery');

ES7新特性

  1. Array.prototypr.includes
    1
    2
    3
    const z=['a','b','c','d'];
    console.log(z.includes('a'));//true
    //与之相比,indexOf返回的是数字下标,includes更方便
  2. 指数操作符 “ ** ”
    1
    2
    console.log(2 ** 10);//1024
    console.log(Math.pow(2,10));//1024

ES8新特性

  1. async和await
    async和await两种语法结合可以让异步代码像同步代码一样。
    (1)async函数
  • async函数的返回值为promise对象
  • promise对象的结果由async函数执行的返回值决定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    async function fn(){
    //return 'aaa';
    //1.只要函数本身返回的不是一个promise对象,则async就能返回一个成功(resolved)的Promise对象

    //throw new Error('出错啦');
    //2.抛出错误,返回的就是一个失败的Promise对象

    //3.返回的结果是一个Promise对象
    return new Promise((resolve,reject)=>{
    //resolve('成功的数据');
    //reject('失败');
    });
    }
    const result=fn();
    console.log(result);//与函数本身返回的结果一致
    (2)await表达式
  • await必须写在async函数中
  • await右侧的表达式一般为promise对象
  • await返回的是promise成功的值
  • await的promise失败了,就会抛出异常,需要通过try…catch捕获处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const p=new Promise((resolve,reject)=>{
    //resolve("成功的值");
    reject("失败了");
    });
    async function main(){
    try{
    let result=await p;
    console.log(result);
    }catch(e){
    console.log(e);
    }
    }
    main();
    (3)例子:async与await结合读取文件内容
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    const fs=require('fs');
    function readMd1(){
    return new Promise((resolve,reject)=>{
    fs.readFile("./xxx1.md",(err,data)=>{
    if(err) reject(err);//如果失败
    resolve(data);//如果成功
    })
    })
    }
    function readMd2(){
    return new Promise((resolve,reject)=>{
    fs.readFile("./xxx2.md",(err,data)=>{
    if(err) reject(err);//如果失败
    resolve(data);//如果成功
    })
    })
    }
    async function main(){
    let result1 = await readMd1();
    let result2 = await readMd2();
    console.log(result1.toString());
    console.log(result2.toString());
    }
    main();
    (4)async与await结合发送Ajax请求
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    function sendAJAX(url){
    return new Promise((resolve,reject)=>{
    const x=new XMLHttpRequest();
    x.open('GET',url);
    x.send();
    x.onreadystatechange=function(){
    if(x.readyState===4){
    if(x.status>=200&&x.status<300){
    resolve(x.resopnse);
    }else{
    reject(x.status);
    }
    }
    }
    })
    }
    //promise then方法测试:
    const result_p = sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
    console.log(value);
    },reson=>{});
    //async与await测试:
    async function main(){
    let result =await sendAJAX("https://api.apiopen.top/getJoke");
    consol.log(result);
    }
    main();
  1. Object.values()
    返回一个给定对象的所有可枚举属性值的数组。

  2. Object.entries()
    返回一个给定对象自身可遍历属性[key,value]的数组。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const obj={
    a:'aaa',
    b:['b1','b2','b3'],
    c:[333,444]
    };
    console.log(Object.keys(obj));//['a','b','c']
    console.log(Object.values(obj));//['aaa',['b1','b2','b3'],[333,444]]
    console.log(Object.entries(obj));//[0:['a','aaa'],1:['b',[...]],2:['c',[...]]]
    console.log(new Map(Object.entries(obj)));//{['a','aaa'],['b',[...]],['c',[...]]}
  3. Object.getOwnPropertyDescriptors()
    返回指定对象所有自身属性的描述对象。
    configurable
    enumerable

ES9新特性

  1. 对象的rest参数和spread扩展运算符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function fn({a,b,...c}){...}
    fn({
    a:'aaa',
    b:123,
    c1:'cc1',
    c2:'cc2',
    c3:333
    })

    const x1={x11:'content1'};
    const x2={x22:'content2'};
    console.log({...x1,...x2});//{x11:'content1',x22:'content2'}
  2. 正则扩展-命名捕获分组
    1
    2
    3
    4
    let str='<a href="https://www.baidu.com">百度</a>';
    const reg=/<a href="(?<url>.*)">(?<text>.*)<\/a>/;
    const result=reg.exec(str);
    consolelog(result);//可以获取result.groups.url, result.groups.text
  3. 正则扩展-反向断言
  4. 正则扩展-dotAll模式

ES10新特性

  1. Object.fromEntries
    Object.entries可以将对象转为二维数组,Object.fromEntries可以将二维数组转为对象(键值对)
  2. 字符串方法扩展:trimStart 和 trimEnd
    指定清除字符串左侧空格和右侧空格
  3. 数组方法扩展:flat 和 flatMap
    flat:将多维数组转为低维数组,从外往内展开,不加参数默认展开一层,参数为深度。
    flatMap:两个操作的结合,先map,后flat。
  4. Symbol.prototype.description
    获取Symbol的字符串描述。
    1
    2
    let s=Symbol('name');
    consol.log(s.description);//name

ES11新特性

  1. 私有属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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.#age);//报错
    girl.intro();//可以获取私有属性
  2. Promise.allSettled
    1
    2
    3
    4
    const p1=new Promise(...);
    const p2=new Promise(...);
    const result=Promise.allSettled([p1,p2]);//只要有一个成功就显示成功
    const result2=Promise.all([p1,p2]);//全都成功才不报错
  3. String.prototype.matchAll
    适合数据的批量提取。
    1
    2
    3
    const str=`...`;
    const reg=/.../;
    const result=str.matchAll(reg);
  4. 可选链操作符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function fn(x){
    //const con=config && config.db && config.db.host;
    const con=x?.name?.a;
    }
    // fn({
    // name:{
    // a:'aaa',
    // b:'bbb'
    // },
    // school:{
    // c:'ccc',
    // d:'ddd'
    // }
    // })
    fn();
    //即使fn没有传入用到的参数,也不会报错,免去了层层判断
  5. 动态import
    ES6的模块化本身是静态的。
    1
    2
    3
    4
    5
    6
    const btn=document.getElementById('btn');
    btn.onclick=function(){
    import('./hello.js').then(module=>{
    module.hello();
    })
    }
  6. BigInt(大整型)数据类型
    1
    2
    3
    4
    5
    6
    7
    let n=521n;
    //整型可以转成大整型
    let n2=123;
    console.log(BigInt(n2));//123n
    //应用于大数值运算
    let max=Number.MAX_SAFE_INTEGER;
    console.log(BigInt(max)+BigInt(1));
  7. 绝对全局对象globalThis
    指向window对象。

编程风格(迷你知识点)

  1. 建议不再使用var命令
  2. let和const之间优先使用const
  3. 静态字符串使用单引号,动态字符串使用反引号,不再使用双引号
  4. 添加属性用Object.assign(obj,{name:’aa’});

End.🐧