对自己做过的事要有足够的了解和思考,特别是简历上提到的技术关键词,务必做到了然于心,做到熟悉源码的程度
对于常用的api不能仅限于使用层面上,分析源码是必须的
算法目标还是刷到100道,手写代码这块每天逼自己多写多练
心态方面吧,及时调整和沟通,多跟大佬们交流、学习
vue-router跟vuex怎么混入到vue实例中的
vue-directive指令的声明周期
requestAnimationFrame是怎么保证在宏任务里的执行顺序
304跟200的区别(304从缓存系统中返回数据,速度更快,如果没有名字协商缓存,需要从源服务器返回,响应慢)
ts中的高级用法源码是如何实现的,比如Picker
1
2
3type Picker<T, K extends keyof T> = {
[P in K]: T[P]
}需要刻意练习,加深理解
四场面试做个总结以及需要补充的点
axios封装和分析源码(精简)
内存管理GC,新生代和老生代
异步编程方案,横向比较Promise/async/Generator
常用的loader和plugin源码分析
vue-router和vuex源码实现(精简)
项目部署相关
JS 内存机制:栈(基本类型、引用类型地址)与堆(引用类型数据)
JavaScript 中的内存空间主要分为三种类型:
- 代码空间:主要用来存放可执行代码,与内存回收关系不大
- 栈空间:调用栈的存储空间就是栈空间,调用栈,存储执行上下的
- 堆空间: 存储执行上下文之外的数据类型
JavaScript 中的变量类型有 8 种,可分为两种:基本类型、引用类型
1 | number |
引用类型主要是 object
基本类型是保存在栈内存中的简单数据段,而引用类型保存在堆内存中
栈空间
基本类型在内存中占有固定大小的空间,所以它们的值保存在栈空间,我们通过 按值访问
一般栈空间不会很大
堆空间
值大小不固定,指针大小(内存地址)是固定的
当查询引用类型的变量时, 先从栈中读取内存地址,然后再通过内存地址找到堆中的值。对于这种,我们把它叫做 按引用访问
一般堆内存空间很大,能存放大量数据,所以内存分配和回收都需要一定的时间
基本类型(栈空间)与引用类型(堆空间)的存储方式决定了:基本类型赋值是值赋值,而引用类型赋值是地址赋值。
栈内存垃圾回收
栈内存中的垃圾回收其实就是销毁执行栈中的执行上下文,我们都知道执行栈中存放的就是函数执行过程中的执行上下文,栈顶就是我们正在执行函数的执行上下文。
当我们的函数执行完毕后,执行栈中对应的执行上下文会被销毁,这也就是栈垃圾回收的过程。
为了了解这一过程,我们举个简单的例子:
主线程上会存在 ESP 指针 ESP 是执行栈中用来记录当前执行状态的指针
综上所述,JS 引擎是通过 ESP 指针的下移操作来完成栈内存中的垃圾回收的
堆内存垃圾回收
从上面的介绍我们知道,堆内存中主要存放的是复杂数据类型(引用类型)和闭包内部函数引用的基本类型的数据。那么堆内存又是怎么进行垃圾回收的,下面我们来看一下。
V8 中把堆分成新生代与老生代两个区域:
新生代:用来存放生存周期较短的小对象,一般只支持1~8M的容量
老生代:用来存放生存周期较长的对象或大对象
V8 对这两块使用了不同的回收器:
新生代使用副垃圾回收器
老生代使用主垃圾回收器
其实无论哪种垃圾回收器,都采用了同样的流程(三步走):
- 标记: 标记堆空间中的活动对象(正在使用)与非活动对象(可回收)
- 垃圾清理: 回收非活动对象所占用的内存空间
- 内存整理: 当进行频繁的垃圾回收时,内存中可能存在大量不连续的内存碎片,当需要分配一个需要占用较大连续内存空间的对象时,可能存在内存不足的现象,所以,这时就需要整理这些内存碎片。
新生代回收也就是副垃圾回收器
它采用 Scavenge 算法及对象晋升策略来进行垃圾回收
所谓 Scavenge 算法,即把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域,如下图所示:
新加入的对象都加入对象区域,当对象区满的时候,就执行一次垃圾回收,执行流程如下:
标记:首先要对区域内的对象进行标记(活动对象、非活动对象)
垃圾清理:然后进行垃圾清理:将对象区的活动对象复制到空闲区域,并进行有序的排列,当复制完成后,对象区域与空闲区域进行翻转,空闲区域晋升为对象区域,对象区域为空闲区域
新生代区域很小的,一般1~8M的容量,所以它很容易满,所以,JavaScript 引擎采用对象晋升策略来处理,即只要对象经过两次垃圾回收之后依然继续存活,就会被晋升到老生代区域中。
老生代回收策略(主垃圾回收器)
老生代区域里除了存在从新生代晋升来的存活时间久的对象,当遇到大对象时,大对象也会直接分配到老生代。
所以主垃圾回收器主要保存存活久的或占用空间大的对象,此时采用 Scavenge 算法就不合适了。V8 中主垃圾回收器主要采用
标记-清除法 进行垃圾回收。
主要流程如下:
标记:遍历调用栈,看老生代区域堆中的对象是否被引用,被引用的对象标记为活动对象,没有被引用的对象(待清理)标记为垃圾数据。
垃圾清理:将所有垃圾数据清理掉
内存整理:标记-整理策略,将活动对象整理到一起
- 增量标记
V8 浏览器会自动执行垃圾回收,但由于 JavaScript 也是运行在主线程上的,一旦执行垃圾回收,就要打断 JavaScript 的运行,可能会或多或少的造成页面的卡顿,影响用户体验,所以 V8 决定采用增量标记算法回收:
即把垃圾回收拆成一个个小任务,穿插在 JavaScript 中执行。
vue-router跟vuex怎么混入到vue实例中的
Vuex的双向绑定通过调用 new Vue实现,然后通过 Vue.mixin 注入到Vue组件的生命周期中,再通过劫持state.get将数据放入组件中
Vuex仅仅是Vue的一个插件。Vuex只能使用在vue上,因为其高度依赖于Vue的双向绑定和插件系统。
Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue.mixin,在所有组件的 beforeCreate生命周期注入了设置 this.$store这样一个对象
划重点:1行代码 Vue.mixin
划重点:重点就是一行代码resetStoreVM(this, state)
1 | // src/store.js |
new Vue。通过 Vue自己的响应式系统注入
resetStoreVM是什么呢
1 | function resetStoreVM (store, state, hot) { |
当你再Vue中通过 this 如果调用 store的数据呢?
1 | // 当获取state时,返回以双向绑定的$$sate |
最终是通过this._vm._data.$$state来获取的
- vuex
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
33function applyMixin (Vue) {
var version = Number(Vue.version.split('.')[0]);
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit });
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
var _init = Vue.prototype._init;
Vue.prototype._init = function (options) {
if ( options === void 0 ) options = {};
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit;
_init.call(this, options);
};
}
/**
* Vuex init hook, injected into each instances init hooks list.
*/
function vuexInit () {
var options = this.$options;
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store;
} else if (options.parent && options.parent.$store) {
// Vue 实例才会具有 $store 属性,组件是没有的
console.log('mixin', options)
this.$store = options.parent.$store;
}
}
}
- vue-router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let _Vue = null;
export default class VueRouter {
static install(Vue) {
// 1. 判断当前插件是否已安装
if (VueRouter.install.installed) {
return;
}
VueRouter.install.installed = true;
// 2. 把 Vue 构造函数存储到全局变量中
_Vue = Vue;
// 3. 把创建 Vue 实例时传入的 router 对象注入到所有 Vue 实例上
// 混入
_Vue.mixin({
beforeCreate() {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router;
}
},
});
}
}