0%

axios和fetch区别

二者都用来发送ajax请求,实现局部更新,axios是基于xhr的,fetch是原生支持的。

axios的拦截器是什么?有什么用?

interceptors.request可以做身份验证

interceptors.response可以做error日志处理,统一返回格式(res.data和res.data.data统一为res.data)

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
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
headers: {
'Content-Type': 'application/json'
}
})


// 类似express中的app.use,可以做身份验证
instance.interceptors.request.use(
function(config){
const token=localStorage.getItem('token')
if(token){
config.headers.Authorization=`Bearer ${token}`
}
return config
}
function(error){
return Promise.reject(error)
}
)

// 可以用来统一处理error日志,而不是在每个catch中处理
axios.interceptors.response.use(
response => {
// 处理成功的响应
console.log('Response received:', response);
// 例如统一处理数据格式
if (response.data && response.data.data) {
return response.data.data;
}
return response;
},
error => {
// 处理错误的响应
console.error('Error response:', error);
// 例如统一处理错误信息
if (error.response) {
switch (error.response.status) {
case 401:
// 处理未授权错误
console.error('Unauthorized');
break;
case 500:
// 处理服务器错误
console.error('Server error');
break;
default:
console.error('Error status:', error.response.status);
break;
}
}
return Promise.reject(error);
}
);

axios还有哪些方法?

axios.get, axios.post, axios.options(预检), axios.patch修改

axios.all(类似promise.all)

组合模式与继承

为什么组合大于继承?

这是在功能不够确定时,组合大于继承。

例如人类在建立鸟的分类系统时,如果使用单继承,就类似文件夹分类。

1
2
3
4
5
abstract class Bird {
fly() {
console.log('fly')
}
}

鸟分为麻雀、天鹅、鹈鹕等,但有些鸟会飞,有些不会。如果鸡继承鸟的话,就会有会飞这个方法,但鸡不会飞,为了避免误用,就需要抛出异常:

1
2
3
4
5
class chicken extends Bird {
fly() {
throw new Error("cannot fly")
}
}

或者分为两个类,

1
2
3
4
5
6
7
8
9
abstract class Bird {}

class FlyableBird {
fly() {
console.log('fly')
}
}

class UnFlyableBird {}

但后来随着种类增多,可能会有生殖方式、寿命、迁徙、地域、嘴巴形状、眼睛形状等等区分,这个继承层级或者说文件夹的深度会变得越来越深。

这种情况可以选择组合模式,类似标签。

1
2
3
4
5
6
7
interface Flyable {
fly(): void;
}

interface EggLayable {
layEgg(): void;
}

但这样我们就会每次都需要实现fly方法,为了减少重复代码,可以使用委托:

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
interface Flyable {
fly(): void;
}

interface Tweetable {
tweet(): void;
}

interface EggLayable {
layEgg(): void;
}

class FlyAbility implements Flyable {
fly() {
console.log("I can fly!");
}
}

class TweetAbility implements Tweetable {
tweet() {
console.log("I can tweet!");
}
}

class EggLayAbility implements EggLayable {
layEgg() {
console.log("I can lay an egg!");
}
}

class Ostrich implements Tweetable, EggLayable {
private tweetAbility: TweetAbility;
private eggLayAbility: EggLayAbility;

constructor() {
this.tweetAbility = new TweetAbility();
this.eggLayAbility = new EggLayAbility();
}

tweet() {
this.tweetAbility.tweet();
}

layEgg() {
this.eggLayAbility.layEgg();
}
}

class Sparrow implements Flyable, Tweetable, EggLayable {
private flyAbility: FlyAbility;
private tweetAbility: TweetAbility;
private eggLayAbility: EggLayAbility;

constructor() {
this.flyAbility = new FlyAbility();
this.tweetAbility = new TweetAbility();
this.eggLayAbility = new EggLayAbility();
}

fly() {
this.flyAbility.fly();
}

layEgg() {
this.eggLayAbility.layEgg();
}
}

柯里化

什么是柯里化?柯里化有什么用?

real world haskell中的“Partial Function Application and Currying”章节有讲到:“In Haskell, all functions take only one argument.”

1
2
3
4
5
ghci> :type dropWhile
dropWhile :: (a -> Bool) -> [a] -> [a]

ghci> :type dropWhile isSpace
dropWhile isSpace :: [Char] -> [Char]

dropWhile isSpace is a function that strips leading whitespace from a string.

How is this useful? As one example, we can use it as an argument to a higher order function:

1
2
ghci> map (dropWhile isSpace) [" a","f","   e"]
["a","f","e"]”

bind和curry同样是partial application,有什么不同?

bind需要绑定context,curry不需要。

  • 在解决这个问题之前,我们可以看一下bind的实现:

==和===有什么不同?

1
2
3
4
5
6
7
5=='5' // '5'会被转换为number 5
0 == false // false会被转换为0
null == undefined // true,因为它们在不进行类型转换的情况下是相等的

null === undefined // false,因为类型不同,null是object类型,undefined是undefined
console.log(typeof a); // 输出:object
console.log(typeof b); // 输出:undefined

Array API

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
let L=console.log.bind(console)
let arr=[1,2,3,4]

L(arr.some(curr=> curr==2 || curr==3)) // true
L(arr.every(curr=> curr >0)) // true
L(arr.every(curr=> curr >1)) // false

let total=0
arr.forEach((v,i)=>{
total+=v
})
L(total) // 10

L(arr.indexOf(4))
L(arr.find((curr=>curr>2))) // the first element that is bigger than 2 is 3

arr.unshift(9)
L(arr) // [9,1,2,3,4]
L(arr.toString()) // '9,1,2,3,4'
L(arr.join())

let arr1=arr.splice(1,4)
L(arr) // [9]
L(arr1) // [1,2,3,4] the element deleted

arr1.reverse()
L(arr1) // [4,3,2,1]

arr1.splice(2,0,10)
L(arr1) // [4,3,10,2,1]

arr1.sort((a,b)=>{
return a-b
})
L(arr1) // [1,2,3,4,10]

L(arr1.at(-1)) // 10

当我们使用axios时发生了什么?

1
2
3
axios.get()
.then((data)=>{conolse.log(data)})
.catch((error)=>{console.log(error)})

axios会生成一个promise对象,对象状态为pending。

如果后端返回成功,则promise调用promise内置的resolve方法将状态改为fullfilled,并且value被传递给then中的方法,then方法中的回调函数被添加到微任务队列。假设我们只执行这一行,则微任务队列为空,立即执行then中的方法。

如果后端返回失败,则promise调用promise内置的reject方法将状态改为rejected,并且 reason(拒绝的原因)会被传递给紧随其后的 .catch() 方法的回调函数,catch方法中的回调函数被添加到微任务队列,假设我们只执行这一行,则微任务队列为空,立即执行catch中的方法。

手写promise.all

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

Promise.all2 = (promises) => {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
resolve()
return
}
if (promises.length !== 0) {
let counter = 0
let results = new Array(promises.length)
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i])
.then((value) => {
counter += 1
results[i] = value
if (counter == promises.length) {
resolve(results)
}
})
.catch(() => {
reject('出错了')
})
}
}

})
}

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo')
}, 100);
});

Promise.all2([promise1, promise2, promise3]).then(values => {
console.log(values); // 预期输出: [3, 42, "foo"]
}).catch(error => {
console.error(error);
});

手写promise

在实现之前,时刻铭记:If we do not want execute immediately, we need to put it into a function and call it when desired.

并且我们需要知道正常的promise具备了哪些功能,一般来说,常用的promise使用为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(res);
}, 1000);
});

promise.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
});
promise.catch((err) => {
console.log(err);
});
promise.finally(() => {
console.log("finally over");
});

所以我们要实现:

  • 对Promise传入callback
  • 当resolved时,执行then中的callback
  • 当rejected时,执行catch中的callback
  • 不管resolved还是rejected,都执行finally中的callback

首先先实现then

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
70
71
const FULFILLED = 'fulfilled'
const PENDING = 'pending'
const REJECTED = 'rejected'


function MyPromise(handler) {
this.status = PENDING
this.value = undefined
this.onFulFilledCallbacks = []
this.onRejectedCallbacks = []

const resolve = (val) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = val
this.onFulFilledCallbacks.forEach(f => f(val))
}
}

const reject = (err) => {
if (this.status === PENDING) {
this.status = REJECTED
this.value = err
this.onRejectedCallbacks.forEach(f => f(err))
}
}

try {
handler(resolve, reject)
} catch (error) {
reject(error)
}
}


MyPromise.prototype.then = function (onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
} else if (this.status === REJECTED) {
onRejected(this.value)
} else {
// pending
this.onFulFilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}

const p1 = new MyPromise((resolve, reject) => {
setTimeout((v) => {
resolve(v)
}, 1000, 'ok')
})

const p2 = new MyPromise((resolve, reject) => {
setTimeout((v) => {
resolve(v)
}, 1000, 'fail')
})


p1.then(v => {
console.log(v)
}, err => {
console.log(err)
})

p2.then(v => {
console.log(v)
}, err => {
console.log(err)
})

当我们在执行p1.then()时,p1仍处于pending状态,所以两个回调分别加入成功队列和失败队列。

等待1s后,p1状态变为fulfilled,并开始执行成功队列的方法。

我们实现了then,但是这个then是没有返回值的,所以也不支持链式调用。链式调用可以让代码更清晰,所以很有必要支持,支持链式调用就需要返回一个新的promise,实现如下:

https://leetcode.cn/problems/count-alternating-subarrays/description/

输入: nums = [0,1,1,1]

输出: 5

5=1+2+1+1

输入: nums = [1,0,1,0]

输出: 10

10=1+2+3+4

可以发现规律,如果不重复,就是每次累加,否则个数就只是+1。

本质是个dp。

1
2
3
4
5
6
7
8
9
10
11
12
13
var countAlternatingSubarrays = function(nums) {
if(!nums || nums.length==0){
return 0
}
//p[i] denote the number of alternating subarrays that ends with nums[i]
let p=new Array(nums.length).fill(1)
for(let i=1;i<nums.length;i++){
if(nums[i]!==nums[i-1]){
p[i]=p[i-1]+1
}
}
return p.reduce((acc,curr)=>acc+curr)
};

但这里可以进行空间优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @param {number[]} nums
* @return {number}
*/
var countAlternatingSubarrays = function(nums) {
if(!nums || nums.length==0){
return 0
}
let i=1 // pointer
let k=1 // counter
let R=1
for(i=1;i<nums.length;i++){
if(nums[i]!==nums[i-1]){
k+=1
R+=k
}else{
k=1
R+=1
}
}
return R
};

https://leetcode.cn/problems/water-bottles-ii/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @param {number} numBottles
* @param {number} numExchange
* @return {number}
*/
var maxBottlesDrunk = function(B, E) {
let R=0
let empty=0
while(true){
[B,empty,R]=[0,empty+B,R+B]
if(B==0&&empty<E){
break
}
[B,empty,E]=[1,empty-E,E+1]
}
return R
};

后序遍历

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
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @param {number[]} to_delete
* @return {TreeNode[]}
*/
var delNodes = function(root, to_delete) {
let R=[]
let S=new Set(to_delete)
if(!to_delete.includes(root.val)){
R.push(root)
}
dfs(root)
return R
function dfs(curr){
if(!curr){
return null
}
curr.left=dfs(curr.left)
curr.right=dfs(curr.right)
if(S.has(curr.val)){
console.log(curr.val)
if(curr.left) {
R.push(curr.left)
}
if(curr.right) {
R.push(curr.right)
}
return null
}else{
return curr
}
}
};

https://leetcode.cn/problems/corporate-flight-bookings/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @param {number[][]} bookings
* @param {number} n
* @return {number[]}
*/
var corpFlightBookings = function(bookings, n) {
let R=new Array(n+1).fill(0)
for(let booking of bookings){
let [first,last,seats]=booking
R[first]+=seats
if(last+1<R.length){
//之后累加时,从first到last都会累计+seats的buff,而为了去buff,
//就需要对last+1上debuff
R[last+1]-=seats
}
}
for(let i=2;i<n+1;i++){
R[i]+=R[i-1]
}
R.shift()
return R
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function reverseWords(s: string): string {
let tmp=[]
let A=[]
for(let i=0;i<s.length;i++){
if(s[i]===' '){
if(tmp.length>0) {
A.push(tmp.join(''))
tmp=[]
}
continue
}
if(s[i]!==' '){
tmp.push(s[i])
}
}
if(tmp.length>0){
A.push(tmp.join(''))
}
return A.reverse().join(' ')
};

https://leetcode.com/problems/zigzag-conversion/description/

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
class Solution:
def convert(self, s: str, nrows: int) -> str:
if nrows==1:
return s
D=defaultdict(list)
# traverse row and once reach the ceil or bottom, then
# change direction and increase/stop increase column
k=0
i=0
j=0
direction=True
increase=False
while k<len(s):
D[i].append(s[k])
k+=1
if direction:
i+=1
else:
i-=1
if increase:
j+=1
if k%(nrows-1)==0:
direction=not direction
increase=not increase
R=""
for row in range(nrows):
R+="".join(D[row])
return R

Actually, we don’t need increase variable. But it is simulation, so i keep it there.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function merge(A: number[][]): number[][] {
if(!A){
return A
}
let [i,k]=[1,0]
A.sort((a,b)=>{
return a[0]-b[0]
})
let R=[A[0]]
while(true){
while(i<A.length && R[k][1]<A[i][0]) {
R.push(A[i])
k++
i++
}
if(i>=A.length) break
R[k][1]=Math.max(R[k][1],A[i][1])
i+=1
}
return R
};

It can be simplified:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function merge(A: number[][]): number[][] {
if(!A){
return A
}
let [i,k]=[1,0]
A.sort((a,b)=>{
return a[0]-b[0]
})
let R=[]
for(let i=0;i<A.length;i++){
if(R.length==0){
R.push(A[i])
continue
}
if(R[R.length-1][1]<A[i][0]){
R.push(A[i])
continue
}
if(R[R.length-1][1]>=A[i][0]){
R[R.length-1][1]=Math.max(R[R.length-1][1], A[i][1])
}
}
return R
};