es6常用总结

官方文档

前言

一些老版本浏览器对es6不识别,所以可以用babel去吧es6转化为es5,
安装转化工具

npm install -g babel-cli

转化指定目录

npx babel src --out-dir lib

或者可以在package.json的script中使用

"build": "babel src --out-dir dist"

新建.babelrc

{
    "presets": [
        "es2015"
    ],
    "plugins": []
}

使用命令

npm run build

1. let 关键字

作用:

与var类似, 用于声明一个变量;

var 特点:声明的变量会被提升;

特点:

1.不会预处理, 不存在提升;

2.在块作用域 { } 内有效;

2、const关键字

定义一个常量, 不能修改,保存不用改变的数据(常量)

3.解构赋值

从数组或对象中提取值, 对应赋值给多个变量

let [a,b] = [1, 'atguigu'];
console.log(a, b) // 1, 'atguigu'


let person = {
    name: '吴杰',
    age: 20
}
let {name, age, sex} = person
console.log(name, age, sex) // 吴杰 20 undefined

如果对象变量名与属性名不一致,这样写

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

4.模板字符串``

`名字:${obj.name}  年龄:${obj.age} 'sdsads'`

5.简化的对象写法:对象增强表达

省略同名的属性值

let obj = {
  a,   		//省略同名的属性值
  b,
  setA (x) {  	//省略function关键字
    this.a = x;
  }
}
console.log(obj);

6、箭头函数

特点
1、简洁
2.它不像其他函数似的有原型对象,他没有,但他有隐式原型

const foo=()=>{}
console.log(foo.prototype);//undifind
console.log(foo.__proto__);//ƒ () { [native code] }

3、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在**定义**的时候处在的对象就是它的this,而正常的函数则是在调用时才能确定他的this。
4、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。

let obj = {};
function foo1() {
     let fn9 = () => console.log(this);  //window
     fn9();
 }
 foo1();


 function foo2() {
     let fn9 = () => console.log(this);  //obj
     fn9();
 }
 foo2.call(obj);


 let fn10 = () => console.log(this); //window
 function foo3() {
     fn10();
 }
 foo3.call(obj);


 let fn11 = () => {
     console.log(this);  //window
     let fn12 = function () {
         console.log(this);  //obj
         let fn13 = () => {
             console.log(this);  //obj
             let fn14 = () => {
                 console.log(this);  //obj
             }
             fn14();
         }
         fn13.call(window)
     }
     fn12.call(obj);
 }
 fn11()


let obj1 = {
  sayName(name) {
        let fn = () => {
            console.log(this);  //obj1
        }
        fn();
    }
}
obj1.sayName('bob');

7.扩展运算符(…)

  • 从容器中将多个数据拆分出来(打包/解包)
  • 底层是遍历interator实现的,任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。
function fn(...values) {	// values为真数组,arguments为伪数组
}
fn(1, 2, 3)
// 数组
const arr1 = [1,2,3]
const arr2 = [4, ...arr1,6] 
console.log(arr2) // [4, 1, 2, 3, 6]

// 对象
 var obj={name: 'zhaohui', age: 18}
 var newObj = {...obj, sex: 'NU'}
 console.log(newObj) // {name: "zhaohui", age: 18, sex: "NU"}

8.形参的默认值

let fn = (x = 1, y = 2) => x + y;
console.log(fn());  //3

9. promise

将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称’回调地狱’)

  • 三种状态 【pending: 初始化状态 fullfilled: 成功状态 rejected: 失败状态】
  • Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
promise.then(res => {}).catch(() => {}).finally()
  • 调用resolve或reject并不会终结 Promise 的参数函数的执行
new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1
上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,
并且会首先打印出来。这是因为立即 resolved 的Promise 是在本轮事件循环的
末尾执行,总是晚于本轮循环的同步任务。
  • Promise.resolve(参数) 直接将promise的初始化状态转化为成功状态,并且返回了一个promise实例对象
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  • Promise.reject(参数) 直接将promise的初始化状态转化为失败状态,并且返回了一个promise实例对象
  • Promise.all有一个失败了,就只走失败的,返回的数据是一个数组,返回的是promise对象,因为可以调用.then.catch
function promise1() {
      return new Promise((resolve, reject) => {
        resolve('你好,我是promise1成功')
      })
    }
    function promise2() {
      return new Promise((resolve, reject) => {
        reject('我是失败的promise2')
      })
    }
    Promise.all([promise1(), promise2()]).then(res => {
      console.log(res) 
    }).catch(err => {
      console.log('失败', err) // 失败 我是失败的promise2
    })
  • Promise.allSettled() (es2020)该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected,可以对比上面.all方法看
function promise1() {
      return new Promise((resolve, reject) => {
        resolve('你好,我是promise1成功')
      })
    }
    function promise2() {
      return new Promise((resolve, reject) => {
        reject('我是失败的promise2')
      })
    }
    Promise.allSettled([promise1(), promise2()]).then(res => {
      console.log(res) // [{status: 'fulfilled', value: '你好,我是promise1成功'},{status: 'rejected', reason: '我是失败的promise2'}]
    }).catch(err => {
      console.log('失败', err)
    })
  • Promise.any()只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
function promise1() {
   return new Promise((resolve, reject) => {
     resolve('你好,我是promise1成功')
   })
 }
 function promise2() {
   return new Promise((resolve, reject) => {
     reject('我是失败的promise2')
   })
 }
 Promise.any([promise1(), promise2()]).then(res => {
   console.log(res) // 你好,我是promise1成功
 }).catch(err => {
   console.log('失败', err)
 })
  • Promise.race() 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
const p = Promise.race([p1, p2, p3]);

如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);
  • promise和try catch区别
    p处理异步,t处理同步
    p出错才走catch,t先走try,报错才被catch捕获
    这是的文字。

10.Symbol

前言:
ES5中对象的属性名都是字符串,容易造成重名,污染环境
ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、大整数(BigInt)、对象(Object))
作用:
由于以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题; 对象中的symbol属性必须通过 [ ],取/赋值;

let symbolName = Symbol("name");
let symbolAge = Symbol("age");
let person = { name: "lala" };
person[symbolName] = "Bob";
person[symbolAge] = 18;
console.log(person, person[symbolName], person[symbolAge], person.name);
// {name: 'lala', Symbol(name): 'Bob', Symbol(age): 18} 'Bob' 18 'lala'

2、Symbol值不能与其他数据进行计算,包括同字符串拼串

let symbolName = Symbol("name")
console.log(symbolName + 'lala') // 报错Uncaught TypeError: Cannot convert a Symbol value to a string

3、for in, for of遍历时不会遍历symbol属性。(虽然symble中的那种接口叫symbol.iterator遍历,但是它不给自己用啊)

let symbolName = Symbol("name")
let person = { sex: "nan" }
person[symbolName] = "Bob"
for (let key in person) {
  console.log(key); //只会打印sex
}
  • symbol 写法
let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
  • Symbol.for()
    -为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。
    -Symbol.for()的这个全局登记特性,可以用在不同的 iframe 或 service worker 中取到同一个值。
    -Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for(“cat”)30 次,每次都会返回同一个 Symbol 值,但是调用Symbol(“cat”)30 次,会返回 30 个不同的 Symbol 值。
const s1 = Symbol.for('name');
const s3 = Symbol.for('name');  //如果全局没有name标识名称的symbol,会新建一个,如果有,使用之前的
console.log(s1==s3)//true;

const s1 = Symbol('name');
const s3 = Symbol('name');  
console.log(s1==s3)//false;

const sym = Symbol('foo');
sym.description // "foo"

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"

11、Iterator遍历器

概念:
iterator(指针对象或者遍历器对象)是一种接口机制,为各种不同的数据结构提供统一的访问机制:任何一种数据结构,只要部署了symbol.iterator 接口,就可以利用for…of命令完成遍历操作。(我理解的就是一个遍历机制)
作用:
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列;
3、ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
工作原理:

  • 创建一个指针对象(遍历器对象),指向数据结构的起始位置。

  • 第一次调用next方法,指针自动指向数据结构的第一个成员

  • 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员

  • 每调用 next() 方法:返回的是一个包含value和done的对象,
    {value: 当前成员的值,done: 布尔值}
    value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。

  • 当遍历结束的时候返回的 value 值是 undefined,done 值为 true

  • eg: …三点运算符就是通过调用 symbol.iterator 接口实现的

打印数组原型上有symbol.iterator,对象没有,所以对象不能用for of 遍历–但是后期可以借助generator函数实现。在这里插入图片描述

12、Generator函数(相当于promise的升级版)

项目中不会用到这个,已经被async替代
概念:
1、ES6提供的解决异步编程的方案之一
2、Generator函数是一个状态机,内部封装了不同状态的数据,
3、用来生成遍历器对象
4、可暂停函数(惰性求值), yield 可暂停,next 方法可启动。
调用 next 方法函数内部逻辑开始执行,遇到 yield 表达式停止,

能够将iterator接口部署到对象上去–直接for of 对象会报错,借助Generator就可以使对象能用for of 了

    const person = {
      name: 'jack',
      age: 18,
      sex: '男'
    }
    person[Symbol.iterator] = function* () {
      for (let key in this) {
        yield this[key];
      }
    }
    console.log(person);
    for (let value of person) {
      console.log(value);
    }

es6常用总结_第1张图片

13、async函数(Generator的语法糖)

  • async取代Generator函数的星号*,await 取代 Generator 的 yield
  • 真正意义上去解决异步回调的问题同步流程表达异步操作(结合promise一起使用)
  • async返回的总是Promise对象
async function foo(){
    await 异步操作;--promise对象
    await 异步操作;;--promise对象
  }

示例

  • 目前: 打印结果为
    【fn函数开始调用了
    a数据请求完毕了~
    失败 456】

  • 若promise1 为resolve,打印结果
    【fn函数开始调用了
    a数据请求完毕了~
    result 456
    fn函数结束调用了
    成功 123】

  • 综上可以看到如果是reject,函数下面就不执行了,直接到.catch了, err就为reject里的值----- 毕竟都不会走到最下面return,所以跟return无关,,,若为resolve,那result为resolve的值, .then里面的res就为return的值

const promise1 = new Promise((resolve, reject) => {
      setTimeout(function () {
        console.log('a数据请求完毕了~');
        reject(456);
      }, 2000)
    })
    async function fn() {
      console.log('fn函数开始调用了');
      const result = await promise1;
      console.log('result', result);
      console.log('fn函数结束调用了')
      return 123;
    }
    const promise = fn();
    promise
      .then(res => {
        console.log('成功', res);
      })
      .catch(err => {
        console.log('失败', err);
      })

14、class类

14.1 跟es5构造函数比较

  • 跟构造函数相比,写法简单,你像构造函数写方法得在函数原型上写,但是class,即明了,然后还都在里面写
  • 继承也很明了,也简单
  • class不能提升, 构造函数可以

写法

function Point(x, y) {
   this.x = x;
    this.y = y;
  }

  Point.prototype.toString = function () {
    return '(' + this.x + ', ' + this.y + ')';
  };

  var p = new Point(1, 2);

  // 上面这种写法,等价于下面class类的写法
  // 类的所有方法都是相当于定义在原型上
  class Point {
    //constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
    //一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会
    //被默认添加。里面这个this.**是直接定义在属性上的
    constructor(x, y) {
      this.x = x;
      this.y = y;
    }

    toString() {
      return '(' + this.x + ', ' + this.y + ')';
    }
  }
  • class里面定义的方法都是不可枚举的,就是里面的那个construct,和方法用 Object.keys拿不出来, 想要拿出来可以用getOwnPropertyNames
class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    getName() {
        console.log(Person.name) // Person
    }
}
function Point(name) {
    this.name = name
}
Point.prototype.setLa = function() {
    console.log(this.name) // a
}
// const s = new Point、Person('a')
console.log(Object.keys(Person.prototype), Object.keys(Point.prototype)) // [] ['setLa']
console.log(Object.getOwnPropertyNames(Person.prototype)) // ['constructor', 'getName']
  • 跟构造函数命名函数一样,在内部可以有那个函数名,在外部不可引用,并且可以查询函数.name
const MyClass = class Me {
    getClassName() {
        return Me.name;
    }
};
let inst = new MyClass();
console.log(inst.getClassName()); // Me
Me.name // ReferenceError: Me is not defined

定义函数也可以访问到name

class Point {}
Point.name // "Point"

14.2 static静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
 static classMethod() {
   return 'hello';
 }
}

console.log(Foo.classMethod()) // 'hello'

var foo = new Foo();
console.log(foo.classMethod())// TypeError: foo.classMethod is not a function

注意:如果静态方法包含this关键字,这个this指的是类,而不是实例。

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

14.3 extends继承

  • 父类的静态方法,可以被子类继承
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
  • 在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。
  • Object.getPrototypeOf一个类是否继承了另一个类。
class Point {
    constructor(x, y) {
      this.x = x;
      this.y = y;
    }
    setName() {
      console.log('lalala')
    }
  }

  class ColorPoint extends Point {
    constructor(x, y, color) { // 如果只是继承Point 的x,y属性,没有多余的就直接不写这个constructor
      // 也行,但是如果有多余的就得写constructor了,super就是继承的属性,别的就直接自己写this.color = color
      // this.color = color; // ReferenceError, 报错,这里不能用this
      super(x, y);
      this.color = color; // 正确
    }
    // 继承的也直接继承了上面那个setName的方法,但是这个方法是在原型的原型上才有
    // 咱们在自己这写的run方法就在原型上
    run() {
      console.log('run')
    }
  }
  let cp = new ColorPoint(25, 8, 'green');
  console.log('cp', cp)

  //实例对象cp同时是ColorPoint和Point两个类的实例
  console.log(cp instanceof ColorPoint) // true
  console.log(cp instanceof Point) // true
  // Object.getPrototypeOf一个类是否继承了另一个类。
  Object.getPrototypeOf(ColorPoint) === Point// true

14.4 私有属性的提案

  • 私有属性就是在class内部可以用,外部调用不行,会报错
class IncreasingCounter {
#count = 0;
    increment() {
    console.log(this.#count)
    }
}
const counter = new IncreasingCounter();
counter.increment()  //这个可以正常打印
console.log(counter.#count)// 这个会报错

14.5 其他注意问题

  • printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。
class Logger {
      printName(name = 'there') {
        this.print(`Hello ${name}`);
      }

      print(text) {
        console.log(text);
      }
    }

    const logger = new Logger();

    // 这样写会报错 // TypeError: Cannot read property 'print' of undefined
    // const { printName } = logger;
    // printName()
    
    // 这样才会正常打印
    logger.printName() // Hello there

15、Module模块

  1. 通过export 关键字暴露模块
  2. 通过import关键字引入模块

16.proxy 拦截器

1.拦截读取

var proxy = new Proxy({name: '张三', age: 18}, {
  get: function (target, property) {
      return 35;
    }
  });

  console.log(proxy.name, proxy.age)// 35, 35

2.如果handler没有设置任何拦截,那就等同于直接通向原对象。

var target = {a: 'b'};
var handler = {};
var proxy = new Proxy(target, handler);
console.log(proxy.a) // "b"
// 上面代码中,handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target。

17.模板编译 <%=

let template = `
    <% for(let i=0; i < data.supplies.length; i++) { %>
  • <%= data.supplies[i] %>
  • <% } %>
` 说明:该模板使用<%...%>放置 JavaScript 代码,使用<%= ... %>输出 JavaScript 表达式。

18 对象扩展

  • Object.assign
  • Object.is

19.Set

Set容器 : 无序不可重复的多个value的集合体 === 【对标数组】
方法:

  • add(value):往set容器里添加值;
  • delete(value):删除 set 中的元素;
  • has(value):判断set容器里面有没有该值;
  • clear():清空set容器里面的所有值;
  • size:类似length,表示里面元素的数量

特点:

  • 可以用for of遍历
  • 多用于数组去重
[...new Set(arr)]

20 map

无序的, key不重复的多个key-value的集合体 === 对标对象
其实就是因为对象属性必须是字符串类型,但是这个map相当于扩充了,key可以为任何类型

方法:

  • set(key, value);添加属性及属性值;
  • get(key)
  • delete(key)
  • has(key)
  • clear()
  • size
    特点:
    可以用for of 遍历
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world
let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let arr = [...map.keys()]; // [1, 2, 3]

21 数值扩展

  1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o(8421法);
  2. Number.isFinite(i) : 判断是否是有限大的数
  3. Number.isNaN(i) : 判断是否是NaN
  4. Number.isInteger(i) : 判断是否是整数
  5. Number.parseInt(str) : 将字符串转换为对应的数值
  6. Math.trunc(i) : 直接去除小数部分

22 链式运算符-ES2020

左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined


const firstName = (message
  && message.body
  && message.body.user
  && message.body.user.firstName) || 'default';
等同于上方
const firstName = message?.body?.user?.firstName || 'default';

23 Null 判断运算符?? --es2020

如果某个属性的值是null或undefined, 指定默认值。
这种做法属性的值如果为空字符串或false或0,默认值也会生效。

const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

若只想只有运算符左侧的值为null或undefined,才会返回右侧的值

const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

24 ||=、&&=、??=逻辑赋值运算符 § ⇧ — ES2021

// 老的写法
user.id = user.id || 1;
// 等同于
// 新的写法
user.id ||= 1;

// 与赋值运算符
x &&= y
// 等同于
x && (x = y)

// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)

你可能感兴趣的:(js,es6,前端,ecmascript)