es6

发布时间 2023-11-27 16:50:39作者: 柯基与佩奇

1. let 和 const

要逐渐放弃 let,在项目中多用 let 和 const,与 let 的区别:
let 有变量提升,有初始化提升,值可变
let 有变量提升,没有初始化提升,值可变
const 有变量提升,没有初始化提升,值不可变,但如果是定义对象,则属性可变

暂时性死区问题说明:其实 let 和 const 是有变量提升的,但是没有初始化提升:

let name = "柯基";

function fn() {
  console.log(name);
  let name = "sunshin_lin";
}
fn(); // Cannot access 'name' before initialization

块级作用域解决问题:

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  });
} // 5 5 5 5 5

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  });
} // 0 1 2 3 4

2. 默认参数

开发中曾遇到过这样的问题,如果参数不传进来,就设置默认参数

function fn(name, age) {
  let name = name || "柯基";
  let age = age || 18;
  console.log(name, age);
}
fn(); // 柯基 18

但是这么写确实不优雅,可以使用 ES6 的默认参数

function fn(name = "柯基", age = 18) {
  console.log(name, age);
}
fn(); // 柯基 18
fn("sunshine", 22); // sunshine 22

3. 扩展运算符

曾经想要拼接多个数组,只能这么做

const arr1 = [1, 2, 4];
const arr2 = [4, 5, 7];
const arr3 = [7, 8, 9];

const arr = arr1.concat(arr2).concat(arr3)[(1, 2, 4, 4, 5, 7, 7, 8, 9)];

现在可以更优雅地进行拼接

const arr1 = [1, 2, 4];
const arr2 = [4, 5, 7];
const arr3 = [7, 8, 9];

const arr = [...arr1, ...arr2, ...arr3][(1, 2, 4, 4, 5, 7, 7, 8, 9)];

4. 剩余参数

一个函数,传入参数的个数是不确定的,这就可以用 ES6 的剩余参数

function fn(name, ...params) {
  console.log(name);
  console.log(params);
}
fn("柯基", 1, 2); // 柯基 [ 1, 2 ]
fn("柯基", 1, 2, 3, 4, 5); // 柯基 [ 1, 2, 3, 4, 5 ]

5. 模板字符串

以前,拼接字符串只能这么做

const name = "柯基";
const age = "22";

console.log(name + "今年" + age + "岁"); // 柯基今年22岁

现在可以这么做,会更优雅

const name = "柯基";
const age = "22";

console.log(`${name}今年${age}岁`); // 柯基今年22岁

6. Object.keys

可以用来获取对象的 key 的集合,进而可以获得对应 key 的 value

const obj = {
  name: "柯基",
  age: 22,
  gender: "男",
};

const keys = Object.keys(obj);
console.log(keys); // [ 'name', 'age', 'gender' ]

7. 箭头函数

以前使用普通函数

function fn() {}

const fn = function () {};

ES6 新加了箭头函数

const fn = () => {};

// 如果只有一个参数,可以省略括号
const fn = (name) => {};

// 如果函数体里只有一句return
const fn = (name) => {
  return 2 * name;
};
// 可简写为
const fn = (name) => 2 * name;
// 如果返回的是对象
const fn = (name) => ({ name: name });

普通函数和箭头函数的区别: 1.箭头函数不可作为构造函数,不能使用 new 2.箭头函数没有的 this 3.箭头函数没有 arguments 对象 4.箭头函数没有原型对象

8. Array.forEach

ES6 新加的数组遍历方法

const eachArr = [1, 2, 3, 4, 5]

// 三个参数:遍历项 索引 数组本身
// 配合箭头函数
eachArr.forEach((item, index, arr) => {
  console.log(item, index, arr)
})
1 0 [ 1, 2, 3, 4, 5 ]
2 1 [ 1, 2, 3, 4, 5 ]
3 2 [ 1, 2, 3, 4, 5 ]
4 3 [ 1, 2, 3, 4, 5 ]
5 4 [ 1, 2, 3, 4, 5 ]

9. Array.map

常用于返回一个处理过后的新数组

const mapArr = [1, 2, 3, 4, 5];

// 三个参数:遍历项 索引 数组本身
// 配合箭头函数,对每一个元素进行翻倍
const mapArr2 = mapArr.map((num, index, arr) => 2 * num);
console.log(mapArr2)[(2, 4, 6, 8, 10)];

10. Array.filter

顾名思义,用来过滤的方法

const filterArr = [1, 2, 3, 4, 5];

// 三个参数:遍历项 索引 数组本身
// 配合箭头函数,返回大于3的集合
const filterArr2 = filterArr.filter((num, index, arr) => num > 3);
console.log(filterArr2)[(4, 5)];

11. Array.some

some,意思就是只有一个是真,那就返回真

const someArr = [false, true, false, true, false];

// 三个参数:遍历项 索引 数组本身
// 配合箭头函数,只要有一个为true,就返回true,一个都true都没有,就返回false
const someArr2 = someArr.some((bol, index, arr) => bol);
console.log(someArr2);
true;

12. Array.every

every 跟 some 是相反的,some 是只有一个就行,every 是要所有为真才返回真

const everyArr = [false, true, false, true, false];

// 三个参数:遍历项 索引 数组本身
// 配合箭头函数,需要所有为true,才返回true,否则返回false
const everyArr2 = everyArr.every((bol, index, arr) => bol);
console.log(everyArr2);

13. Array.reduce

第一个参数 callback 函数: pre 为上次 return 的值,next 为数组的本次遍历的项
第二个参数为初始值,也是第一个 pre

举两个例子:

// 计算 1 + 2 + 3 + 4 + 5
const reduceArr = [1, 2, 3, 4, 5];
const sum = reduceArr.reduce((pre, next) => {
  return pre + next;
}, 0);
console.log(sum); // 15

// 统计元素出现个数
const nameArr = ["柯基", "sunshine_lin", "柯基", "柯基", "科比"];
const totalObj = nameArr.reduce((pre, next) => {
  if (pre[next]) {
    pre[next]++;
  } else {
    pre[next] = 1;
  }
  return pre;
}, {});
console.log(totalObj); // { '柯基': 3, sunshine_lin: 1, '科比': 1 }

14. 对象属性同名简写

以前同名属性需要这么写

const name = "柯基";
const age = "22";

const obj = {
  name: name,
  age: age,
};

console.log(obj); // { name: '柯基', age: '22' }

ES6 新增语法,只需这么写

const name = '柯基'
const age = '22'

// 属性同名可简写
const obj = {
  name
  age
}

console.log(obj) // { name: '柯基', age: '22' }

15. Promise

Promise,中文名为承诺,承诺在哪呢?承诺在,一旦他的状态改变,就不会再改。这里就介绍基本使用,如果想要深入理解如何使用,请看的另一篇文章看了就会,手写 Promise 原理,最通俗易懂的版本!!!
看看基本使用

成功状态

function requestData() {
  // 模拟请求
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("柯基");
    }, 1000);
  });
}

requestData().then(
  (res) => {
    console.log(res); // 一秒钟后输出 '柯基'
  },
  (err) => {
    console.log(err);
  }
);

失败状态

function requestData() {
  // 模拟请求
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("错误");
    }, 1000);
  });
}

requestData().then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err); // 一秒钟后输出 '错误'
  }
);

all 方法

接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
如果所有 Promise 都成功,则返回成功结果数组
如果有一个 Promise 失败,则返回这个失败结果

// 如果全都为成功
function fn(time) {
  return new Promise((resolve, reject) => {
    console.log(88);
    setTimeout(() => {
      resolve(`${time}毫秒后成功!!!`);
    }, time);
  });
}

Promise.all([fn(2000), fn(3000), fn(1000)]).then(
  (res) => {
    // 3秒后输出 [ '2000毫秒后成功!!!', '3000毫秒后成功!!!', '1000毫秒后成功!!!' ]
    console.log(res);
  },
  (err) => {
    console.log(err);
  }
);

// 如果有一个失败
function fn(time, isResolve) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      isResolve
        ? resolve(`${time}毫秒后成功!!!`)
        : reject(`${time}毫秒后失败!!!`);
    }, time);
  });
}

Promise.all([fn(2000, true), fn(3000), fn(1000, true)]).then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err); // 3秒后输出 '3000毫秒后失败!!!'
  }
);

race 方法

接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
哪个 Promise 最快得到结果,就返回那个结果,无论成功失败

function fn(time, isResolve) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      isResolve
        ? resolve(`${time}毫秒后成功!!!`)
        : reject(`${time}毫秒后失败!!!`);
    }, time);
  });
}

Promise.race([fn(2000, true), fn(3000), fn(1000)]).then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err); // 1秒后输出
  }
);

16. class

以前使用构造函数生成对象,这么做

function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function () {
  console.log(this.name);
};

const kobe = new Person("科比");
kobe.sayName(); // 科比

而有了 ES6 的 class 可以这么做

class Person {
  constructor(name) {
    // 构造器
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}

const kobe = new Person("科比");
kobe.sayName(); // 科比

值得一提的是,class 本质也是 function,class 是 function 的语法糖

class Person {}

console.log(typeof Person); // function

除了以上,还需要知道 class 的以下知识点
静态属性和静态方法,使用 static 定义的属性和方法只能 class 用,实例用不了

class Person {
  constructor(name) {
    this.name = name;
  }

  static age = 22;

  static fn() {
    console.log("哈哈");
  }
}
console.log(Person.age); // 22
Person.fn(); // 哈哈

const sunshine_lin = new Person("柯基");
console.log(sunshine_lin.age); // undefined
sunshine_lin.fn(); // fn is not a function

extend 继承

class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Cat extends Animal {
  say() {
    console.log(this.name, this.age);
  }
}

const cat = new Cat("ketty", 5); // 继承了Animal的构造器
cat.say(); // ketty 5

17. 解构赋值

以前想提取对象里的属性需要这么做

const obj = {
  name: "柯基",
  age: 22,
  gender: "男",
};

const name = obj.name;
const age = obj.age;
const gender = obj.gender;
console.log(name, age, gender); // 柯基 22 男

ES6 新增了解构赋值的语法

const obj = {
  name: "柯基",
  age: 22,
  gender: "男",
  doing: {
    morning: "摸鱼",
    afternoon: "摸鱼",
    evening: "sleep",
  },
};

const { name, age, gender } = obj;
console.log(name, age, gender); // 柯基 22 男

// 解构重名
const { name: myname } = obj;
console.log(myname); // 柯基

// 嵌套解构
const {
  doing: { evening },
} = obj;
console.log(evening); // sleep

也可以进行数组的解构

const arr = [1, 2, 3];

const [a, b, c] = arr;
console.log(a, b, c); // 1 2 3

// 默认赋值
const [a, b, c, d = 5] = arr;
console.log(a, b, c, d); // 1 2 3 5

// 乱序解构
const { 1: a, 0: b, 2: c } = arr;
console.log(a, b, c); // 2 1 3

18. find 和 findIndex

find:找到返回被找元素,找不到返回 undefined
findIndex:找到返回被找元素索引,找不到返回-1

const findArr = [
  { name: "科比", no: "24" },
  { name: "罗斯", no: "1" },
  { name: "利拉德", no: "0" },
];

const kobe = findArr.find(({ name }) => name === "科比");
const kobeIndex = findArr.findIndex(({ name }) => name === "科比");
console.log(kobe); // { name: '科比', no: '24' }
console.log(kobeIndex); // 0

19. for of 和 for in

一句话概括:for in 是遍历(object)键名,for of 是遍历(array)键值。

for...in

for...in 循环只遍历可枚举属性(包括它的原型链上的可枚举属性)。像 Array 和 Object 使用内置构造函数所创建的对象都会继承自 Object.prototype 和 String.prototype 的不可枚举属性,例如 String 的 indexOf() 方法或 Object 的 toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。

let obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
  console.log(key);
}
// a
// b
// c

for...of

for...of 语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句

const array1 = ["a", "b", "c"];
for (const val of array1) {
  console.log(val);
}
// a
// b
// c

for of 不可以遍历普通对象,想要遍历对象的属性,可以用 for in 循环, 或内建的 Object.keys()方法

for...of 与 for...in 的区别

无论是 for...in 还是 for...of 语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。
for...in 语句以任意顺序迭代对象的可枚举属性。
for...of 语句遍历可迭代对象定义要迭代的数据。

以下示例显示了与 Array 一起使用时,for...of 循环和 for...in 循环之间的区别。

Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};

let iterable = [3, 5, 7];
iterable.foo = "hello";

for (let i in iterable) {
  console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // 0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7
}

总结:for in 一般用来遍历对象的 key、for of 一般用来遍历数组的 value

20. Set 和 Map

Set

Set 的基本用法

// 可不传数组
const set1 = new Set();
set1.add(1);
set1.add(2);
console.log(set1); // Set(2) { 1, 2 }

// 也可传数组
const set2 = new Set([1, 2, 3]);
// 增加元素 使用 add
set2.add(4);
set2.add("柯基");
console.log(set2); // Set(5) { 1, 2, 3, 4, '柯基' }
// 是否含有某个元素 使用 has
console.log(set2.has(2)); // true
// 查看长度 使用 size
console.log(set2.size); // 5
// 删除元素 使用 delete
set2.delete(2);
console.log(set2); // Set(4) { 1, 3, 4, '柯基' }

Set 的不重复性

// 增加一个已有元素,则增加无效,会被自动去重
const set1 = new Set([1]);
set1.add(1);
console.log(set1); // Set(1) { 1 }

// 传入的数组中有重复项,会自动去重
const set2 = new Set([1, 2, "柯基", 3, 3, "柯基"]);
console.log(set2); // Set(4) { 1, 2, '柯基', 3 }

// Set的不重复性中,要注意引用数据类型和NaN
// 两个对象都是不用的指针,所以没法去重
const set1 = new Set([1, { name: "柯基" }, 2, { name: "柯基" }]);
console.log(set1); // Set(4) { 1, { name: '柯基' }, 2, { name: '柯基' } }

// 如果是两个对象是同一指针,则能去重
const obj = { name: "柯基" };
const set2 = new Set([1, obj, 2, obj]);
console.log(set2); // Set(3) { 1, { name: '柯基' }, 2 }

NaN !== NaN,NaN 是自身不等于自身的,但是在 Set 中他还是会被去重

const set = new Set([1, NaN, 1, NaN]);
console.log(set); // Set(2) { 1, NaN }

利用 Set 的不重复性,可以实现数组去重

const arr = [1, 2, 3, 4, 4, 5, 5, 66, 9, 1];

// Set可利用扩展运算符转为数组哦
const quchongArr = [...new Set(arr)];
console.log(quchongArr); // [1,  2, 3, 4, 5, 66, 9]

Map

Map 对比 object 最大的好处就是,key 不受类型限制

// 定义map
const map1 = new Map();
// 新增键值对 使用 set(key, value)
map1.set(true, 1);
map1.set(1, 2);
map1.set("哈哈", "嘻嘻嘻");
console.log(map1); // Map(3) { true => 1, 1 => 2, '哈哈' => '嘻嘻嘻' }
// 判断map是否含有某个key 使用 has(key)
console.log(map1.has("哈哈")); // true
// 获取map中某个key对应的value 使用 get(key)
console.log(map1.get(true)); // 2
// 删除map中某个键值对 使用 delete(key)
map1.delete("哈哈");
console.log(map1); // Map(2) { true => 1, 1 => 2 }

// 定义map,也可传入键值对数组集合
const map2 = new Map([
  [true, 1],
  [1, 2],
  ["哈哈", "嘻嘻嘻"],
]);
console.log(map2); // Map(3) { true => 1, 1 => 2, '哈哈' => '嘻嘻嘻' }

21. weakmap 和 weakset

weakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。
成员都是数组和类似数组的对象,若调用 add() 方法时传入了非数组和类似数组的对象的参数,就会抛出错误。

const b = [1, 2, [1, 2]];
new WeakSet(b); // Uncaught TypeError: Invalid value used in weak set

成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏。
WeakSet 不可迭代,因此不能被用在 for-of 等循环中。
WeakSet 没有 size 属性。

WeakMap

WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。

只接受对象作为键名(null 除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有 get、set、has、delete