# ES2015 + 常用API

----胖大人本胖     

# 变量

# var和let与const

# 相同点

  • 声明变量

# 区别

  1. var声明的变量存在变量提升(变量被声明前可用);let和const不存在变量提升(变量声明前使用会抛出错误)

    • 示例 1
    console.log(a) // 输出 undefined
    var a = 10;
    /***********以上代码在被执行时完全等于以下代码***********/
    var a;
    console.log(a);
    a = 10;
    
    1
    2
    3
    4
    5
    6
    • 示例二
    console.log(a); // 输出 undefined
    var b = 2;
    if (b === 2) {
      var a = 10;
    }
    /***********以上代码在被执行时完全等于以下代码***********/
    var a,b = 2;
    console.log(a);
    if (b === 2) {
      a = 10;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    • 示例 3
    console.log(a); // 报错 不再往下执行;注掉此行
    fn1();
    function fn1() {
      console.log(a); // 注掉第一行代码后,输入 undefined
      var a = 10;
    }
    /**********以上代码在被执行时完全等于以下代码(注释第一行console.log后)*********/
    fn1();
    function fn1() {
      var a;
      console.log(a); // 注掉第一行代码后,输入 undefined
      a = 10;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    • 示例 4
    <body>
      <script src="./index.js"></script>
      <script src="./index2.js"></script>
    </body>
    
    1
    2
    3
    4
    // index.js
    console.log(a); // 报错
    // index2.js
    var a = 10
    
    1
    2
    3
    4

    总结: 1. var声明的变量的变量提升不是直接提到最顶层,而是提升到当前变量执行作用域的顶层; 2. 除var存在变量提升外,function同样存在变量提升

    ​ 3. var的变量提升在独立的js文件内提升,不会跨文件提升;

  2. var的作用域是它当前执行的上下文;let和const存在块级作用域,即距离变量最近的花括号内

    • 示例 1
    <body>
      <script src="./index.js"></script>
      <script src="./index2.js"></script>
    </body>
    
    1
    2
    3
    4
    // index.js
    {
      var a = 10; // var 换成const/let后报错
    }
    // index2.js
    console.log(a); // 输出10
    
    1
    2
    3
    4
    5
    6
    • 示例 2 是关于函数/if判断/for循环内的作用域,实际效果和第一点示例3一致
  3. var在相同作用域内可以重复声明变量;let和const重复声明会抛出错误

    • 示例
    var a = 10;
    var a = 20;
    console.log(a); // 输出20
    
    /**************** or ******************/
    let a = 10;
    let a = 20;
    console.log(a); // 报错,不会继续执行
    
    1
    2
    3
    4
    5
    6
    7
    8
  4. let与var声明的变量所指向的内存地址可变(表现为定义的变量的可以被修改);const声明的变量所指向的内存地址不可改变(表现为不可以重新赋值)

    • 示例
    var a = 10; // 假设 10的内存地址 102091092091,a指向就是102091092091 下面同理
    let b = 15; // 假设内存地址为 102091092092
    const c = { a: a, b: b }; // 假设内存地址为 102091092093 内部是 拷贝a赋值a 拷贝 b 赋值给b
    const e = [10, b]; // 假设内存地址为 102091092094
    
    a = 20; // 假设 20的内存地址 102091092095,此时a的指向已经改变了,重新指向了102091092095,102091092091(也就是10)已经没有被引用,此时就被垃圾回收了;
    b = 25; // 同上
    // var/let声明的变量可以修改指向的内存地址
    console.log(a, b, c, e); // 输出 20 25 {a: 10, b: 15} [10, 15]
    c.d = 30; // 此时会在c所指向的内存地址中添加内容,而不会扩展
    e.push(c); // 同上
    console.log(c,e); // 输出 {a: 10, b: 15, d: 30} [10, 15, {a: 10, b: 15, d: 30}]
    c = { a: a, c: c, d: c.d }; // 此时相当于新建对象新建内存,但const声明的变量内存指向不允许更改,所以报错不在执行
    e = [10, 15, 20]; // 同上 但前一行报错之后的内容也不会执行
    console.log(c, e); // 同上
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 使用var可能导致的问题

<body>
  <script src="./index.js"></script>
  <script src="./index2.js"></script>
</body>
1
2
3
4
// index.js 假设此文件是一个第三方库
var a = 10;
function fn1() {
  return a + 20;
}
// index2.js 假设此文件是我们自己的业务js
var a = 60;
console.log(fn1()); // 输出 80
1
2
3
4
5
6
7
8

变量覆盖,变量被修改。假设我们在Vue项目没有使用第三方库,仅我们自己的业务js。如果在项目中全部使用var,虽然js写在不同的文件中,但webPack打包后会进行文件整合,假设对于状态的变量都使用了state/status,由于var的变量提升,变量被复写的可能性降大幅提高,对于let也会可能增加变量被修改的可能行,基于此如果引入第三方库对于常用的储存格式化等很基础的语义化单词,被复写修改可能性会更高

# 推荐

基于以上内容,推荐变量使用const > let > var

# 解构赋值

# 数组解构赋值

const list = [1, 2, 3, 4, 8, 9, 10];
const [first,second,,,fifth] = list; // 解构取数组前两个元素和第五个元素
console.log(first, second, fifth); // 1 2 8
1
2
3

# 对象的解构赋值

const obj = {name: '张三', sex: 1, sexDecs: '男', age: 12 };
const { name, sexDecs } = obj;
console.log(name, sexDecs); // 张三 男
1
2
3

# 函数的结构赋值

function fn1 ({userName, userId }) {
  console.log(userName, userId);
}
const user = {userName: '杜少陵', userId: 'dsl'};
fn1(user);
1
2
3
4
5

# 深层次混合嵌套的结构赋值

const user = {
  name: '杜甫',
  anotherName: '杜少陵',
  birthplace: '河南巩县',
  friends: [
    {
      name: '李白',
      dynasty: '唐',
      getOtherInfo: function() {
        return {
          father: '未知',
          mother: '不知',
        }
      },
    },
    {
      {
      name: '元稹',
      dynasty: '唐',
      getOtherInfo: function() {
        return {
          father: '元宽',
          mother: '郑氏',
        }
      },
    }
  ],
  getRelativesInfo: function() {
    return {
      grandfather: '杜审言',
      father: '杜闲',
      mother: '卢氏',
      wife: '司农少卿杨怡之女',
      child: ['宗文', '宗武', '凤儿'],
    }
  }
}
const { name, anotherName, birthplace, getRelativesInfo, friends: [, {dynasty, getOtherInfo}] } = user;
console.log(name, anotherName, birthplace, getRelativesInfo, dynasty, getOtherInfo); // 杜甫 杜少陵 河南巩县 输出函数 唐 输出函数
const {grandfather, father, mother, wife, child: [,,childrenName]} = getRelativesInfo();
console.log(grandfather, father, mother, wife, childrenName); // 杜审言 杜闲 卢氏 司农少卿杨怡之女 凤儿
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

# 解构赋值后重命名

const name = '李白';
const user = {name: '白居易', job: '诗人'};
// 对user进行解构赋值取name,会造成重复声明,此时可以解构后重新指定变量名
const {name: baiName} = user;
console.log(baiName); // 白居易
1
2
3
4
5

# 解构赋值的默认值

有时候进行解构赋值的时候我们并不知道我们所要解构的对象中是否有我们所要的对象,此时就可以指定默认值

  • 对象的默认值
const ChiZhiYang = { name: '柴止痒', age: 26 };
const {name = '未命名', gender = '未知', age = 0, bodyWeight = '0KG', other: {formerResidence} = {formerResidence: '中国'}} = ChiZhiYang;
console.log(name, gender, age, bodyWeight, formerResidence); // 柴止痒 未知 26 0KG 中国
1
2
3
  • 数组的默认值
const programmer = [];
const [firstPeople = '第一个程序猿', secondPeople = '第二个程序猿'] = programmer;
console.log(firstPeople, secondPeople); // 第一个程序猿 第二个程序猿
1
2
3

# 字符串

# 模版字符串

  • ES6新增内容

  • 模版字符串保持字符串格式不变

    • 普通字符串
    const str = '渡荆门送别\n李白\n渡远荆门外,来从楚国游。\n山随平野尽,江入大荒流。\n月下飞天镜,云生结海楼。\n仍怜故乡水,万里送行舟。';
    console.log(str); // 输出见下图
    
    1
    2

    image-20191101150247150

    • 模版字符串
    // 普通字符串无法换行定义
    const str = `
          渡荆门送别
                  李白
    
    渡远荆门外,来从楚国游。
    山随平野尽,江入大荒流。
    月下飞天镜,云生结海楼。
    仍怜故乡水,万里送行舟。
    `;
    console.log(str); // 输出见下图
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    image-20191101150401268

  • 模版字符串中可以使用变量

    关于reduceAPI会在之后讲解

    • 普通字符串模版拼接
    const prace = {title: '渡荆门送别', autor: '李白', content: ['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。']}
    
    const render = function (accumulator, currentValue, index) {
      return index === 1 ? '<p>' + accumulator + '</p>' + '<p>' + currentValue + '</p>' : accumulator + '<p>' + currentValue + '</p>';
    }
    
    const nodes = '<article><h3>' + prace.title + '</h3><address>' + prace.autor + '</address>' + prace.content.reduce(render) + '</article>';
    
    document.write(nodes); // 输出看下图
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    image-20191101160644004

    • 在模版字符串中使用变量拼接
    const prace = {title: '渡荆门送别', autor: '李白', content: ['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。']}
    
    const render = function (accumulator, currentValue, index) {
      return index === 1 ? `<p>${accumulator}</p><p>${currentValue}</p>` : `${accumulator} <p>${currentValue}</p>`;
    }
    
    const nodes = `
    <article>
    	<h3>${prace.title}</h3>
    	<address>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;${prace.autor}</address>
    	${prace.content.reduce(render)}
    </article>
    `;
    
    document.write(nodes); // 输出看下图
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    ![image-20191101161453357](/Users/mac/Library/Application Support/typora-user-images/image-20191101161453357.png)

# .includes

  • ES6新增API

  • 判断一个字符串是否包含在另一个字符串中,返回truefalse

  • 语法str.includes(searchString[, position])

    • searchString 要搜索的字符串
    • position 从指定位置开始检索
    const str = `
          渡荆门送别
                  李白
    
    渡远荆门外,来从楚国游。
    山随平野尽,江入大荒流。
    月下飞天镜,云生结海楼。
    仍怜故乡水,万里送行舟。
    `;
    console.log(str.includes('李白')); // true
    console.log(str.includes('李白', 40)); // false 从40开始搜索,40之前的字符串会被忽略 检索不到
    // 注意字符串的定义是使用模版字符串,顾下标为0的元素是空格而不是”渡“
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# .startsWith

  • ES6新增API

  • 判断一个字符串是否在另一个字符串的开始位置(准确说:判断当前字符串是否以一个给定的字符串开始的),返回truefalse

  • 语法str.startsWith(searchString[, position])

    • searchString 要搜索的字符串
    • position 从指定位置开始检索
    const str = '渡荆门送别李白渡远荆门外,来从楚国游。山随平野尽,江入大荒流。月下飞天镜,云生结海楼。仍怜故乡水,万里送行舟。';
    console.log(str.startsWith('渡荆门送别')); // true
    console.log(str.startsWith('渡荆门送别', 3)); // false
    
    1
    2
    3

# .endsWith

  • ES6新增API

  • 判断一个字符串是否在另一个字符串的结束位置(准确说:判断当前字符串是否以一个给定的字符串结束的),返回truefalse

  • 语法str.endsWith(searchString[, position])

    • searchString 要搜索的字符串
    • position 从指定位置开始检索 默认为str.length
    const str = '渡荆门送别李白渡远荆门外,来从楚国游。山随平野尽,江入大荒流。月下飞天镜,云生结海楼。仍怜故乡水,万里送行舟。';
    console.log(str.endsWith('万里送行舟。')); // true
    console.log(str.endsWith('万里送行舟。', 54)); // false
    
    1
    2
    3

# .padStart

  • ES8新增API

  • 用一个字符串填充当前字符串到指定长度。从字符串左侧开始填充

  • 适用场景: 月份补0,倒计时时分秒补0

  • 语法str.padStart(targetLength [, padString])

    • targetLength 目标长度。如果目标长度小于当前字符串长度,会返回字符串本身
    • padString 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "
    const date = new Date();
    const minutes = date.getMinutes();
    
    1
    2

console.log(${minutes}.padStart(2, '0')); // 如果你获取到的分钟数是一位,那么你此时可以看到会在开始位置补0



### .padEnd

* ES8新增API

* 用一个字符串填充当前字符串到指定长度。从字符串右侧开始填充

* 语法`str.padEnd(targetLength [, padString])`

* **targetLength** 目标长度。如果目标长度小于当前字符串长度,会返回字符串本身
* **padString** 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断。此参数的缺省值为 " "

```js
const str = '渡远荆门外,';
console.log(str.padEnd(18, '来从楚国游。')); // 渡远荆门外,来从楚国游。来从楚国游。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# .trim

  • 删除字符串两端的空白;空白字符是所有的空白字符 (space, tab, no-break space 等) 以及所有行终止符字符(如 LF,CR等)
  • 适用场景: input表单验证表单提交
  • 语法str.trim()
<template>
	<div class="container">
    <input type="text" v-model="value" />
    <button @click="handleSubmit">
      提交
  	</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      value: '',
    };
  },
  methods: {
    handleSubmit() {
      const value = this.value.trim(); // 去除字符串两端空白
      // ........ 业务逻辑
    }
  },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# .trimStart

  • ES10 新增API
  • 去除字符串开始位置(左侧)空白
  • 语法str.trimStart()
const str =  `
渡远荆门外

		`;
console.log(str.trimStart());
1
2
3
4
5

# .trimEnd

  • ES10 新增API
  • 去除字符串结束位置(右侧)空白
  • 语法str.trimEnd()
const str =  `
渡远荆门外

		`;
console.log(str.trimEnd());
1
2
3
4
5

# .repeat

  • ES6 新增API
  • 将一个字符串重复N次,并返回一个新的字符串
  • 语法const resultString = str.repeat(count);
    • count要重复的次数 Number类型,不能为负数,如果传入的是String会先转换成Number,如果是小数会向下取整
const str = '渡远荆门外';
console.log(str.repeat(2)); // 渡远荆门外渡远荆门外
1
2

# .split

  • 根据指定字符将字符串分割成数组
  • 语法str.split([separator[, limit]])
    • separator指定分割的字符,可以为正则表达式 可选 如果不传返回 [str]
    • limit 限制分割数组长度 如果长度超过本身可分割后的长度或为负数 都会返回本身可分割的数组
const str = ' 渡 远 荆 门 外 ';
console.log(str.split()); // [" 渡 远 荆 门 外 "]
console.log(str.split('')); // (11) [" ", "渡", " ", "远", " ", "荆", " ", "门", " ", "外", " "]
console.log(str.split('', -1)); // (11) [" ", "渡", " ", "远", " ", "荆", " ", "门", " ", "外", " "]
console.log(str.split('', 3)); // [" ", "渡", " "]
console.log(str.split('', 20)); // (11) [" ", "渡", " ", "远", " ", "荆", " ", "门", " ", "外", " "]
1
2
3
4
5
6

# 箭头函数

# 定义

// 无参数定义
const fn = () => {
  return '渡远荆门外';
};
console.log(fn()); // 渡远荆门外

// 一个参数定义
const fn1 = str => {
  return str;
};
console.log(fn1('渡远荆门外')); // 渡远荆门外

// 多个参数
const fn2 = (str1, str2, str3) => {
  return `${str1}
${str2}
${str3}`;
}
console.log(fn2('渡远荆门外','来从楚国游','山随平野尽'));
// 渡远荆门外
// 来从楚国游
// 山随平野尽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 函数内部无逻辑,直接返回的,省略大括号

const fn = () => '渡远荆门外'; // 表示直接返回内容
console.log(fn()); // 渡远荆门外

const fn1 = str => str;
console.log(fn1('渡远荆门外')); // 渡远荆门外

const fn3 = str => console.log(str); // 表示 直接打印参数
fn3('山随平野尽'); // 山随平野尽

const fn4 = () => fn1('月涌大荒流'); // 表示 返回fn1的执行结果
fn4(); // 月涌大荒流

const fn2 = (str1, str2, str3) => `${str1}
${str2}
${str3}`;
console.log(fn2('渡远荆门外','来从楚国游','山随平野尽'));
// 渡远荆门外
// 来从楚国游
// 山随平野尽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 关于this指向

  • funtion 定义函数时this指向是运行时决定的;箭头函数的指向时编译时决定的
<template>
	<div class="container">
    <button @click="handleClick">
      {{btnText}}
  	</button>
  </div>
</template>
<script>
import { MessageBox } from 'element-ui';
export default {
  data() {
    return {
      btnText: '提交',
    };
  },
  methods: {
    handleClick() {
      MessageBox({
        title: '弹窗标题',
        message: '弹窗消息祝内容',
        type: 'success',
        callback: (action, instance) => {
          this.btnText = '提交中'; // 箭头函数的this是继承下来的指向的是组件实例
        }
      })
    },
    handleClickFn() {
      const self = this;
    	MessageBox({
        title: '弹窗标题',
        message: '弹窗消息祝内容',
        type: 'success',
        callback: function(action, instance) {
          self.btnText = '提交中'; // 如果使用的是function定义函数,必须提前定义this
        }
      })
  	}
  },
}
</script>
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

# 箭头函数的解构

const params = {
  name: '李白',
  isDie: true,
}
const getName = params => params;
const friends = [params, '小白', getName];

const fn = ([{name, isDie},,fn],fn2) => {
  console.log(name,fn(name), fn2(isDie))
}
fn(friends,getName); // 李白 李白 true
1
2
3
4
5
6
7
8
9
10
11

# 对象属性是函数

const people = {
  name: '李白',
  getAge() {
    console.log(this.name); // 李白
    return 18;
  }
}
// 这里getAge用了简写 实质是function定义的,并不是箭头函数;在这里使用this指向的是对这个对象,如果用的是箭头函数定义的,指向取决于对象所处环境,this指向就是上下文

// 上面代码完全等于以下代码
const people = {
  name: '李白',
  getAge: function () {
    console.log(this.name); // 李白
    return 18;
  }
}

// 如果是用箭头函数定义的
const people = {
  name: '李白',
  getAge: () => {
    console.log(this.name); // 这里的this就是看你在哪里调用的 在全局就指向window。
    return 18;
  }
}
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

# 数组

# isArray

  • 检测一个值是否为Array 返回true/false
  • 适用场景: 判断一个值是数组且长度存在
  • 语法Array.isArray(obj)
    • obj 需要检测的值。
<template>
	<!-- 当list为数组且长度存在的时候在进行渲染 -->
	<div class="container" v-if="Array.isArray(list) && list.length">
    <p v-for="list" :key="item">
      {{item}}
  	</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: ['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。'],
    };
  },
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# .find

  • ES6 新增API

  • 从数组中查找第一个符合匹配条件的元素;查找不到返回undefined

  • 实用程度:

  • 适用场景: 从数组中找元素

  • 语法arr.find(callback[, thisArg])

    • callback在数组每一项上执行的函数,接收 3 个参数

      • element 当前遍历到的元素
      • index 当前遍历到的元素下标/索引
      • array 数组本身
    • thisArg 执行回调用做this的对象

<template>
	<!-- element-ui的选择器组件 -->
	<el-select v-model="value" placeholder="请选择" @change="handleChange">
    <el-option
      v-for="item of hospitalList"
      :key="item.id"
      :label="item.hospitalName"
      :value="item.id">
    </el-option>
  </el-select>
</template>
<script>
import { Select, Option } from 'element-ui';
export default {
  components: {
    ElSelect: Select,
    ElOption: Option,
  }
  data() {
    return {
      value: undefined,
      hospitalList: [
        {
          id: 203,
          hospitalName: '洛阳市中心医院',
          organCode: '661',
          provinceCode: '108'
          cityCode: '407'
          areaCode: '362'
        },
        {
          id: 211,
          hospitalName: '北京和协医院',
          organCode: '602',
          provinceCode: '101'
          cityCode: '236'
          areaCode: '675'
        },
      ],
      form: {
        id: undefined,
        hospitalName: undefined,
        organCode: undefined,
      }
    };
  },
  methods: {
    handleChange(value) {
      // 组件v-model绑定的只是Option的:value,也就是说我们只能拿到医院信息的ID,假设后端需要的数据是“医院ID,医院名,机构Code”,此时最适合find来处理
      
      // try/catch会在之后讲解
      // 配合解构赋值
      try {
        const {id, hospitalName, organCode} = this.hospitalList.find(item => item.id === value);
        this.form.id = id, this.form.hospitalName = hospitalName, this.organCode = organCode;
        // 这个场景非常适合使用find方法,基本一行代码可以搞定。如果不使用find也许你会字符串拼接到value,再截取,也许会用forEach/for循环便利查找等各种其他操作,但都没有find更方便
        // 这里用了try/catch当这里代码执行出错了,会被catch掉 不会阻塞代码运行
      } catch(err) {
        throw err;
      }
    }
  }
}
</script>
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# .findIndex

  • ES6 新增API

  • 从数组中查找第一个符合匹配条件的元素的索引;查找不到返回 -1

  • 实用程度:

  • 适用场景: 从数组中找元素索引(更多场景项目中实际发掘吧)

  • findAPI相比:find返回的是符合条件的元素,findIndex返回的是符合条件元素的索引

  • 语法arr.findIndex(callback[, thisArg])

    • callback在数组每一项上执行的函数,接收 3 个参数

      • element 当前遍历到的元素
      • index 当前遍历到的元素下标/索引
      • array 数组本身
    • thisArg 执行回调用做this的对象

const hospitalList = [
  {
    id: 203,
    hospitalName: '洛阳市中心医院',
    organCode: '661',
    provinceCode: '108'
    cityCode: '407'
    areaCode: '362'
  },
  {
    id: 211,
    hospitalName: '北京和协医院',
    organCode: '602',
    provinceCode: '101'
    cityCode: '236'
    areaCode: '675'
  },
];
const idIndex = hospitalList.findIndex(item => item.id === 201); // -1
const organCodeIndex = hospitalList.findIndex(item => item.organCode === '601'); // 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# .includes

  • ES7 新增API

  • 检测数组是否包含指定的元素,返回true/false

  • 实用程度:

  • 适用场景: 订单列表符合条件的订单状态才显示操作按钮

  • 语法arr.includes(valueToFind[, fromIndex])

    • valueToFind要查找的元素值

      includes 对大小写敏感

    • fromIndex 从指定索引处开始查找;默认为0;若为负值,则取绝对值从后往前(从右往左)开始查找

['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].includes('d'); // true
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].includes('d', 2); // true 从索引为2开始查找
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].includes('d', 3); // false 从索引为3开始查找,查找不到false
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].includes('d', -3); // true 从右往左倒数索引为-3开始查找(从‘h’开始往左查)
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].includes('d', 50); // false 从索引50开始查找,数组长度不够,根本找不到 false
1
2
3
4
5
<template>
	<!-- element-ui的选择器组件 -->
	<div class="container">
    <div class="order" v-for="item of orderList" :key="item.id">
      <!-- 在这里 -->
      <button v-if="[3,6,8].includes(item.status)">
        分配
  		</button>
  	</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      orderList: [
        {
          id: 101909286936,
          status: 5,
        },
        {
          id: 101909286976,
          status: 6,
        },
        {
          id: 101939286936,
          status: 3,
        },
        {
          id: 101909286536,
          status: 7,
        },
      ],
    };
  }
}
</script>
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

# .flat

  • ES10 正式新增
  • 将多维数组进行展开成一维数组
  • 语法const newArray = arr.flat([depth])
    • depth要拉平的数组深度;默认为1
const list = [{name: '李白'}, [{name: '李白'}, {name: '杜甫'}, [str => str, ['唐', '宋', '元', '明', '清']]]];
list.flat(); // [{name: '李白'}, {name: '李白'}, {name: '杜甫'}, [str => str, ['唐', '宋', '元', '明', '清']]];
list.flat(2); // [{name: '李白'}, {name: '李白'}, {name: '杜甫'}, str => str, ['唐', '宋', '元', '明', '清']];

// 如果超过数组本身的深度,那就能拉几层拉几层
list.flat(10); // [{name: '李白'}, {name: '李白'}, {name: '杜甫'}, str => str, '唐', '宋', '元', '明', '清'];

// 如果是负数,就返回数组本身
list.flat(-2); // [{name: '李白'}, [{name: '李白'}, {name: '杜甫'}, [str => str, ['唐', '宋', '元', '明', '清']]]];
1
2
3
4
5
6
7
8
9

# .sort

  • 对数组进行排序(官方表述:sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的)

  • 会修改原数组

  • 语法:arr.sort([compareFunction])

    • compareFunction 可选。用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。

      • firstEl 第一个用于比较的元素
      • secondEl 第二个用于比较的元素
    • 返回值: 排序后的数组。请注意,数组已原地排序,并且不进行复制。

如果没有指明 compareFunction ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。例如 "Banana" 会被排列到 "cherry" 之前。当数字按由小到大排序时,9 出现在 80 之前,但因为(没有指明 compareFunction),比较的数字会先被转换为字符串,所以在Unicode顺序上 "80" 要比 "9" 要靠前。

如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:

  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;

  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本);

  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。

  • compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

const hiStory = ['秦','a','1','汉','b',2,'隋','c','3','唐','d',4,'宋',5,'e','元', '6','f','明','g','7',8,'h','清'];
hiStory.sort(); // ["1", 2, "3", 4, 5, "6", "7", 8, "a", "b", "c", "d", "e", "f", "g", "h", "元", "唐", "宋", "明", "汉", "清", "秦", "隋"]
1
2
const orderList = [
  {createTime: 1572944297747, id: 1},
  {createTime: 1572944290747, id: 2},
  {createTime: 1572944390747, id: 3},
  {createTime: 1572944290747, id: 4},
  {createTime: 1572944290147, id: 5},
  {createTime: 1575944290747, id: 6},
  {createTime: 1572943290747, id: 7},
]
orderList.sort((first,second) => first.createTime - second.createTime) // 正序
orderList.sort((first,second) => second.createTime - first.createTime) // 倒序
// 正序
orderList.sort((first,second) => {
  if(first.createTime < second.createTime) {
     return -1; // a小于b,a排在b之前 返回负数 a排b之前
  }
  if(first.createTime > second.createTime) {
     return 1; // a大于b,b应该排在a之前 返回正数 b在a之前
  }
  return 0; // 相等,位置不变 返回0
})
// 倒序
// ............不写了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# .concat

  • 将多个数组合并为一个
  • 此API是对数组的深拷贝
  • 语法const new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
    • valueN 将数组和/或值连接成新数组。如果省略了valueN参数参数,则concat会返回一个它所调用的已存在的数组的浅拷贝
const list1 = [{name: '李白'}],list2 = ['陶潜'],other = '窝窝头', school = () => '豫章书院';
const newList = list1.concat(list2,other,school); // [{name: '李白'}, '陶潜', '窝窝头', () => '豫章书院'];
1
2

# .filter

  • 数组的过滤器;返回符合条件的所有元素

  • 实用程度:

  • 适用场景: 条件查询

  • 语法const newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

    • callback 用来测试数组的每个元素的函数

      • element 数组中当前正在处理的元素。
      • index 可选。正在处理的元素在数组中的索引。
      • array 可选。调用了 filter 的数组本身。
    • thisArg 执行 callback 时,用于 this 的值。

const orderList = [
  {id: 102, content: '数组1', status: 3},
  {id: 103, content: '数组2', status: 4},
  {id: 104, content: '数组3', status: 3},
  {id: 105, content: '数组4', status: 6},
  {id: 106, content: '数组5', status: 2},
  {id: 107, content: '数组6', status: 4},
  {id: 108, content: '数组7', status: 7},
  {id: 109, content: '数组8', status: 3},
  {id: 110, content: '数组9', status: 8},
  {id: 111, content: '数组10', status: 6},
]
const newList = orderList.filter(element => element.status === 3 || element.status === 6); // 查询订单状态是3或者是6的订单

// 输出结果自行CV大法到浏览器终端查看吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# .map和.forEach

  • map()forEach()都是ES5就具有的方法

  • map()forEach()都可以实现对数组的遍历

  • map()会对数组进行深拷贝(官方描述:map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果),forEach()对数组的每一个元素执行一次提供的函数,会修改原数组,无返回值

  • 实用程度:

  • 语法:

    • const new_array = arr.map(callback(currentValue[, index[, array]])[, thisArg])

    • const new_array = arr.forEach(callback(currentValue[, index[, array]])[, thisArg])

      • callback 针对数组中的每个元素, 都会执行该回调函数, 执行时会自动传入下面三个参数:

        • element 数组中当前正在处理的元素。
        • index 可选。正在处理的元素在数组中的索引。
        • array 可选。调用了 此API 的数组本身。
      • thisArg 执行 callback 时,用于 this 的值。

    mapforEach语法一样,没有写错

    注意: 如果使用箭头函数表达式来传入函数参数,thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。

const orderList = [{"contact":"李贺","price":4,"orderStatus":4,"logisticsState":260},{"contact":"袁天罡","price":23.7,"orderStatus":5,"logisticsState":220},{"contact":"袁天罡","price":219.5,"orderStatus":4,"logisticsState":220},{"contact":"苏轼","price":59,"orderStatus":4,"logisticsState":220},{"contact":"秦始皇","price":175.6,"orderStatus":2,"logisticsState":110},{"contact":"孟浩然","price":127.62,"orderStatus":3,"logisticsState":260},{"contact":"苏轼","price":108,"orderStatus":2,"logisticsState":250},{"contact":"李商隐","price":230.8,"orderStatus":4,"logisticsState":220},{"contact":"孟浩然","price":111.3,"orderStatus":4,"logisticsState":110},{"contact":"秦始皇","price":34.8,"orderStatus":3,"logisticsState":260}];

// orderStatus 1 未付款 2 待发货 3 运输中 4 已收货 5 已评价
// logisticsState 110 已接单 220 运输中 140 已揽收 260 配送中 250 已签收
const newOrderList = orderList.map(item => {
  item.priceDesc = `${item.price}`;
  if(item.orderStatus === 1) {
    item.orderStatusDesc = '未付款';
  } else if(item.orderStatus === 2) {
    item.orderStatusDesc = '待发货';
  } else if(item.orderStatus === 3) {
    item.orderStatusDesc = '运输中';
  } else if(item.orderStatus === 4) {
    item.orderStatusDesc = '已收货';
  } else {
    item.orderStatusDesc = '已评价';
  }
  if(item.logisticsState === 110) {
    item.logisticsStateDesc = '已接单';
  } else if(item.logisticsState === 220) {
    item.logisticsStateDesc = '运输中';
  } else if(item.logisticsState === 140) {
    item.logisticsStateDesc = '已揽收';
  } else if(item.logisticsState === 260) {
    item.logisticsStateDesc = '配送中';
  } else {
    item.logisticsStateDesc = '已签收';
  }
  return item;
});

console.log(newOrderList,orderList); // 自行运行看打印结果,不粘结果了;  newOrderList是修改过后的新数组 orderList是原始数组,内容没有改变

orderList.forEach(item => {
  item.priceDesc = `${item.price}`;
  if(item.orderStatus === 1) {
    item.orderStatusDesc = '未付款';
  } else if(item.orderStatus === 2) {
    item.orderStatusDesc = '待发货';
  } else if(item.orderStatus === 3) {
    item.orderStatusDesc = '运输中';
  } else if(item.orderStatus === 4) {
    item.orderStatusDesc = '已收货';
  } else {
    item.orderStatusDesc = '已评价';
  }
  if(item.logisticsState === 110) {
    item.logisticsStateDesc = '已接单';
  } else if(item.logisticsState === 220) {
    item.logisticsStateDesc = '运输中';
  } else if(item.logisticsState === 140) {
    item.logisticsStateDesc = '已揽收';
  } else if(item.logisticsState === 260) {
    item.logisticsStateDesc = '配送中';
  } else {
    item.logisticsStateDesc = '已签收';
  }
});
console.log(orderList); // 我们同样得到了我们想要的结果,但原始数组已经被改变了
var orderList = Mock.mock({
  'orderList|10-15': [
      {
        'contact|1': ['李白', '杜甫', '李商隐','陶渊明','李贺','孟浩然','苏轼','怀素','袁天罡','李淳风','秦始皇','李煜'],
		'price|1-230.0-2': 1,
		'orderStatus|1-5': 1,
     'logisticsState|1': [110,220,140,260,250],
      }
  ]
})
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

这段代码里的将状态码翻译成文字写的非常非常烂,之后我们会使用Map对象结合扩展运算符进行优化。

# .indexOf和.findIndex

  • indexOf是ES5的API,findIndex是ES6的API

  • indexOffindIndex 都是从数组中找元素索引

  • indexOf 在数组中查找**给定元素(值)的第一个索引,并返回,找不到返回 -1;findIndex 在数组中查找满足条件(函数)**的第一个元素索引,并返回。否则返回 -1;

  • 实用程度:

  • 语法:

    • arr.indexOf(searchElement[, fromIndex])

      • searchElement 要查找的元素
      • fromIndex 可选 开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-3表示从倒数第三个元素开始查找(从左往右), 如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.
    • const index = arr.findIndex(callback(currentValue[, index[, array]])[, thisArg])

      • callback 用来测试数组的每个元素的函数
        • currentValue 当前元素
        • index 当前元素的索引
        • array 执行findIndex API的数组本身
['a','s','d','f','g','h'].indexOf('h',-2); // 5 从倒数第二个元素处往右开始查找
['a','s','d','f','g','h'].indexOf('f',-2); // -5 从倒数第二个元素处往右开始查找,找不到

const orderList = [{"contact":"李贺","price":4,"orderStatus":4,"logisticsState":260},{"contact":"袁天罡","price":23.7,"orderStatus":5,"logisticsState":220},{"contact":"袁天罡","price":219.5,"orderStatus":4,"logisticsState":220},{"contact":"苏轼","price":59,"orderStatus":4,"logisticsState":220},{"contact":"秦始皇","price":175.6,"orderStatus":2,"logisticsState":110},{"contact":"孟浩然","price":127.62,"orderStatus":3,"logisticsState":260},{"contact":"苏轼","price":108,"orderStatus":2,"logisticsState":250},{"contact":"李商隐","price":230.8,"orderStatus":4,"logisticsState":220},{"contact":"孟浩然","price":111.3,"orderStatus":4,"logisticsState":110},{"contact":"秦始皇","price":34.8,"orderStatus":3,"logisticsState":260}];
orderList.orderList.findIndex(item => item.orderStatus === 2); // 4
1
2
3
4
5

# .lastIndexOf

  • 返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找(从右向左),从 fromIndex 处开始。
  • 语法:arr.lastIndexOf(searchElement[, fromIndex])
    • searchElement 要查找的元素
    • fromIndex 可选 从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

lastIndexOfindexOfAPI是两个作用相同结果相反的API;相同点都是从数组中查找给定元素的索引,不同点是indexOf是从左往右查,lastIndexOf是从右往左查,indexOf 是查找元素在数组的第一个索引(同一元素,在数组中有多处,返回第一处索引),lastIndexOf 是查找元素在数组的最后一个索引(同一元素,在数组中有多处,返回最后一处索引)

代码和indexOf API相反,就不写了

# .join

  • 这个API在js的第一个版本就有

  • join 根据指定分隔符(默认分隔符为英文逗号)将数组所有元素拼成字符串返回。如果数组只有一个项目,那么将返回该项目而不使用分隔符

  • 实用程度:

  • 语法:arr.join([separator])

    • separator 可选 指定一个字符串来分隔数组的每个元素,默认为英文逗号
['a','f','d','f','g','h'].join('<-->'); // "a<-->f<-->d<-->f<-->g<-->h"

const orderList = [{"contact":"李贺","price":4,"orderStatus":4,"logisticsState":260},{"contact":"袁天罡","price":23.7,"orderStatus":5,"logisticsState":220},{"contact":"袁天罡","price":219.5,"orderStatus":4,"logisticsState":220},{"contact":"苏轼","price":59,"orderStatus":4,"logisticsState":220},{"contact":"秦始皇","price":175.6,"orderStatus":2,"logisticsState":110},{"contact":"孟浩然","price":127.62,"orderStatus":3,"logisticsState":260},{"contact":"苏轼","price":108,"orderStatus":2,"logisticsState":250},{"contact":"李商隐","price":230.8,"orderStatus":4,"logisticsState":220},{"contact":"孟浩然","price":111.3,"orderStatus":4,"logisticsState":110},{"contact":"秦始皇","price":34.8,"orderStatus":3,"logisticsState":260}];
orderList.orderList.join('<----->'); // "[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]<----->[object Object]" 如果是array<Object> 转字符串不会把对象成字符串后再拼接
1
2
3
4

# .reduce和.reduceRight

  • reducereduceRight 都是ES5就有的方法

  • 都是累加器;reduce 从左往右执行,reduceRight 从右往左执行

  • 适用场景 还记得讲模版字符串中可以使用变量的那个例子吗

  • 语法:

    • arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

    • arr.reduceRight(callback(accumulator, currentValue[, index[, array]])[, initialValue])

      • callback 一个回调函数,用来操作数组中的每个元素,可接受四个参数

        • accumulator 上一次调用回调的返回值,或提供的 initialValue。如果没有提供initialValue , 第一次函数执行时accumulator就是数组的第0个元素,currentValue是数组的第一个元素
        • currentValue 当前被处理的元素
        • index 可选 数组中当前被处理的元素的索引
        • array 可选 调用 reducet() 的数组
      • initialValue 可选 值用作回调的第一次调用的累加器。如果未提供初始值,则将使用并跳过数组中的最后一个元素。在没有初始值的空数组上调用reduce或reduceRight就会创建一个TypeError。

// 模版字符串中使用变量的例子是这么写的
const prace = {title: '渡荆门送别', autor: '李白', content: ['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。']}

const render = function (accumulator, currentValue, index) {
  return index === 1 ? '<p>' + accumulator + '</p>' + '<p>' + currentValue + '</p>' : accumulator + '<p>' + currentValue + '</p>';
}

const nodes = '<article><h3>' + prace.title + '</h3><address>' + prace.autor + '</address>' + prace.content.reduce(render) + '</article>';

// 可以看到上面例子由于我们没有提供 initialValue ,所以我们返回的时候做了判断
// 现在我们对上面例子做个简化和优化
['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。'].reduce((count,item) => {
	return count + `<span>${item}</span>`
}, '')
// 输出 <span>渡远荆门外,来从楚国游。</span><span>山随平野尽,江入大荒流。</span><span>月下飞天镜,云生结海楼。</span><span>仍怜故乡水,万里送行舟。</span>
// 我们提供了初始累计是空字符串,然后就可以省略掉判断了

// 使用reduceRight
['渡远荆门外,来从楚国游。', '山随平野尽,江入大荒流。', '月下飞天镜,云生结海楼。', '仍怜故乡水,万里送行舟。'].reduceRight((count,item) => {
	return count + `<span>${item}</span>`
},'')
// 输出 <span>仍怜故乡水,万里送行舟。</span><span>月下飞天镜,云生结海楼。</span><span>山随平野尽,江入大荒流。</span><span>渡远荆门外,来从楚国游。</span>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# .shift和.pop

  • shiftpop 都是用来从数组中删除元素

  • shiftpop 都会改变原数组

  • shift删除数组的第一个元素,并返回该元素的值;pop删除数组的最后一个元素,并返回该元素的值

  • 语法

    • arr.shift()

    • arr.pop()

      • 当数组为空时返回undefined

这两个API很简单,也是较为常用的API,代码省略

# .unshift和.push

  • unshiftpush都是用来向数组添加元素的
  • unshiftpush都会返回数组的新长度
  • unshiftpush都会改变原数组
  • unshift向数组的开头添加元素,push向数组的结尾添加元素
  • 语法
    • arr.unshift(element1, ..., elementN)
    • arr.push(element1, ..., elementN)
      • elementN 被添加到数组开头或结尾的元素
const list = [];
let length = list.unshift({name: '李白'}, str =>str, ['柴志阳']);
console.log(length,list) // 3 [{name: '李白'},str =>str, ['柴志阳']]

// push
length = list.push('a', 's', 909);
console.log(length,list) // 6 [{name: '李白'},str =>str, ['柴志阳'],'a', 's', 909]
1
2
3
4
5
6
7

这两个API也很简单,也很好理解,可以多用用,多感受下

# .some和.every

  • someevery 都是ES5就有的API

  • someevery 都是用来测试数组元素是否可以通过测试条件(我们提供的一个测试函数),通过就返回 true ,否则返回 false ;不同的是数组中只要有一个元素通过测试some就会返回 true ,而every 需要所有元素都通过测试才会返回true

  • 语法

    • arr.some(callback(element[, index[, array]])[, thisArg])

    • arr.every(callback(element[, index[, array]])[, thisArg])

      • callback 用来测试每个元素的函数,它可以接收三个参数:

        • element 用于测试的当前值。
        • index 可选 用于测试的当前值的索引。
        • array 可选 当前数组。
      • thisArg 可选 执行 callback 时使用的 this 值。

      • 返回值 回调函数必须返回Boolean值

    注意: 如果使用箭头函数表达式来传入函数参数,thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this 值。

const list = [{id: 1012098909, userName: '李白'}, {id: null, userName: '杜甫'}, {id: 102909890, userName: '松鼠航'}]
console.log(list.every(item => Boolean(item.id))); // false
console.log(list.some(item => Boolean(item.id))); // true
1
2
3

这两个判断很简单,就是一个并(&)一个或(|)。

# .slice

  • 数组截取,根据传入的起始位置和截止位置从原数组中截取指定长度的元素组成新的数组返回,新数组为原数组的浅拷贝不会改变原数组;(官方描述:slice() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。)

  • 语法:arr.slice([begin[, end]])

    • begin 可选 提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。

      如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。

      如果省略 begin,则 slice 从索引 0 开始。

      如果 begin 大于原数组的长度,则会返回空数组。

    • end 可选 提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。

      slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。

      如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。

      如果 end 被省略,则 slice 会一直提取到原数组末尾。

      如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。

    • 返回值 一个含有被提取元素的新数组。

slice 不会修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:

  • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。

  • 对于字符串、数字及布尔值来说(不是 StringNumber 或者 Boolean对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

如果向两个数组任一中添加了新元素,则另一个不会受到影响。

const obj1 = {name: '李白', age: 18};
const list1 = [1,2,3,4,5];
const list = ['item1', 'item2', obj1, 'item3', list1, 'item4']; // 数组中obj1 和 list1 都是引用
console.log(list);

const copyList = list.slice(1); // 省略了结束位置,则从第二个元素开始截取
console.log(copyList);

list1.forEach(function(item,index) {
  this[index] = item * 2;
}, list1); // 修改了list对list1的引用,修改list1,则copyList和list都会被改变
console.log(list,copyList);

list[2] = 'item5'; // 直接修改原始数组的元素,浅拷贝的数组也不会受影响
console.log(list,copyList);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# .splice

  • splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

  • 可以删除元素,也可以替换元素,也可以插入元素

  • 语法array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

    • start 指定修改的开始位置(从0计数)。

      如果超出了数组的长度,则从数组末尾开始添加内容;

      如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);

      如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

    • deleteCount 可选 整数,表示要移除的数组元素的个数。

      如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。

      如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。

      如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。

    • item1,item2,... 可选 要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

    • 返回值 由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

const list = ['a', 1, ['李白'], {tip: '提示语'}];
// 在指定位置插入元素
const item = list.splice(2,0,'小白', '小二'); // 开始操作索引 2 ,删除元素0(也就是不删除元素),添加两个元素
console.log(item,list); // item 返回被删除元素组成的数组,没有元素被删除,所以 item 是空数组

// 替换元素
const updatedList = list.splice(2,2, {say: 'how are you'}, 'fine', ['how are you', 'fine'] ); // 开始操作索引2,删除两个元素,补充进3个元素
console.log(updatedList,list)

// 删除元素
console.log(list.splice(1,3), list); // 打印出来的第一个是被删除元素组成成的数组,第二个是被修改后的数组
1
2
3
4
5
6
7
8
9
10
11

# 其他(不常用)

# from

  • 将一个可迭代对象创建为数组(官方描述:Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。)
  • 语法Array.from(arrayLike[, mapFn[, thisArg]])
    • arrayLike 想要转换成数组的可迭代对象
    • mapFn 可选 如果指定了该参数,新数组中的每个元素会执行该回调函数。(也就表示,先转成数组,然后在对数组进行map遍历)
    • thisArg 可选 可选参数,执行回调函数 mapFnthis 对象。
    • 返回值 新数组
// 字符串变数组
const iterableStr = '渡远荆门外,来从楚国游。';
const iterableStrList = Array.from(iterableStr, item =>`${item}${item}`); // ["渡渡", "远远", "荆荆", "门门", "外外", ",,", "来来", "从从", "楚楚", "国国", "游游", "。。"]

// Map变数组
// Map在之后的内容,不理解暂可忽略
const mapObj = new Map([['name', '李白'], ['age', 18]]);
const mapObjList = Array.from(mapObj); // [["name", "李白"], ["age", 18]]
1
2
3
4
5
6
7
8

js内置的可迭代对象有StringArrayTypedArrayMapSet

# of

  • 此API是ES6新增的API
  • Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。(核心:用来创建数组
  • 语法:Array.of(element0[, element1[, ...[, elementN]]])
    • elementN 任意个参数,将按顺序成为返回数组中的元素。
    • 返回值为创建的数组
Array.of('周日', '周一', '周二', '周三', '周四'); // ['周日', '周一', '周二', '周三', '周四']
Array.of(6); // [6]
1
2

Array.of()Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,数组长度为1,而 Array(7) 创建一个长度为7的空数组(**注意:**这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

# .copyWithin

  • 此API为ES6新增API

  • 将数组的某些元素复制插入到数组的指定位置,并返回改变后的数组(官方描述:copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。)

  • 特点:会修改原数组;

  • 语法:arr.copyWithin(target[, start[, end]])

    • target 【可选】 0 为基底的索引,复制序列到该位置。如果是负数,target 将从末尾开始计算。

      如果 target 大于等于 arr.length,将会不发生拷贝。如果 targetstart 之后,复制的序列将被修改以符合 arr.length

    • start 【可选】 0 为基底的索引,开始复制元素的起始位置。如果是负数,start 将从末尾开始计算。

      如果 start 被忽略,copyWithin 将会从0开始复制。

    • end 【可选】 0 为基底的索引,开始复制元素的结束位置。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。

      如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length)。

const arr = ['a','s','d','f','g','h','1','2','3','4','5','6','j'];
const newArr = arr.copyWithin(2,5,8); // 复制3个元素 从索引5开始到8结束,不包含8 从索引为2开始往右进行替换
console.log(arr,newarr); // 
1
2
3

# .fill

# .entries

# .keys

# .values

# .flatMap

# .reverse

# 对象

# is

# assign

# keys

# values

# entries

# 属性简洁表示法

# 属性名表达式

# 其他(不常用)

# super

# fromEntries

# freeze

# isFrozen

# Map

Map 对象是ES6新出的数据结构,类似于对象,同样是键值对的集合,但是它的键(key)不再局限于字符串,任何对象(数组,对象,函数)或者原始值(number、string、boolean、null、undefined)都可以作为键(key)

# Map和Object

引用自MDN

ObjectsMaps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps 使用。不过 MapsObjects 有一些重要的区别,在下列情况里使用 Map 会是更好的选择:

  • 一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型
  • Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
  • 你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
  • Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。虽然 ES5 开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
  • Map 在涉及频繁增删键值对的场景下会有些性能优势。

# 最佳实践

最佳实践是我在实际项目中所感受到的最适合于使用它的场景,带有强烈的主观视觉

  • 场景一: 当后端给我们返回的是某一状态的code码而不是具体状态Name的时候,一般情况可能我们会使用if 判断,比如Code等于1就是待付款,或者使用 switch 去比对,但实际我们这个时候使用Map会非常方便
  • 场景二: 文章列表--文章详情互相跳转,列表和详情可能会频繁的切换跳转,此时就可以把内容详情数据储存到map中,当用户进入过已静查看过的文章,我们就可以从map中取我们储存的值,不需要再次请求接口
  • 场景三: 策略模式
  • 其他更适合的场景项目中自行探索,但这个API真的非常好用

# 创建

  • 重要程度:

  • 语法:new Map([iterable])

    • iterable Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
// orderStatus 1  2 待发货 3 运输中 4 已收货 5 已评价
// logisticsState 110 已接单 220 运输中 140 已揽收 260 配送中 250 已签收
const stateMap = new Map(); // 创建一个空的Map对象
const statusMap = new Map([
  [1, '未付款'],
  [2, '待发货'],
  [3, '运输中'],
  [4, '已收货'],
  [5, '已评价'],
]); // 创建有初始值的Map对象

const newStateMap = new Map([
  ['orderStatus', statusMap],
  ['logisticsState', new Map([
    [110, '已接单'],
    [220, '运输中'],
    [140, '已揽收'],
    [260, '配送中'],
    [250, '已签收']
  ])]
]) // map对象的值可以还是map对象
const getStatusDesc = new Map([
  ['getOrderStatusDesc', (code) => {
    return statusMap.get(code);
  }],
  ['getLogisticsStateDesc', code => {
    const logisticsState = newStateMap.get('logisticsState');
    return logisticsState.get(code);
  }]
]) // map对象的值还可以是函数
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

# .set

  • Map 对象添加或更新一个指定了键(key)和值(value)的(新)键值对
  • set() 方法支持链式调用
  • 语法:myMap.set(key, value);
    • key 要添加至相应 Map 对象的元素的键
    • value 要添加至相应 Map 对象的元素的值
    • 返回值修改后的Map 对象
const stateMap = new Map(); // 创建一个空的Map对象
stateMap.set(1, '未付款'); // 添加一个
stateMap.set(2, '待发货').set(3, '运输中').set(4, '已收货'); // 链式调用
stateMap.set(3, '已评价'); // 更新
1
2
3
4

# .delete

  • 删除Map 对象中指定的元素
  • 语法:myMap.delete(key);
    • key 必须。从 Map 对象中移除的元素的键。
    • 如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false
const statusMap = new Map([
  [1, '未付款'],
  [2, '待发货'],
  [3, '运输中'],
  [4, '已收货'],
  [5, '已评价'],
]);
statusMap.delete(1); // 删除
1
2
3
4
5
6
7
8

# .get

  • 根据key 获取值

  • 重要程度:

  • 语法:myMap.get(key);

    • key 必须参数,也是它唯一的参数,要从目标 Map 对象中获取的元素的键。
    • 返回 Map 对象中与指定键相关联的值,如果找不到这个键则返回 undefined
const statusMap = new Map([
  [1, '未付款'],
  [2, '待发货'],
  [3, '运输中'],
  [4, '已收货'],
  [5, '已评价'],
]); // 创建有初始值的Map对象

const newStateMap = new Map([
  ['orderStatus', statusMap],
  ['logisticsState', new Map([
    [110, '已接单'],
    [220, '运输中'],
    [140, '已揽收'],
    [260, '配送中'],
    [250, '已签收']
  ])]
]) // map对象的值可以还是map对象
const statusDesc = new Map([
  ['getOrderStatusDesc', (code) => {
    return statusMap.get(code); // 查询
  }],
  ['getLogisticsStateDesc', code => {
    const logisticsState = newStateMap.get('logisticsState');
    return logisticsState.get(code); // 查询
  }]
]) 

const getStatusDesc = statusDesc.get('getLogisticsStateDesc'); // 查询后拿到一个函数
console.log(getStatusDesc(140)); // 传入状态code码,输出 已揽收
console.log(getStatusDesc(5)); // undefined
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

如果要使用Map 对象,get() API是一个必须掌握的API,它是我们从来查询数据的;同样创建Map 对象也是必须要掌握的。你可以说我之后不在添加和更新Map 对象了,那set() API可以略放过,但创建必须会。

# .has

  • 检测Map 对象中是否含有指定的元素,返回Boolean
  • 语法myMap.has(key);
    • key 必填. 用来检测是否存在指定元素的键值.
    • 如果指定元素存在于Map中,则返回true。其他情况返回false
const statusMap = new Map([
  [1, '未付款'],
  [2, '待发货'],
  [3, '运输中'],
  [4, '已收货'],
  [5, '已评价'],
]);
status.has(3); // true
1
2
3
4
5
6
7
8

# .clear

  • 删除Map 对象中所有元素。(删库跑路)
  • 语法:myMap.clear();
const statusMap = new Map([
  [1, '未付款'],
  [2, '待发货'],
  [3, '运输中'],
  [4, '已收货'],
  [5, '已评价'],
]);
statusMap.clear();
1
2
3
4
5
6
7
8

# 其他(不常用)

# .keys

# .values

# .forEach

# .entries

# Set

# 创建

# .add

# .clear

# .delete

# .has

# 其他(不常用)

# .entries

# .forEach

# .values

# Promise

Promise 描述请看MDN

# 创建

const promise = new Promise((reslove,reject) => {
  // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
  const num = parseInt(Math.random()*2); // 取个0或者1的随机数
  if(num) {
    reslove(num); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
  } else {
    reject(num); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
  }
})
1
2
3
4
5
6
7
8
9

也许经常在项目中看到的是在函数内Promise作为 返回值返回的,其实都是一样的。不要被局限

# .then

  • then() 接收Promise 成功(官方描述:接收)或失败(官方描述:拒绝)状态(或者就理解promise 成功或失败状态的时候会执行)
  • then() 支持链式调用
  • 语法:promise.then(onFulfilled(result)[, onRejected(result)]);
    • onFulfilled 当 Promise 变成成功状态时调用的函数。该函数有一个参数,即接受的最终结果(result)。如果该参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数
    • onRejected 当 Promise 变成失败状态(rejected)时调用的函数。该函数有一个参数,即拒绝的原因。 如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数
const promise = new Promise((reslove,reject) => {
  // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
  const num = parseInt(Math.random()*2); // 取个0或者1的随机数
  if(num) {
    reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
  } else {
    reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
  }
});
promise.then(res => {
  console.log('成功', res); // 当Promise变成成功状态执行,也就是随机数是1,走if,reslove()执行
}, ({fail}) => {
	console.log('失败', fail); // 当Promise变成失败状态执行,也就是随机数是0,走else,reject()执行
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 链式调用
const promise = new Promise((reslove,reject) => {
  // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
  const num = parseInt(Math.random()*2); // 取个0或者1的随机数
  if(num) {
    reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
  } else {
    reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
  }
});
promise.then(res => {
  console.log('成功', res); // 当Promise变成成功状态执行,也就是随机数是1,走if,reslove()执行
  return res;
}, ({fail}) => {
  console.log('失败', fail); // 当Promise变成失败状态执行,也就是随机数是0,走else,reject()执行
  return Promise.reject(fail)
}).then(res => {
  console.log('第二次链式调用的结果', res)
}, () => {
	console.log('第二次链式调用失败结果,我不接其值')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# .catch

  • 如果.then() 方法没有提供第二个参数,且Promise 是失败状态,.catch() 的内容会被执行(官方描述: catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)))

  • 语法Promise.catch(onRejected);

    • 当Promise 被rejected时,被调用的一个Function。 该函数拥有一个参数:

      reason rejection 的原因。

      如果 onRejected 抛出一个错误或返回一个本身失败的 Promise , 通过 catch() 返回的Promise 被rejected;否则,它将显示为成功(resolved)

const promise = new Promise((reslove,reject) => {
  // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
  const num = parseInt(Math.random()*2); // 取个0或者1的随机数
  if(num) {
    reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
  } else {
    reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
  }
});
promise.catch(res => {
  console.log('失败', res); // 接受Promise失败状态,但是一般不会这么用
})
// 这么使用是有可能的
promise.then(res => {
  console.log('成功', res); // 当Promise变成成功状态执行,也就是随机数是1,走if,reslove()执行
  return res; // 原先是成功状态,我返回了新的Promise并且是失败状态
}).catch(res => {
  console.log('失败', res)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

一般来说,如果我们给.then() 提供了第二个参数,也就是处理失败状态的函数,那么就完全没有必要调用.catch() 方法;如果没有给.then() 提供第二个参数,此时我们可以用.catch() 处理失败;

如果,既给.then() 提供了第二个参数,还写了.catch() 那么,.catch() 永远不会执行

# .all

  • 官方描述: Promise.all(iterable) 方法返回一个 Promise实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果

  • .all() 接收一个数组或者一个字符串,反正是一个可迭代的对象,数组中一般而言我们都会放Promise ,这些Promise 都是成功状态的时候.all() 就返回成功状态的Promise ,这些Promise 只要有一个失败了,就会返回失败状态,并且失败之后的内容不会再执行。这些Promise 的成功结果会汇聚成数组在成功状态中返回,失败的仅会返回失败的结果;如果是字符串,你得到的就是这些字符串迭代的数组,例如传入asdfghjkl 得到的就是['a','s','d','f','g','h','j','k','l']

  • 实用程度:

  • 适用场景:再带有条件查询的列表中,我们可能既要查询列表又要查询条件,此时就可以使用Promise.all() 同时并发两个请求,而不用一个一个的发送

  • 语法:Promise.all(iterable);

    • iterable 一个可迭代对象,如 ArrayString
    • 返回值
      • 如果传入的参数是一个空的可迭代对象,则返回一个**已完成(already resolved)**状态的 Promise
      • 如果传入的参数不包含任何 promise,则返回一个异步完成(asynchronously resolved) Promise。注意:Google Chrome 58 在这种情况下返回一个**已完成(already resolved)**状态的 Promise
      • 其它情况下返回一个处理中(pending)Promise。这个返回的 promise 之后会在所有的 promise 都完成或有一个 promise 失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”示例。返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
const createPromise = () => {
  return new Promise((reslove,reject) => {
    // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
    const num = parseInt(Math.random()*2); // 取个0或者1的随机数
    if(num) {
      reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
    } else {
      reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
    }
  });
}
const resList = Promise.all([createPromise(),createPromise(),createPromise()]);
resList.then(resultList => {
	console.log('三个promise都成功', resultList); // [{ success: 1}, { success: 1}, { success: 1}]
},resultList => {
	console.log('三个promise有失败的', resultList); // { fail: 0, sort: 2 }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# .race

  • .race().all() 有一定相似性,.race() 同样接收数组(可迭代对象),不同的是传入的所有Promise 哪一个先执行完成就采用哪一个结果,第一个执行完成的是成功状态那么就返回成功状态,第一个执行完成的是失败状态那么就返回失败状态;(官方描述:Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。)
  • 适用场景:假设我们需要查询一个列表,有两个接口都可以返回我们需要的数据,此时就可以使用.race() 哪一个先返回我们就采用哪一个结果,减少用户的等待时间
  • 语法:Promise.race(iterable);
    • iterable 一个可迭代对象,如 ArrayString
    • 返回值
      • 一个待定的 Promise只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。
const createPromise = () => {
  return new Promise((reslove,reject) => {
    // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
    const num = parseInt(Math.random()*2); // 取个0或者1的随机数
    if(num) {
      reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
    } else {
      reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
    }
  });
}
const resList = Promise.race([createPromise(),createPromise(),createPromise()]);
resList.then(resultList => {
	console.log('成功结果', resultList); 
},resultList => {
	console.log('失败结果', resultList); 
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# .resolve

  • 返回一个成功状态的Promise(官方描述:Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。如果该值为promise,返回这个promise;如果这个值是thenable(即带有"then" 方法,返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。)
  • 实用程度:
  • 语法Promise.resolve(value);
    • value 将被Promise对象解析的参数。也可以是一个Promise对象,或者是一个thenable。
    • 返回值
      • 返回一个解析过带着给定值的Promise对象,如果参数是一个Promise对象,则直接返回这个Promise对象。
const createPromise = () => {
  return Promise.resolve({name: '成功'});
}
createPromise().then(res => {
  console.log(res);
})
1
2
3
4
5
6

# .reject

  • 返回一个失败状态的Promise(官方描述:Promise.reject(reason)方法返回一个带有拒绝原因reason参数的Promise对象)
  • 实用程度:
  • 语法Promise.reject(reason);
    • reason 表示Promise被拒绝的原因。
    • 返回值
      • 一个给定原因了的被拒绝的 Promise
const createPromise = () => {
  return Promise.reject({name: '成功'});
}
createPromise().catch(res => {
  console.log(res);
})
1
2
3
4
5
6

需要注意如果使用了Promise.resolve() 那么只能在Promise.then() 中进行接收结果,.then() 的第二个参数不会起作用,.catch() 也不会起作用;

同理,使用了Promise.reject() 那么也只能在.then() 的第二个参数中接收结果或者.catch() 中接收结果。

# 其他(不常用)

# .finally

# .allSettled

# .any

# .try

Promise的最佳实践应该是配合async/await来使用,可以非常明显的简化我们的代码

# 语句和声明

# try...catch

  • try...catch 用来检测代码执行过程中出现的错误,并捕获错误(可以避免由于某个js错误造成页面渲染出错)
  • 实用程度:
try{
  // 这里是我们正常的业务逻辑,改写什么就写什么
} catch(error) {
  // 如果try模块内的代码执行出错,出错之后的代码不会再执行,此时会进入catch模块,error是具体的出错原因,在这里可以对错误进行处理
}
1
2
3
4
5
try {
  const data = null;
  const list = data.map(item => {
    return {name: item.name, id: item.id};
  })
} catch(error) {
  console.log('捕获错误', error);
}
// 正常情况下我们会很少见到这种写法,data是个null,结果还去遍历null,正常人都不会这么写

// 但是很多时候,这个data是后端接口给我们返回来的,正常data应该是个数组,但是后端由于没有查询到数据,该返回空数组的时候,后端返回了null,就造成了我们代码出错

// 如果我们不做处理,这个list还是要渲染到页面上的那么就会造成完全空白

// 此时就可以加个try...catch用来做个兼容的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# async/await

  • async/await 是ES8写入的JS标准

  • 我们使用async 来定义一个异步函数,使用await 等待一个异步的执行结果(准确说await 等待一个Promise对象。await 必须在async function 内部使用),async隐式的返回一个Promise;(官方描述:async function 用来定义一个返回 AsyncFunction对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise返回其结果。如果你在代码中使用了异步函数,就会发现它的语法和结构会更像是标准的同步函数。)

  • 实用程度:

  • 重要程度:

  • async/await 可以让我们以更加优雅的姿势来写异步函数,或者说采用async/await之后,我们就不需要再去考虑这个函数是不是异步的。在此之前,对于异步函数,我们必须等待异步函数执行完成之后,拿到异步函数的结果,才可以继续往下进行,基于此必须写深深的回调,在回调里拿结果,在回调里继续写异步.......俗称“回调地狱”。ES6的出现,带来了新的异步解决方案Promise, 但Promise 真的是完美的吗?虽然我们确实走出了“回调地狱”,但相对的不过是把回调变成了纵向的,形成了一个纵向的回调链。异步编程的最高境界,就是应该让我们不用再去关心它是不是异步。async/await的出现,就是让我们以同步的方式来编写异步代码。

  • async/await 依赖于Promise,是Generator 函数的语法糖。async/await 不会替换Promise,但是好像可以替换Generator,所以,Generator就不讲了(我自己也不怎么用)

// 使用 async 定义一个异步函数
const fn = async () => {} // 箭头函数
async function fn2() {} // function
1
2
3
// 本来不想用延时器的,但实在没想到更适合的例子

const fn = () => {
  let time = '18:35:36'
  setTimeout(() => {
    time = '18:35:41';
    console.log(time);
    return time;
  }, 5000);
}
const fn2 = () => {
  const time = fn();
  console.log('fn执行结果', time)
}
fn2();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如上代码,我们本意是想fn() 执行结束之后拿到返回值,将返回值给变量time 然后打印出time,但由于fn()内的setTimeout是异步执行的,并不会等待 5秒等fn执行完成之后再打印time,所以造成的结果就是先打印time = undefined,等了5秒后fn才执行完。

但是这是我们理想的代码写法,虽然目前它没有按我们的理想执行,但使用async/await 之后,我们就可以使我们理想的代码按照我们的理想去执行。

结合Promise去写理想代码

const fn = () => {
  let time = '18:35:36';
  return new Promise((resolve,reject) => {
		setTimeout(() => {
      time = '18:35:41';
      console.log(time);
      resolve(time)
    }, 5000);
  })
}
const fn2 = async () => {
  const time = await fn(); // await等待Promise结果/加了await之后,js执行到此暂停,直到这个函数返回Promise或者出错
  console.log('fn执行结果', time)
}
fn2();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

最佳实践应该是try...catch + async/await + Promise,三者结合写段代码

const createPromise = () => {
  return new Promise((reslove,reject) => {
    // 在这里去写异步操作,完成之后,修改promise状态。这里随便写写
    const num = parseInt(Math.random()*2); // 取个0或者1的随机数
    if(num) {
      reslove({'success': num}); // 取的随机数是0或者1,1 的时候是true,调用resolve()将promise修改为成功状态
    } else {
      reject({'fail': num}); // 取的随机数是0或者1,0 的时候是false,调用reject()将promise修改为失败状态
    }
  });
}
const fn = async () => {
  try {
    const resList = await Promise.all([createPromise(),createPromise(),createPromise()]);
    console.log('成功', resList); // promise成功状态继续往下走,返回失败状态,就进catch阶段
	} catch(error) {
    console.log('失败', error); // promise成功状态继续往下走,返回失败状态,就进catch阶段
    // 其实一直没有说,如果你不需要对错误进行处理,catch中可以什么都不写
    // 或者如果你不想在这里对错误进行处理,可以使用throw 将错误抛出去
    // 如果不想处理错误,也不想接收参数不想接收,catch后可以直接跟大括号,但是这个是ES10提出的,尚未写入标准,兼容性也存疑
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 扩展运算符

... ,用来将数组或者对象进行展开,也可以展开字符串,还可以在函数调用时对入参进行展开(描述不准确,看代码演示);(官方描述: 展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。)

  • 常用来用来实现数组和对象的深拷贝
/************************** 浅拷贝 ****************************/
let numList1 = [1,2,3,4,5,6,7,8];
let numList2 = numList1;
// 这里给forEach传了第二个参数,用来充当this
// 这里使用了function定义函数,因为使用箭头函数第二个参数会被忽略
numList2.forEach(function(item,index) {
  this[index] = item * 3;
},numList2);
console.log(numList1, numList2);

1
2
3
4
5
6
7
8
9
10

我们本意是希望将numList1拷贝一份给numList2,然后对numList2进行修改,numList1我们可能在其他地方还需要用,但由于js对引用类型实行的时浅拷贝,所以会造成numList1也被改变,基于此,我们希望numList2改变不会影响numList1,我们就需要对numList1进行深拷贝

/************************** map深拷贝 ****************************/
const numList1 = [1,2,3,4,5,6,7,8];
const numList2 = [...numList1]
numList2.forEach(function(item,index) {
  this[index] = item * 3;
},numList2);
console.log(numList1, numList2);
// 实现这功能最适合的应该是map()方法,一步到位。这里是为了刻意演示扩展运算符
1
2
3
4
5
6
7
8
  • 对象深拷贝
const obj1 = {name: '李白'};
const obj2 = {age: 18};
const obj3 = {job: '诗人'};
const obj4 = {...obj1,...obj2,...obj3,friend: '汪伦'};
console.log(obj4);
1
2
3
4
5

对于以上写法,如果obj2中的属性在obj1中已经存在了,则obj2中的属性会覆盖obj1 中的属性;后覆盖前

以上写法也是刻意为之,对象深拷贝还有其他API

  • 字符串扩展运算符和在函数调用时
console.log(...'qasxxxxxdrrrrrrrfgbbbhuuuujmmmmko');

const fn = (x,y,z) => {
  return `${x}----${y}----${z}`;
}
fn(...['who','are','you']);
1
2
3
4
5
6

# 扩展

# async/await Promise Generator 的前世今生

# forEach VS map 性能对比

# Symbol 新的JS原始数据类型

# 使用Web自定义事件(CustomEvent 结合EventTarget )更好的实现发布订阅

# 借助Map 更好的实现策略模式

# 放弃Axios 使用fetch

# FileReader 前端文件读取

# FormData 详解

# What is Blob

# 如何在Web使用手机原生的分享能力

# 如何在Input 中输入图片(实现简易富文本编辑器)

Last Updated: 2020-4-27 22:56:04