# Es6

# let、const

  • let

    • 不可重复声明变量
    • 暂时临时死域
    • 作用域块
  • const

    • 必须先赋值
    • 不可重复声明变量
    • 对于纯数字、字符、等基本结构的话,不可更改,但可以更改数组里面的元素、对象里面的 key
    • 只能去改变引用类型(object array),无法取改变基本类型(booleannumberstringnullundefinedsymbolbigint(Chrome 67+开始))
  • var

    • var 声明,存在变量提升问题
    • var 是全局变量声明的方式
for (var i = 0; i < 5; i++) {}
console.info(i); // 5

for (let i = 0; i < 5; i++) {}
console.info(i); // 抛出未定义 且 for 括号和 大括号是不同的作用域

# 箭头函数

https://www.cnblogs.com/mengff/p/9656486.html

  1. 箭头函数 this 是父作用域的 this,不是调用时的 this

    • 如何知道当前的父作用域
  2. 箭头函数不能作为构造函数,不能使用 new

  3. 箭头函数没有 arguments 和 caller、callee

  4. 箭头函数通过 call、apply 调用不会改变 this 指向,只会入参

  5. 箭头函数没有原型属性

const fn = () => {};
console.log(fn.prototype);
  1. 箭头函数不能作为 Generator 函数,不能使用 yield 关键字

  2. 箭头函数返回对象需要加括号

  3. 箭头函数不能在 ES6 class 中声明的方法为实例方法,不是原型方法

//deom1
class Super {
  sayName() {
    //do some thing here
  }
}
//通过Super.prototype可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super();
var b = new Super();
a.sayName === b.sayName; //true
//所有实例化之后的对象共享prototypy上的sayName方法

//demo2
class Super {
  sayName = () => {
    //do some thing here
  };
}
//通过Super.prototype访问不到sayName方法,该方法没有定义在prototype上
var a = new Super();
var b = new Super();
a.sayName === b.sayName; //false
//实例化之后的对象各自拥有自己的sayName方法,比demo1需要更多的内存空间
  1. 多重箭头函数是高阶函数,相当于内嵌函数,就是闭包函数

# 函数

  • 剩余参数(rest 参数)的表示法,同样,items 是最后一个参数
// 此时的items 是一个数组
function push(array, ...items) {
  items.forEach(function(item) {
    array.push(item);
  });
}

let a = [];
push(a, 1, 2, 3);
console.log(a); //[1,2,3]

# class 类

特殊的函数=> 类表达式类声明

  • 只能有且只有一个constructor方法
  • 一个构造函数可以使用super 关键字来调用一个父类的构造函数
  • static 关键字定义一个类的静态方法。可以不需要实例化该类,但不能通过一个类实例调用静态方法
//demo1
class Home {
  //构造函数
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // getter,隐藏的get属性
  get area() {
    return this.all();
  }
  //methods
  all() {
    return this.height * this.width;
  }
}

const h = new Home(360, 480);
console.log(h.height); //360
console.log(h.area); //就可以调用
console.log(h.area()); //报错。get 的属性值不是一个function

// demo2  `demos` 关键字定义一个类的静态方法。可以不需要实例化该类,但不能通过一个`类实例调用静态方法`
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  //demos 关键字,顶一个类的静态方法
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return Math.hypot(dx, dy); //参数平方和的平方根
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
// 相当于Point.distance({x:5,y:10},{x:10,y:5})
  • 用原型和静态方法包装
class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat(); // class Animal
let eat = Animal.eat;
eat(); // undefined

//当然了。如果小改动了一下

speak.bind(obj)(); //这样就可以了~~

// demo2 ,知道结果可能如下,但不太理解这样的方式
function Animal() {}

Animal.prototype.speak = function() {
  return this;
};

Animal.eat = function() {
  return this;
};

let obj = new Animal();
let speak = obj.speak;
speak(); // global object

let eat = Animal.eat;
eat(); // global object
  • Object.setPrototypeOf() 继承常规对象
  • extends 创建子类
// 父类
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name + "Noise");
  }
}
// 子类
class Dog extends Animal {
  speak() {
    //此处基础父类的this 属性name 值
    console.log(this.name + "by dog");
  }
}

//实例化
const d = new Dog("Lilei");
d.speak();
  • species

    派生数组类。返回 Array 对象,允许覆盖默认的构造函数。类似map()返回默认构造函数的方法时,希望返回一个父 Array 对象,而不是 Arr,可以Symbol.species

class Arr extends Array {
  static get [Symbol.species]() {
    return Array;
  }
}
const a = new Arr(1, 2, 3);
const mapped = a.map((x) => x * x);
console.log(mapped instanceof Arr);
console.log(mapped instanceof Array);
  • supper

    supper 关键字用于调用对象的父对象上的函数

// demo1 这个demo 看不出来什么
class Cat {
  constructor(name) {
    this.name = name;
  }
  speack() {
    console.log(this.name + " makes a noise");
    return 2;
  }
}
class Lio extends Cat {
  speak() {
    super.speak();
    console.log(this.name + " for Lio");
    return 111;
  }
}
const animal = new Lio("litter red");

/** demo2 super 简单应用 */
// 声明一个对象
const Family = {
  name: "Jo Home",
};
// 再生一个对象,内含一个函数`getName`
const main = {
  getName() {
    return super.name;
  },
};
let home = main.getName();
console.log(home);
//以上这样做并没有什么卵用,但是如果使用了Object.setPrototypeOf(要设置在原型上的对象,prototype)
// 在home前面增加
Object.setPrototypeOf(main, Family);
/** demo3 关于class*/
supper.name;
// 等同于 属性
Object.getPrototypeOf(this).name;
// 等同于 方法
Object.getPrototypeOf(this).name.call(this);

# Promise 对象

状态的变更

缺点:无法向外抛出错误移除,并主动中断这样的流程结果

const promise = new Promose((resolve, reject) => {
  const a = 1;
  if (a === 1) {
    resolve("ddd");
  } else {
    reject("sss"); //最好是返回一个变量,不然某些环境下,会导致警告或者报错,可以是字符串、数组、对象,但只能是一个参数
  }
});

promise()
  .then((res) => {
    console.log(res);
  })
  .cacth((err) => {
    console.log();
  });
  • resolve()

    • 只能入参一个,但可以是数组对象
  • reject()

    • 只能入参一个,但可以是数组对象
  • then()

  • catch()

  • finally()

  • all()

  • racr()

# Generator

# async await

  • generator 的语法糖
  • Generator 的改进
    • 内置执行器
    • 更好的语义
    • 更广的适用性
    • 返回值是 promise
  • async 函数的的返回值是 Promise 对象,aysnc 表示 该函数内部有异步操作
  • await 命令后可以是 Promise 对象和原始类型的值(数值,字符串,布尔值,此时等同于同步操作)
  • 如果包装成为一个函数,then 里面表示当遇到 await 是执行 then 然后才执行后面
  • 如何使用 asyns/await
    • 函数声明
    • 函数表达式
    • 对象的方法
    • class 的方法
    • 箭头函数方法
// demo1
async function all() {
  return new Promise((resolve, reject) => {
    let time1 = Math.random();
    let time2 = Math.random();
    // 第一个异步
    setTimeout(() => {
      console.log(1, time1 * 10 * 5000);
      resolve(time1 * 10 * 5000);
    }, time1 * 10 * 5000);

    // 第二个异步

    setTimeout(() => {
      console.log(2, time2 * 10 * 5000);
      resolve(time2 * 10 * 5000);
    }, time2 * 10 * 5000);
  });
}
all()
  .then((res) => {
    console.log(3, res);
  })
  .catch((err) => {
    console.log(4, err);
  });

/*************************************************/
// 第一个异步
async function all1() {
  return new Promise((resolve) => {
    let time1 = Math.random();
    setTimeout(() => {
      console.log(1, time1 * 10 * 1000);
      resolve(time1);
    }, time1 * 10 * 1000);
  });
}

// 第二个异步
async function all2() {
  return new Promise(() => {
    let time2 = Math.random();
    setTimeout(() => {
      console.log(2, time2 * 10 * 1000);
    }, time2 * 10 * 1000);
  });
}

// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
  console.log("a");
  await all1();
  console.log("b");
  await all2();
  console.log("c"); //这个不会执行,以为还在等待promise 的回来
}
all();

而以下代码呢?

// 第一个异步
async function all1() {
  return new Promise((resolve) => {
    let time1 = Math.random();
    setTimeout(() => {
      console.log(1, time1 * 10 * 1000);
      resolve(time1 * 10 * 1000);
    }, time1 * 10 * 1000);
  });
}
// 第二个异步
async function all2() {
  return new Promise((resolve) => {
    let time2 = Math.random();
    setTimeout(() => {
      console.log(2, time2 * 10 * 1000);
      resolve(time2 * 10 * 1000);
    }, time2 * 10 * 1000);
  });
}

// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
  console.log("a");
  await all1().then((res1) => {
    console.log(res1);
  });
  console.log("b");
  await all2().then((res2) => {
    console.log(res2);
  });
  console.log("c");
}
all();

// 结论是
/*a
Promise {<pending>}
1 2824.509694408435
27 2824.509694408435
29 b
16 2 6266.805712440053
32 6266.805712440053
34 c*/

再看一下这个 结论: a 第 0s——10s 计时后,打印 1 10 10 异步一函数的 then 打印 b 第 10s——17s 计时后, 打印 2 8 8 异步二函数的 then c 第 18s……

// 第一个异步
async function all1() {
  return new Promise((resolve) => {
    let time1 = 10;
    setTimeout(() => {
      console.log(1, time1);
      resolve(time1);
    }, time1 * 1000);
  });
}
// 第二个异步
async function all2() {
  return new Promise((resolve) => {
    let time2 = 8;
    setTimeout(() => {
      console.log(2, time2);
      resolve(time2);
    }, time2 * 1000);
  });
}

// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
  let i = 0;
  setInterval(() => {
    console.log(i++);
  }, 1000);
  console.log("a");
  await all1().then((res1) => {
    console.log(res1);
  });
  console.log("b");
  await all2().then((res2) => {
    console.log(res2);
  });
  console.log("c");
}
all();

再看,把 async/await 里面有两个普通的定时任务会怎么样?

结论,此时 all1 与 all2 是异步任务了, a b c 0s-8s 计时 2 8 9s 10 s 1 10

// 第一个异步
async function all1() {
  let time1 = 10;
  setTimeout(() => {
    console.log(1, time1);
  }, time1 * 1000);
}
// 第二个异步
async function all2() {
  let time2 = 8;
  setTimeout(() => {
    console.log(2, time2);
  }, time2 * 1000);
}

// 一个普通async 函数里面,执行两个异步函数会怎么样呢?
async function all() {
  let i = 0;
  setInterval(() => {
    console.log(i++);
  }, 1000);
  console.log("a");
  await all1();
  console.log("b");
  await all2();
  console.log("c");
}
all();
// 以下声明都成立
function* a1() {}
function* a2() {}
function* a3() {}
function* a4() {}

a1();
a2();
a3();
a4();

function* hello() {
  yield "hello"; //yield 表达式
  yield "world"; //yield 表达式
  return "hellow and world";
}
  • 分段执行。yiled 表示暂停执行的标志,next 表示恢复执行
  • es6 提供的异步编程解决方案。阮一峰 Generator 函数的语法 (opens new window)
  • 状态机,封装了多个内部状态
  • *星号 function * a(){}
  • 函数体内部使用了 yield 表达式,定义不同的内部状态(yield 产出的意思) function * a(){yield 'hello';};var func = a();

# Symbol

  • 无法计算
  • 描述值相同,两个值也是不相同的
  • 无法使用new 命令,symbol 不是一个对象
  • 描述值是一个对象,则调用该对象的 toString()方法转为字符
  • Symbol 无法与其他类型进行运算
  • 每个 Symbol 值都不相等,保证不会出现同名的属性
  • Symbol 作为属性名,不会出现在for...infor...of循环中
  • 无法被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回
  • 可通过 Object.getOwnPropertySymbols方法,返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值
  • Symbol.for()与 Symbol()前者调用返回存在的值,否则每次都新建

# 消除魔术字符串

# 属性名遍历

const obj = {
  [Symbol(21)]: 8,
};
Object.getOwnPropertySymbols(obj);

# Symbol.for()

# Symbol.keysFor()

# todo Reflect

现阶段一些方法同时部署到 Object 、Reflect 对象上,未来某些方法只能从 Reflect 上获取,比如 Object.defineProperty

  • 合理化取值。比如 Object.defineProperty(obj,name,desc) 在无法定义属性时,报错,但 Reflect.defineProperty(obj,name,desc) 会返回 false
// 旧的方法
try {
  Object.defineProperty(target, property, attributes);
  // 成功的取值
} catch (e) {
  // 失败
}

if (Reflect.defineProperty(target, property, attributes)) {
  // 成功
} else {
  // 失败
}
  • Object操作都变成了函数行为,而不是一个操作符
// 旧的写法
"assign" in Object; //true
// 新写法
Reflect.has(Object, "assigne"); //true

// 旧的delete 操作符
var obj = {
  name: "Old",
};
delete obj.name;

// 新的函数式操作方法
Reflect.deleteProperty(obj, "name");
  • Reflect 对象的方法和 Proxy 对象的方法一一对象,只要在 Proxy 有,Reflect 就会有。
  • 不管 Proxy 怎么修改默认行为,总可以在 Reflect 上获取默认行为
var obj = new Proxy(obj, {
  get(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log("delete ", name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log("has", name);
    return Reflect.has(target, name);
  },
});
  • proxy 的拦截操作,内部的偶调用对应的 Reflect 方法,保证原生行为能够正常执行
  • 易阅读
// old
Function.prototype.apply.call(Math.floor, undefined, [1.6]); //1

// new
Reflect.apply(Math.floor, undefined, [1.6]); //1

# Reflect 静态方法:一共 13 个

  • Reflect.apply(target,thisArg,args)
  • Reflect.construct(target,args)
  • Reflect.get(target,name,receiver)
  • Reflect.set(target,name,value,receiver)
  • Reflect.defineProperty(target,name,dec)
  • Reflect.deleteProperty(target,name)
  • Reflect.has(target,name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.prevenExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target,name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target,prototype)

# 使用 Proxy 实现观察者模式

观察者模式(Observer mode)

const queueObservers = new Set();
const observer = (fn) => queueObservers.add(fn);

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queueObservers.forEach((ob) => ob());
  return result;
}

先定义一个 Set 集合,所有观察者函数都放进这个集合,observable 函数返回对象的代理,拦截赋值操作,拦截函数 set 中,启动执行所有观察者

# 字符串

# 模板字符串无法的打印 symbol

const sy1 = Symbol(11);

console.log(sy1);

console.log(`${sy1}`); // `Cannot convert a Symbol value to a string`