51信用卡(一面)
在ES5中如何实现继承
小提示:这里我说了很多,从借用构造函数到组合继承到寄生组合继承,但面试官其实最想听到的是寄生组合继承。面试官还追问我具体要如何实现寄生组合继承。当然这里其实问的问题还可以很多,比如ES6的类继承和ES5中的继承有什么区别。
绝对定位
小提示:这个建议大家好好回忆一下,例如子元素是相对父元素的padding、border还是content进行定位之类的,当时面试官问的就这么细。
消抖和节流
小提示:面试官只是问了一下具体的使用场景,没有问实现原理。
简单消抖
function debounce (fn, wait = 1000) {
let timeOutId
return function () {
let context = this
if (timeOutId) {
clearTimeout(timeOutId)
}
timeOutId = setTimeout(() => {
fn.apply(context, arguments)
}, wait)
}
}
带立即执行参数的消抖
function debounceImmediate (fn, wait = 1000, immediate) {
let timeOutId, context, args
const later = (immediate) => setTimeout(() => {
if (!immediate) {
fn.apply(context, args)
timeOutId = context = args = null
}
}, wait)
return function () {
if (!timeOutId) {
timeOutId = later(true)
if (immediate) {
fn.apply(this, arguments)
}
context = this
args = arguments
} else {
clearTimeout(timeOutId)
timeOutId = later(false)
}
}
}
节流
function throttle (fn, wait) {
let timeoutId = null
return function () {
let context = this
if (!timeoutId) {
timeoutId = setTimeout(() => {
fn.apply(context, arguments)
timeoutId = null
}, wait)
}
}
}
Vue中的computed实现原理
小提示:这个问题面试官问的很细,绝对是想问你是否阅读过源码。他首先问
computed的实现原理,其次问了这样一个问题:现在有两个computed计算值,其中一个computed计算值为什么可以依赖另外一个computed计算值。这里顺便将watch的实现原理也贴上。
watch的实现原理
watch的分类:
deep watch(深层次监听)user watch(用户监听)computed watcher(计算属性)sync watcher(同步监听)
watch实现过程:
- watch的初始化在data初始化之后(此时的data已经通过Object.defineProperty的设置成响应式)
- watch的key会在Watcher里进行值的读取,也就是立马执行get获取value(从而实现data对应的key执行getter实现对于watch的依赖收集),此时如果有- immediate属性那么立马执行watch对应的回调函数
- 当data对应的key发生变化时,触发user watch实现watch回调函数的执行
computed运行原理
- computed的属性是动态挂载到vm实例上的,和普通的响应式数据在data里声明不同
- 设置computed的getter,如果执行了computed对应的函数,由于函数会读取data属性值,因此又会触发data属性值的getter函数,在这个执行过程中就可以处理computed相对于data的依赖收集关系了
- 首次计算computed的值时,会执行vm.computed属性对应的getter函数(用户指定的computed函数,如果没有设置getter,那么将当前指定的函数赋值computed属性的getter),进行上述的依赖收集
- 如果computed的属性值又依赖了其他computed计算属性值,那么会将当前target暂存到栈中,先进行其他computed计算属性值的依赖收集,等其他计算属性依赖收集完成后,在从栈中pop出来,继续进行当前computed的依赖收集
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
由于 this.firstName 和 this.lastName (上面是Vue官方示例)都是响应式变量,因此会触发它们的 getter,根据我们之前的分析,它们会把自身持有的 dep 添加到当前正在计算的 watcher 中,这个时候Dep.target就是这个 computed watcher,具体步骤如下:
- data 属性初始化 getter setter
- computed 计算属性初始化,提供的函数将用作属性 vm.fullName 的 getter
- 当首次获取 fullName 计算属性的值时,Dep 开始依赖收集
- 在执行 message getter 方法时,如果 Dep 处于依赖收集状态,则判定firstName和lastName为fullName 的依赖,并建立依赖关系
- 当firstName或lastName 发生变化时,根据依赖关系,触发 fullName 的重新计算
- 如果计算值没有发生变化,不会触发视图更新
通过以上的分析,我们知道计算属性本质上就是一个 computed watcher,也了解了它的创建过程和被访问触发 getter 以及依赖更新的过程,其实这是最新的计算属性的实现,之所以这么设计是因为 Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化才会触发渲染 watcher 重新渲染,本质上是一种优化。
computed计算值为什么还可以依赖另外一个computed计算值
小提示:这个问题当时完全不知道,哎,官方源码的套路太深了......
周期函数有哪些(beforeCreated和created中间都做了什么)
初始化 data、props、computed、watcher、provide。官方源码具体位置src/core/instance/init.js:
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
小结
51信用卡的这次面试其实面试官考察的点还是蛮深入的,问了一些Vue底层源码的实现,总体感觉自己回答的还可以,但是面试官说:你应该去阿里.
