Skip to content

Commit c101c95

Browse files
committed
add qa
1 parent f3c9079 commit c101c95

File tree

1 file changed

+163
-9
lines changed

1 file changed

+163
-9
lines changed

src/interview/js.md

Lines changed: 163 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,167 @@
22
title: 'JavaScript 相关'
33
icon: 'javascript'
44
order: 1
5+
headerDepth: 1
56
---
67

7-
## 讲讲 apply、call、bind 的区别
8+
## apply、call、bind 的区别是什么?
89

9-
- call 和 apply 都是立即改变,bind 则是返回一个函数等待下一次调用
10-
- call 和 bind 的参数形式相同,apply 的形式是数组
11-
- call 的性能比 apply 高
10+
::: details 答案
1211

13-
## 数组方法中哪些会改变原数组?哪些不改变原数组?
12+
`call``apply``bind`作用是改变函数执行时的上下文,简而言之就是改变函数运行时的 `this` 指向
13+
14+
那什么情况下需要改变`this`的指向呢?下面举个例子
15+
16+
```js
17+
var name = 'lucy';
18+
var obj = {
19+
name: 'martin',
20+
say: function () {
21+
console.log(this.name);
22+
}
23+
};
24+
obj.say(); // martin,this 指向 obj 对象
25+
setTimeout(obj.say, 0); // lucy,this 指向 window 对象
26+
```
27+
28+
从上面可以看到,正常情况`say`方法输出`martin`
29+
30+
但是我们把`say`放在`setTimeout`方法中,在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的,这时候`this`指向`window`,所以输出`lucy`
31+
32+
我们实际需要的是`this`指向`obj`对象,这时候就需要该改变`this`指向了
33+
34+
```js
35+
setTimeout(obj.say.bind(obj), 0); // martin,this指向obj对象
36+
```
37+
38+
### apply
39+
40+
`apply`接受两个参数,第一个参数是`this`的指向,第二个参数是函数接受的参数,以数组的形式传入
41+
42+
改变`this`指向后原函数会立即执行,且此方法只是临时改变`this`指向一次
43+
44+
```js
45+
function fn(...args) {
46+
console.log(this, args);
47+
}
48+
let obj = {
49+
myname: '张三'
50+
};
51+
52+
fn.apply(obj, [1, 2]); // this会变成传入的obj,传入的参数必须是一个数组;
53+
fn(1, 2); // this指向window
54+
```
55+
56+
当第一个参数为`null``undefined`的时候,默认指向`window`(在浏览器中)
57+
58+
```js
59+
fn.apply(null, [1, 2]); // this指向window
60+
fn.apply(undefined, [1, 2]); // this指向window
61+
```
62+
63+
### call
64+
65+
`call`方法的第一个参数也是`this`的指向,后面传入的是一个参数列表
66+
67+
`apply`一样,改变`this`指向后原函数会立即执行,且此方法只是临时改变`this`指向一次
68+
69+
```js
70+
function fn(...args) {
71+
console.log(this, args);
72+
}
73+
let obj = {
74+
myname: '张三'
75+
};
76+
77+
fn.call(obj, 1, 2); // this会变成传入的obj,传入的参数必须是一个数组;
78+
fn(1, 2); // this指向window
79+
```
80+
81+
同样的,当第一个参数为`null``undefined`的时候,默认指向`window`(在浏览器中)
82+
83+
```js
84+
fn.call(null, [1, 2]); // this指向window
85+
fn.call(undefined, [1, 2]); // this指向window
86+
```
87+
88+
### bind
89+
90+
`bind` 方法和 `call` 很相似,第一参数也是 `this` 的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)
91+
92+
改变 `this` 指向后不会立即执行,而是返回一个永久改变 `this` 指向的函数
93+
94+
```js
95+
function fn(...args) {
96+
console.log(this, args);
97+
}
98+
let obj = {
99+
myname: '张三'
100+
};
101+
102+
const bindFn = fn.bind(obj); // this 也会变成传入的 obj ,bind 不是立即执行需要执行一次
103+
bindFn(1, 2); // this 指向 obj
104+
fn(1, 2); // this 指向 window
105+
```
106+
107+
从上面可以看到,`apply``call``bind`三者的区别在于:
108+
109+
- 三者都可以改变函数的 `this` 对象指向
110+
- 三者第一个参数都是 `this` 要指向的对象,如果如果没有这个参数或参数为 `undefined``null`,则默认指向全局 `window`
111+
- 三者都可以传参,但是 `apply` 是数组,而 `call` 是参数列表,且 `apply``call` 是一次性传入参数,而 `bind` 可以分为多次传入
112+
- `bind` 返回绑定 `this` 之后的函数,`apply``call` 则是立即执行
113+
114+
:::
115+
116+
## 如何实现一个 `bind`
117+
118+
::: details 答案
119+
120+
实现`bind`的步骤,我们可以分解成为三部分:
121+
122+
- 修改`this`指向
123+
- 兼容`new`关键字
124+
- 动态传递参数
125+
126+
```js
127+
// 方式一:只在 bind 中传递函数参数
128+
fn.bind(obj, 1, 2)();
129+
130+
// 方式二:在 bind 中传递函数参数,也在返回函数中传递参数
131+
fn.bind(obj, 1)(2);
132+
```
133+
134+
整体实现代码如下:
135+
136+
```js
137+
Function.prototype.myBind = function (context) {
138+
// 判断调用对象是否为函数
139+
if (typeof this !== 'function') {
140+
throw new TypeError('Error');
141+
}
142+
143+
// 获取参数
144+
const args = [...arguments].slice(1);
145+
const fn = this;
146+
147+
return function Fn() {
148+
// 根据调用方式,传入不同绑定值
149+
return fn.apply(
150+
this instanceof Fn ? new fn(...arguments) : context,
151+
args.concat(...arguments)
152+
);
153+
};
154+
};
155+
```
156+
157+
:::
158+
159+
## 数组方法中哪些会改变原数组,哪些不会?
160+
161+
::: details 答案
14162

15163
`let arr = ['a', 'b', 'c', 'd'];`
16164

17-
### 改变原数组的
165+
### 改变原数组的
18166

19167
- `shift`:将第一个元素删除并且返回删除元素,空即为 `undefined`
20168

@@ -109,7 +257,7 @@ a = arr.map((i) => (i.m = 3)); // 注意返回值的差异
109257
console.log(a); // [3, 3]
110258
```
111259

112-
### 不改变原数组的
260+
### 不改变原数组的
113261

114262
- `concat`:用于合并两个或多个数组,不会更改原数组,而是返回一个新数组
115263

@@ -169,7 +317,11 @@ console.log(arr); // ['a', 'b', 'c', 'd']
169317

170318
- `every``flat``keys``toString``entries`
171319

172-
## 使用 `new` 时发生了什么
320+
:::
321+
322+
## 使用 `new` 时发生了什么?
323+
324+
::: details 答案
173325

174326
当你使用 `new` 关键字调用一个构造函数时,它将:
175327

@@ -204,6 +356,8 @@ const salva = new Person('Salva');
204356

205357
- [构造函数介绍 -- mozilla](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Basics#%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%BB%8B%E7%BB%8D)
206358

359+
:::
360+
207361
## 讲一讲原型链
208362

209-
## 手写一个 Promise
363+
## 如何实现一个 `Promise`

0 commit comments

Comments
 (0)