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 攻击