new 操作符的执行过程:

  • 首先创建一个新的空对象
  • 设置原型,将对象的原型设置为函数的 prototype 对象
  • 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  • 判断函数的返回值类型,如果是值类型(值类型通常包括基本的数据类型),返回创建的对象。如果是引用类型,就返回这个引用类型的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function objectFactory(){
let newObject = null ;
//通过 Array.prototype.shift.call(arguments),我们将 shift() 方法应用于 arguments 对象,并返回被移除的第一个参数。注意,这会修改 arguments 对象本身,使其少一个参数。
let constructor = Array.prototype.shift.call(arguments);
let result = null;
//判断参数是否是一个函数
if (typeof constructor!== "function") {
console.error("type error");
return;
}
//新建一个空对象,对象的原型为构造函数的prototype对象
newObject = Object.create(constructor.prototype);
//将this指向新建对象,并执行函数
result = constructor.apply(newObject,arguments);
//判断返回对象
let flag = result & (type result === "object" || typeof result === "function");
//判断返回结果
result flag ? result : newObject ;
}
//使用方法
objectFactory(构造函数,初始化参数);

什么是类数组对象?
一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象。类数组对象的格式通常如下:

1
{0:value1,1:value2,2:value3,length:3}

类数组对象和数组类似,但是不能调用数组的方法。常见的类数组对象有 arguments 和 DOM 方法的返回结果,还有一个函数也可以被看作是类数组对象,因为它含有 length 属性,代表可以接收到参数的个数。
常见的类数组转换成数组的方法:
(1)通过 call 调用数组的 slice 方法来实现转换

1
Array.prototype.slice.call(arrayLike);

(2)通过 call 调用数组的 splice 方法来实现转换

1
Array.prototype.split.call(arrayLike, 0);

(3) 通过 apply 调用数组的 concat 方法来实现转换

1
Array.prototype.concat.apply([], arrayLike);

(4) 通过 Array.from 方法来实现转换

1
Array.from(arrayLike);

Ajax 指的是通过 js的异步通信 ,从服务器获取 XML 文档从中提取数据,在更新到当前网页的对应部分,而不用刷新整个网页。
创建 Ajax 请求的步骤:

  • 创建一个XMLHttpRequest对象。
  • 在这个对象上使用open 方法创建 HTTP 请求,open 方法所需要的参数是请求方法,请求地址,是否为异步和用户的认证信息。
  • 在发起请求前,可以为对象添加一些信息和监听函数
  • 当对象的属性和监听函数设置完成后,最后调用sent 方法来向服务器发起请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
if (this.status === 200) {
handle(this.response);
} else {
console.log(this.statusText);
}
};
xhr.onerror = function () {
console.log(this.statusText);
};
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
xhr.send(null);

使用 Promise 封装 Ajax:

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
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
let promise = new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function () {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}

| Map | Object |
| ——– | ————————————————————————- | ————————————————————————— |
| 意外的键 | Map 默认情况不包含任何键,只包含显示插入的键 | Object 有一个原型,原型链上的键名有可能和自己在对象上的设置的键名产生冲突。 |
| 键的类型 | Map 的键可以是任意值,包括函数,对象,或任意基本类型 | Object 的键必须是 String 或是 Symbol |
| 键的顺序 | Map 中的 key 是有序的。因此哦,当迭代的时候,Map 对象以插入的顺序返回键值 | object 的键是无序的 |
| Size | Map 的键值对个数可以轻易地通过 size 属性获取 | Object 地键值对个数只能手动计算 |
| 迭代 | Map 是 iterable 的,所以可以直接被迭代 | 迭代 Object 需要以某种方式获取它的键然后才能迭代 |
| 性能 | 在频繁增删键值对的场景下表现更好 | 在频繁增删键值对的场景下未作出优化 |

延迟加载就是等页面加载完成之后再加载 JS 文件。JS 延迟加载有利于提高页面加载速度,一般有以下几种方式:

  • defer 属性(脚本的加载与文档解析同步解析,解析后执行)
  • async 属性(脚本异步加载。加载成功后立即执行)
  • 动态创建 DOM 方式
  • 使用 setTimeout 延迟方式:设置定时器来延迟加载 js 脚本
  • 让 JS 放到文档底部最后加载。

数组和字符串的转换方式:

方法 解释
toString() 将数组转化成字符串
join() 将数组的元素连接成一个字符串,可以指定转换成字符串时的分隔符;

数组添加元素操作方法

方法 解释 返回值 是否改变原数组
push() 向数组末尾添加一个或多个元素 新数组的长度
unshift() 向数组的开头添加一个或多个元素 新数组的长度
1
2
3
4
5
6
7
8
9
let array = [1, 2, 3];
let length = array.push(4, 5);
console.log(length); //5
console.log(array); //[1,2,3,4,5]

let array1 = [1, 2, 3];
let length1 = array1.unshift(4, 5);
console.log(array1); //[4,5,1,2,3]
console.log(length1); //5

数组删除元素操作方法

方法 解释 返回值 是否改变原数组
pop() 删除数组的最后一个元素 删除的元素
shift() 删除数组的第一个元素 删除的元素
1
2
3
4
5
6
7
8
9
let array = [1, 2, 3];
let lastElement = array.pop();
console.log(array); // [1, 2]
console.log(lastElement); // 3

let array1 = [1, 2, 3];
let firstElement = array1.shift();
console.log(array1); // [2, 3]
console.log(firstElement); // 1

合并数组的操作方法

方法 解释 返回值 是否改变原数组
concat() 将两个或者多个数组合并成一个新的数组 返回合成的新数组
1
2
3
4
let array1 = [1, 2, 3];
let array2 = [4, 5];
let newArray = array1.concat(array2);
console.log(newArray); // [1, 2, 3, 4, 5]

数组截取元素操作方法

方法 解释 返回值 是否改变原数组
slice() 用于截取数组的一部分元素,截取元素左闭右开 一个新的数组(截取的目标元素组成的)
1
2
3
let array = [1, 2, 3, 4, 5];
let newArray = array.slice(1, 4);
console.log(newArray); // [2, 3, 4]

数组插入元素操作方法

方法 解释 返回值 是否改变原数组
splice() 从数组中删除,替换或插入元素。 被删除元素组成的新数组
fill() 用指定的值填充数组的元素

splice() 详解
splice 的使用格式:

1
array.splice(startIndex,deleteCount,item1,item2)

参数详解:

  • startIndex: 要开始进行修改的索引位置。
  • deleteCount: 要删除的元素数量。如果设置为 0,则不会删除任何元素
  • item1,item2…..:要添加到数组的元素
1
2
3
4
let array = [1, 2, 3, 4, 5];
let removedElements = array.splice(1, 2, 6, 7);
console.log(array); // [1, 6, 7, 4, 5]
console.log(removedElements); // [2, 3]

对数组排序操作方法

方法 解释 返回值 是否改变原数组
reverse() 反转数组顺序 反转后的数组
sort() 对数组进行排序 反转后的数组
1
2
3
4
5
6
7
let array = [1, 2, 3, 4, 5];
array.reverse();
console.log(array); // [5, 4, 3, 2, 1]

let array1 = [3, 1, 4, 2, 5];
array1.sort();
console.log(array1); // [1, 2, 3, 4, 5]

查找特定元素的操作方法

方法 返回值/解释 是否改变原数组
indexOf() 返回指定元素在数组中第一个匹配的位置的索引
lastIndexOf() 返回指定元素在数组中最后一个匹配的位置的索引
includes() (解释:)判断数组是否包含指定元素
1
2
3
let array = [1, 2, 3, 4, 5, 2];
let index = array.indexOf(2);
console.log(index); // 1

迭代的操作方法

方法 解释 返回值 是否改变原数组
forEach() 对数组的每一个元素执行指定的操作 没有返回值
map() 对数组的每一个元素执行指定的操作 一个新的数组
filter() 对数组的每个元素进行筛选 返回一个累计结果
every() 检测数组的每个元素是否符合指定条件 只要有一个是 false 就返回 false
some() 检测数组是否有元素符合指定条件 只要有一个是 true 就返回 true
find()
返回数组中满足指定条件的第一个元素
findIndex() 返回数组中满足指定条件的第一个元素的索引
flatMap() 对数组的每个元素执行指定的操作并将结果压缩成一个新数组 返回一个新数组

数组的其他操作方法

方法 解释
flat() 将多维数组转化成一维数组
isArray() 判断一个对象是否为数组
reduce() 不改变原数组,reduce()对数组正序操作
reduceRight() 不改变原数组,reduceRight()对数组逆序操作

前言:如果想要了解什么是类数组,及其具体内容的话,可以参考上方js基础/考察较多/第二条内容。
arguments 是一个对象,它的属性是从 0 开始一次递增的数字,还有 callee 和 length 等属性,与数组类似;但是它却没有数组常见的方法舒总,如 forEach , reduce 等,所以叫他们类数组。
遍历类数组有三个方法:
(1)将数组的方法应用到类数组上,这个时候就可以使用call 和 apply方法,如:

1
2
3
function foo() {
Array.prototype.forEach.call(argiments, (a) => console.log(a));
}

(2) 使用 Array.from方法将类数组转化程数组:

1
2
3
4
function foo() {
const arrArgs = Areray.from(arguments);
arrArgs.forEach((a) => console.log(a));
}

(3) 使用展开运算符将类数组转化成数组

1
2
3
4
function foo() {
const arrArgs = [...arguments];
arrArgs.forEach((a) => console.log(a));
}

ES6 Module 和 CommonJS 模块的 区别

  • CommonJS 是对模块的浅拷贝,ES6 Module 是对模块的引用,即 ES6 Module 只存只读,不能改变其值,也就是指针指向不能变,类似 const;
  • import 的接口是 read-obly(只读状态),不能修改其变量值。即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对 commonJS 重新赋值(改变指针指向),但是对 ES6 Module 赋值会编译报错。

ES6 Module 和 CommonJS 模块的 共同点 :

  • CommonJS 和 ES6 Module 都可以对引入的对象进行赋值,即对对象内部属性的值进行改变。

  • 使用instanceof运算符来判断构造函数的 prototypr 属性是否出现在对象的原型链中的任何位置
  • 第二种方法,通过对象的constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写
  • 第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用Object.prototype.toString()方法来打印对象的[[Class]]属性进行判断。

for…of 是 ES6 新增的遍历方式,允许遍历一个含有 iterator 接口的数据结构(数组,对象等)并且返回各项的值,和 ES3 中的 for…in 的区别如下:

  • for…of 无法直接遍历普通对象的属性的,因为对象不是可迭代。但是它可以遍历类数组对象。对象遍历获取的是对象的键值for…in 获取的是对象的键名
  • for…in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for…of 只遍历当前对象,不会遍历原型链;
  • 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性);for…of 只返回数组的下标对应得属性值

总结for…in 循环主要是为了遍历对象而生的,不适用于遍历数组;for..of 循环可以用来遍历数组,类数组对象,字符串,Set,Map 以及 Generator 对象

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
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key); //a,b,c
console.log(obj[key]); //1,2,3
}

const arr = [1, 2, 3];
for (let value of arr) {
console.log(value); //1,2,3
}

//对于对象来说,for...of循环是无法直接遍历对象的属性的
//因为对象不是可迭代对象。但是我们可以通过一些方法将对象转换为可迭代对象
//然后再使用for...of循环进行遍历。下面是一个例子:
const obj = { a: 1, b: 2, c: 3 };

// 将对象转换为可迭代对象
const entries = Object.entries(obj);

// 使用for...of循环遍历可迭代对象
for (let [key, value] of entries) {
console.log(key); // 输出:a, b, c
console.log(value); // 输出:1, 2, 3
}
//在这个例子中,我们使用Object.entries(obj)方法将对象转换为一个包含键值对的数组。然后,我们使用for...of循环遍历这个数组,每次迭代时将键值对解构赋值给变量[key, value],
//然后可以分别访问键和值。
//需要注意的是,这种方法遍历对象的属性时并不能保证属性的顺序
//因为对象的属性在内部是无序的。如果需要按照特定顺序遍历对象的属性,可以使用for...in循环。

for…of 是作为 ES6 新增的遍历方式,允许遍历一个含有 iterator 接口的数据结构(数组,对象等)并且返回各项的值,但是普通的对象用 for…of 遍历会报错。
如果需要遍历的对象是类数组对象,用 Array.from 转化成数组即可。

1
2
3
4
5
6
7
8
9
var obj = {
0: "one",
1: "two",
length: 2,
};
obj = Array.from(obj);
for (let k of obj) {
console.log(k);
}

如果不是类数组对象,就给对象添加一个[Symbol.iterator]属性,并指向一个迭代器即可。

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
//方法一:
var obj = {
a: 1,
b: 2,
c: 3,
};

obj[Symbol.iterator] = function () {
var keys = Object.keys(this);
var count = 0;
return {
next() {
if (count < keys.length) {
return { value: obj[keys[count++]], done: false };
} else {
return { value: undefined, done: true };
}
},
};
};

for (var k of obj) {
console.log(k);
}

// 方法二
var obj = {
a: 1,
b: 2,
c: 3,
};
obj[Symbol.iterator] = function* () {
var keys = Object.keys(obj);
for (var k of keys) {
yield [k, obj[k]];
}
};

for (var [k, v] of obj) {
console.log(k, v);
}

这方法都是用来遍历数组的,两者区别如下:

  • forEach()方法会针对每一个元素执行提供的函数,对数据的操作会改变原数组,该方法没有返回值;
  • map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// (1)匹配16进制颜色值
var regex = /#([0-9a-fA-F]{6}|([0-9a-fA-F]{3})/g;

// (2)匹配日期,如yyyy-mm-dd格式
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][1-9]|3[01])$/;

// (3)匹配QQ号
var regex = /^1[345678]\d{9}$/g;

// (4)手机号码正则
var regex = /^1[34578]\d{9}$/g;

// (5)用户名正则
var regex = /^[a-zA-Z\$][a-zA-Z0-9_\$]{4,16}$/;

正则常用符号总结:

符号 含义 例子
[ ] 里面的内容表示或运算 [ab]==a|b
[a-z] : 任意小写字母
[^ ] 表示除了
. 表示任意字符
\ 转义字符 \.表示.
\w 任意字母,数字 等价于[A-z0-9]
\W 除了字母,数字 等价于[^A-z0-9]
\d 任意数字 等价于[0-9]
\D 除了数字 等价于[^0-9]
\s 空格
\S 除了空格
\b 单词边界
\B 除了单词边界

正则量词表示:

量词 描述
n+ 匹配任何包含至少一个 n 的字符串
n* 匹配任何包含零个或多个 n 的字符串
n? 匹配任何包含零个或一个 n 的字符串
n{x} 匹配任何包含 x 个 n 的序列的字符串
n{x,y} 匹配任何包含 x 个或 y 个 n 的序列的字符串
n{x,} 匹配任何至少包含 x 个 n 的序列的字符串
n$ 匹配任何结尾为 n 的字符串
^n 匹配任何开头为 n 的字符串
?=n 匹配任何其后紧接指定字符串为 n 的字符串
?!n 匹配任何其后没有紧接指定字符串为 n 的字符串

支持正则表达式的 String 对象的常用方法:

方法 描述
search 检索与正则表达式相匹配的值
match 找到一个或多个正则表达式的匹配
replace 替换与正则表达式匹配的子串
split 把字符串分割为字符串数组

2.对 JSON 的理解
JSON 是一种基于文本的轻量级的数据交换格式。它可以被任何的编程语言读取和作为数据格式来传递。
因此,在项目开发过程中使用 JSON 作为前后端数据交换的方式。在前端通过将一个符合 JSON 格式的数据结构序列转化为 JSON 的字符串,然后将它传递到后端,后端通过 JSON 格式的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。
因为 JSON 的语法是基于 js 的,因此很容易将 JSON 和 js 中的对象弄混,但是应该注意的是 JSON 和 js 中的对象不是一回事,JSON 中对象格式更加严格,比如说在 JSON 中属性值不能为函数,不能出现 NaN 这样的属性值等,因此大多数的 js 对象是不符合 JSON 对象的格式的。
在 js 中提供了两个函数来实现 js 数据结构和 JSON 格式的转换处理:

  • JSON.stringify()函数 ,通过传入一个符合 JSON 格式的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不符合 JSON 格式,那么在序列化的时候会对这些值进行对应的特殊处理,使其符合规范。 在前端向后端发送数据时 ,可以调用这个函数将数据对象转化为 JSON 格式的字符串。
  • JSON.parse()函数 ,这个函数用来将 JSON 格式的字符串转换为一个 js 数据结构,如果传入的字符串不是标准的 JSON 格式的字符串的话,将会抛出错误。当 从后端接收到 JSON 格式的字符串时 ,可以通过这个方法来将其解析为一个 js 数据结构,以此来进行数据的访问。

尾调用指的是函数的最后异步调用另一个函数。代码执行是基于执行栈的,所以当一个函数里调用另一个函数时,会保留当前执行的上下文,然后再新建另一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步了,所以这时可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式下无效 。

  • DOM 节点的获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getElementById; // 按照 id 查询    结果形式=>元素
getElementsByTagName; // 按照标签名查询 结果形式=>集合
getElementsByClassName; // 按照类名查询 结果形式=>集合
querySelectorAll; // 按照 css 选择器查询 结果形式=>集合

// 按照 id 查询
var imooc = document.getElementById("imooc"); // 查询到 id 为 imooc 的元素
// 按照标签名查询
var pList = document.getElementsByTagName("p"); // 查询到标签为 p 的集合
console.log(divList.length);
console.log(divList[0]);
// 按照类名查询
var moocList = document.getElementsByClassName("mooc"); // 查询到类名为 mooc 的集合
// 按照 css 选择器查询
var pList = document.querySelectorAll(".mooc"); // 查询到类名为 mooc 的集合
  • DOM 节点的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container">
<h1 id="title">我是标题</h1>
</div>
<script>
var container = document.getElementById("container");
var targetSpan = document.createElement("span");
targetSpan.innerHTML = "hello world";
container.appendChild(targetSpan);
</script>
</body>
</html>
  • DOM 节点的删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container">
<h1 id="title">我是标题</h1>
</div>
<script>
var container = document.getElementById("container");
var targetNode = document.getElementById("title");
container.removeChild(targetNode);
</script>
</body>
</html>
  • 修改 DOM 元素

指定交换两个 DOM 元素的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<title>DEMO</title>
</head>
<body>
<div id="container">
<h1 id="title">我是标题</h1>
<p id="content">我是内容</p>
</div>
<script>
// 获取父元素
var container = document.getElementById("container");
// 获取两个需要被交换的元素
var title = document.getElementById("title");
var content = document.getElementById("content");
// 交换两个元素,把 content 置于 title 前面
container.insertBefore(content, title);
</script>
</body>
</html>
  • 添加和移除 CSS 类

使用 classList 属性的 add()和 remove()方法向元素添加或移除 CSS 类。

1
2
element.classList.add("newClass");
element.classList.remove("oldClass");
  • DOM 操作元素特征
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//操作元素内容
//使用innerHTML属性获取或设置元素的HTML内容
element.innerHTML = "New content";

//操作元素文本
//使用textContent属性获取或设置元素的纯文本内容
element.textContent = "New text";

//操作元素属性
//使用getAttribute()和setAttribute()方法获取或设置元素的属性值
const value = element.getAttribute("src");
element.setAttribute("src", "newImage.jpg");

//监听事件
//使用addEventListener()方法添加事件监听器,以响应用户操作或其他事件
element.addEventListener("click", handleClick);

  • 强类型语言: 强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量使用要严格符合定义,所有变量都必须先定义后使用。 Java 和 C++等语言都是强制类型定义的 ,也就是说,一旦一个变量被指定了某一个数据类型,如果不经过强制类型转换,那么它就永远是这个数据类型。例如:我们定义了一个整数,如果不显式地进行转换,那么这个整数就不能被视为是一个字符串。
  • 弱类型语言: 弱类型语言也称为弱类型定义语言,与强类型定义相反。JS 语言就属于弱类型语言。简单理解就是一种变量类型可以被忽略地语言。比如 JS 是弱类型定义的 ,在 JS 中就可以将字符串”12”和整数 3 进行连接得到字符串’123’,在相加的时候会进行强制类型转换。
  • 区别

两者对比:强类型语言在速度上可能略逊于弱类型语言,但是强类型语带来的严谨性可以有效地避免很多错误。

(1)AJAX
Ajax 即“AsynchronousJavascriptAndXML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。它是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。其缺点如下:

  • 本身是针对 MVC 编程,不符合前端 MVVM 的浪潮
  • 基于原生 XHR 开发,XHR 本身的架构不清晰
  • 不符合关注分离(Separation of Concerns)的原则
  • 配置和调用方式非常混乱,而且基于事件的异步模型不友好。

(2)Fetch
fetch 号称是 AJAX 的替代品,是在 ES6 出现的,使用了 ES6 中的 promise 对象。Fetch 是基于 promise 设计的。Fetch 的代码结构比起 ajax 简单多。fetch 不是 ajax 的进一步封装,而是原生 js,没有使用 XMLHttpRequest 对象。
fetch 的优点:

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 更加底层,提供的 API 丰富(request, response)
  • 脱离了 XHR,是 ES 规范里新的实现方式

fetch 的缺点:

  • fetch 只对网络请求报错,对 400,500 都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • fetch 默认不会带 cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
    fetch 不支持 abort,不支持超时控制,使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
  • fetch 没有办法原生监测请求的进度,而 XHR 可以

(3)Axios
Axios 是一种基于 Promise封装的 HTTP 客户端,其特点如下:

  • 浏览器端发起 XMLHttpRequests 请求
  • node 端发起 http 请求
  • 支持 Promise API
  • 监听请求和返回
  • 对请求和返回进行转化
  • 取消请求
  • 自动转换 json 数据
  • 客户端支持抵御 XSRF 攻击