这个文章主要用来收集一些面试题目
我记得我面试的时候完全就没怎么让手写面试题的

基础 JavaScript 题目

一般来讲邪门的题目大都是 JS 题目吧

关于 this 指向的

1
2
3
4
5
6
7
8
9
var user = {
count: 1,
getCount: function () {
return this.count;
},
};
console.log(user.getCount()); // 1
var func = user.getCount;
console.log(func()); // undefined

分析:

var func = user.getCount这句,func是一个返回this.count的方法
可以理解为window.func()
可以复制到控制台里,输入window.count = 123,执行func(),将会输出 123

关于中括号语法取值的

1
2
3
4
5
6
7
8
9
10
var a = {};
var b = { key: "b" };
var c = { key: "c" };
var d = [3, 4, 5];
a[b] = 123;
a[c] = 345;
a[d] = 333;
console.log(a[b]); // 345
console.log(a[c]); // 345
console.log(a[d]); // 333

分析:

中括号语法取值之前,会先toString()一下
所以就变成了:

1
2
3
4
5
a["[object Object]"] = 123;
a["[object Object]"] = 345;
a["3,4,5"] = 333;
// 控制台里跑一下
// {[object Object]: 345, 3,4,5: 333}

关于函数 return 给了谁的迷惑问题

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = function (val, index) {
console.log(index);
return {
fn: function (name) {
return a(name, val);
},
};
};
// 注意 打印的是index
var b = a(0); // undefined
b.fn(1); // 0
b.fn(2); // 0
b.fn(3); // 0

分析:

执行b = a(0),b 被赋值为{fn: function (name) { return a(name, 0) }}
执行b.fn('val')的时候,会返回一个对象{fn: function (name) { return a(name, 'val') }},这个对象没有赋值给任何变量(进垃圾桶吧你),b从未被改变
很简单的问题,迷惑了好久,都怪这个 index 语义化…
可以试试这样

1
2
3
var q = b.fn(1); // 0
var w = b.fn(2); // 1
var e = b.fn(3); // 2

经典的作用域闭包题目

红宝书上的

1
2
3
4
5
6
7
8
9
10
11
12
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
// 5 5 5 5 5
}
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 0);
// 0 1 2 3 4
}

不要用 let, 用闭包试试

1
2
3
4
5
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(() => console.log(j), 1000);
})(i);
}

可以去书里查一查块级作用域,let 和 var 的区别

异步,事件循环,宏任务微任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
console.log("start");
setTimeout(() => {
console.log("children2");
Promise.resolve().then(() => {
console.log("children3");
});
}, 0);

new Promise(function (resolve, reject) {
console.log("children4");
setTimeout(function () {
console.log("children5");
resolve("children6");
}, 0);
}).then((res) => {
console.log("children7");
setTimeout(() => {
console.log(res);
}, 0);
});

思路:
浏览器先把代码看一遍,遇到直接执行的就执行(第一个宏任务)
遇到宏任务和微任务代码先按顺序扔一边
同步的执行完之后就执行微任务
微任务里面肯定有同步任务,微任务,宏任务
遇到同步任务直接解决,遇到微任务在微任务后面继续排队,遇到宏任务在宏任务后面继续排队
整体的执行顺序是:
有同步任务先解决,然后解决微任务,最后解决宏任务
以上输出结果:

1
2
3
4
5
6
7
"start";
"children4";
"children2";
"children3";
"children5";
"children7";
"children6";

我觉得我没讲明白,但是我明白
我觉得应该找张纸面对面地讲,画四个圈儿分别代表主任务队列,微任务,宏任务,回调任务…
然后执行到哪儿就写一个

宏任务

  • script(整体代码)
  • setTimeout,setInterval
  • xhr
  • I/O
  • UI 交互事件

微任务

  • Promese
  • await 之后的内容(我不知道这样理解是否正确,但是在执行顺序上确实类似于微任务)
  • MutationObserver

Vue2 父子组件生命周期顺序

2022.3.30

今天被问到的问题:

组件的生命周期相关 现在有一对父子组件 他们一定有四个生命周期需要执行
父组件的 beforeCreate 父组件的 created 子组件的 beforeCreate 子组件的 created
这四个的执行顺序是怎样的

我一下子没有迷糊过来…. 回答错误了

正确的答案是这样的

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
// 父元素
export default Vue.extend({
name: 'HomeView',
components: {
HelloWorld,
},
beforeCreate() {
console.log('父元素', 'beforeCreate')
},
created() {
console.log('父元素', 'created')
},
beforeMount() {
console.log('父元素', 'beforeMount')
},
mounted() {
console.log('父元素', 'mounted')
},
});
// 子元素
export default Vue.extend({
name: 'HelloWorld',
props: {
msg: String,
},
beforeCreate() {
console.log('子元素', 'beforeCreate')
},
created() {
console.log('子元素', 'created')
},
beforeMount() {
console.log('子元素', 'beforeMount')
},
mounted() {
console.log('子元素', 'mounted')
},
});
1
2
3
4
5
6
7
8
父元素 beforeCreate
父元素 created
父元素 beforeMount
子元素 beforeCreate
子元素 created
子元素 beforeMount
子元素 mounted
父元素 mounted

我觉得是因为在 mounted 之前,Vue 要完成组件树的构建,构建完成之后,从子组件开始依次向上渲染。所以 beforeMount 是一个分界线