Skip to content

Es6

let、const

  • let

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

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

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

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

数组

以下 为 es6 引入的新特性

特性静态方法实例方法描述
Array.from()
Array.of()替代 Array() 方法,强化一致性
copyWithin()会修改原始数组
- 性能优化
- 音频处理
find()返回查找的第一项,存在 undefined
findIndex()找到返回索引值,否则 -1
findLast()
findLastIndex()
fill()
entries()
keys()
values()
includes()
flat()
flatMap()
at()
toReversed()
toSorted()
toSpliced()
width()
group()
groupToMap()
数组空位

Array.from()

Array.of()

find()

和下面方法是一组类似的函数:

  • findIndex()
  • findLast()
  • findLastIndex()

同样的,filter() 及其相近,只是 filter() 返回数组,存在多个,find() 返回第一个匹配的元素。

filter() 区别:

filter()find()
时间复杂度O(n)最佳 O(1)、最差 O(n)
链式调用更具有多段处理能力单个
返回值始终数组undefined 的额外判断
性能考虑性能差性能优先

findIndex()

返回匹配得第一项的索引值,否则 -1

indexOf()find()
查找简单最佳✅⚠️一般
查找属性
复杂度条件
返回值一样一样

findLast()

从后往前,其他跟 find()一样

findLastIndex()

从后往前,其他跟 findIndex()一样

fill()

用于填充数组。填充的元素可以是任意类型,

参数:

  • value
  • start
  • end
js
[1, , 2, 3, 4, 5, 6, 7, 8, 9, 10].fill(7)
// [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]

new Array(3).fill(6)
// [6, 6, 6]

Array.of(1, 2, 3).fill(8)
// [8, 8, 8]

keys()

  • 返回迭代器,for ... of 语句可以遍历
js

const a = ['a', 'b'].keys()

console.log(a)
// Array Iterator {}...

Array.from(a)
// [0,1]

values()

  • 返回迭代器,for ... of 语句可以遍历
js

const b = ['a', 'b'].values()

console.log(b)
// Array Iterator {}...

console.log(b.next())
// {value:"b",done:false}

Array.from(b)
// ["a","b"]

console.log(b.next())
// {value:undefined,done:true}

entries()

js
for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

// 也可以适用 next() 方法获取
console.log(entries.next().value); // [0, 'a']

includes

与字符串引入的 includes 一样,返回布尔值,表示是否包含给定的值。

  • 更加严格,比如 NaN 误判

参数:

  • value
  • start

es5 之前达到判断效果的方法:

js
const arr = [1, 2, 3];
const el = 2;
if (arr.indexOf(el) !== -1) {
  // ...
}

// or

if (~arr.indexOf(el)) {
  // ...
}

兼容性方案:

js
const constains = (() => {
  return Array.prototype.includes
    ? (arr, value) => err.includes(value)
    : (arr, value) => arr.some(el => el === value)
})

constains(['a', 'b'], ' a')

flat()

拍平数组,默认拍平一层,无限层,则入参是 Infinity

  • 默认自动跳过空位
  • 不改变原数组

flatMap()

执行一次 map() 函数,然后对返回值再 flat(),返回新数组。

处理嵌套数据结构:

js

const books = [
  {title: "Book 1", tags: ["fantasy", "adventure"]},
  {title: "Book 2", tags: ["sci-fi"]}
];

// 获取所有标签的扁平数组
const allTags = books.flatMap(book => book.tags);
// ["fantasy", "adventure", "sci-fi"]

字符串处理:

js
const sentences = ["Hello world", "Goodbye universe"];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ["Hello", "world", "Goodbye", "universe"]

at()

允许使用负数索引,超出返回,返回 undefined

js
const arr = [1, 2, 3]

arr.at(2) // 3

arr.at(-1) // 3

toReversed()

不改变原始数组,返回一个新数组。

对比es6es5
toReversed()reverse()

toSorted()

不改变原始数组,返回一个新数组。

对比es6es5
toSorted()sort()

toSpliced()

执行位置,删除指定数量成员,并插入心智,不改变原始数组,返回一个新数组。

对比es6es5
toSpliced()splice()

with()

用来将指定位置成员替换为新值,不改变原始数组,返回一个新数组。

对比es6es5
with()splice(index,1,value)

group() 暂不兼容

数组成员分组,返回一个新对象,字符串分组,使用 group()

js
const array = [1, 2, 3, 4, 5];

array.group((num, index, array) => {
  return num % 2 === 0 ? 'even' : 'odd';
});
// { odd: [1, 3, 5], even: [2, 4] }

groupToMap() 暂不兼容

数组成员分组,返回一个新 Map 对象,key 可以是各种值,对象分组使用 groupToMap()

js
const array = [1, 2, 3, 4, 5];

const odd = {odd: true};
const even = {even: true};
array.groupToMap((num, index, array) => {
  return num % 2 === 0 ? even : odd;
});

空位处理

es5 中:

方法跳过空位
forEach()
filter()
reduce()
every()
some()
map()✅ 跳过保留
-✅保留这个值
join()视为 undefined, 和 null 处理为空字符串
toString()视为 undefined, 和 null 处理为空字符串

es6 中,空位视为 undefined

处理空位的方法或语句:

  • Array.from()
  • ...拓展符
  • copyWithin()
  • fill()
  • for ... of

将空位视为 undefined:

  • entries()
  • keys()
  • values()
  • find()
  • findIndex()

sort 稳定性

js
const arr = [
  {id: 1, val: 2},
  {id: 2, val: 2},
  {id: 3, val: 1}
];
  • 稳定排序:关键字相同项目后,排序前后的顺序不变,默认
js
// 结果可能为:
// [
//   {id: 3, val: 1},
//   {id: 2, val: 2}, // id: 2 跑到 id: 1 前面
//   {id: 1, val: 2}
// ]
  • 不稳定排序:关键字排序后,再排序
    • 多重排序有问题
js
// 新版引擎输出(稳定):
arr.sort((a, b) => a.val - b.val);
// 结果保证:
// [
//   {id: 3, val: 1},
//   {id: 1, val: 2}, // id: 1 和 id: 2 顺序不变
//   {id: 2, val: 2}
// ]

箭头函数

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

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

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

  • 箭头函数没有 arguments 和 caller、callee

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

  • 箭头函数没有原型属性

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

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

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

js
//demo1
class Super {
  sayName() {
    //do some thing here
  }
}

//通过Super.prototype可以访问到sayName方法,这种形式定义的方法,都是定义在prototype上
var a = new Super();
var b = new Super();
a.sayName === b.sayName; //true
//所有实例化之后的对象共享 prototype 上的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需要更多的内存空间
  • 多重箭头函数是高阶函数,相当于内嵌函数,就是闭包函数

函数

  • 剩余参数 (rest 参数) 的表示法,同样,items 是最后一个参数
js
// 此时的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 关键字定义一个类的静态方法。可以不需要实例化该类,但不能通过一个 类实例调用静态方法
js
//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})
  • 用原型和静态方法包装
js
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 创建子类
js
// 父类
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

js
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 关键字用于调用对象的父对象上的函数

js
// demo1 这个demo 看不出来什么
class Cat {
  constructor(name) {
    this.name = name;
  }
  speak() {
    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 对象

状态的变更

TIP

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

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

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

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

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

  • catch()

  • finally()

  • all()

  • race()

Generator

async await

  • generator 的语法糖
  • Generator 的改进
    • 内置执行器
    • 更好的语义
    • 更广的适用性
    • 返回值是 promise
  • async 函数的的返回值是 Promise 对象,async 表示该函数内部有异步操作
  • await 命令后可以是 Promise 对象和原始类型的值 (数值,字符串,布尔值,此时等同于同步操作)
  • 如果包装成为一个函数,then 里面表示当遇到 await 是执行 then 然后才执行后面
  • 如何使用 async/await
    • 函数声明
    • 函数表达式
    • 对象的方法
    • class 的方法
    • 箭头函数方法
js
// 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();

而以下代码呢?

js
// 第一个异步
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……

js
// 第一个异步
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

js
// 第一个异步
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();
js
// 以下声明都成立
function* a1() {}
function* a2() {}
function* a3() {}
function* a4() {}

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

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

var func = a()

Reflect

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

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

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

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

// 新的函数式操作方法
Reflect.deleteProperty(obj, 'name');
  • Reflect 对象的方法和 Proxy 对象的方法一一对象,只要在 Proxy 有,Reflect 就会有。
  • 不管 Proxy 怎么修改默认行为,总可以在 Reflect 上获取默认行为
js
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 方法,保证原生行为能够正常执行
  • 易阅读
js
// 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.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target,name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target,prototype)

使用 Proxy 实现观察者模式

观察者模式 (Observer mode)

js
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

js
const sy1 = Symbol(11);

console.log(sy1);

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

Powered by veaba