最后
整理面试题,不是让大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。
《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》
开源分享:docs.qq.com/doc/DSmRnRG… const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } }
在get方法中先把window.target设置成了this,也就是当前的watcher实例,然后再读一下data.a.b.c的值,这会触发getter。
触发getter就会触发收集依赖的逻辑。而关于收集依赖,会从window.target中读取一个依赖并添加到Dep中。
这就导致,只要先在window.target附一个this,然后再读一下值去触发getter就可以把this主动添加到keypath的Dep中。
依赖注入到Dep中后,每当data.a.b.c的值发生变化时,就会让依赖列表中所有的依赖循环触发update方法,也就是watcher中的update方法。而update方法会执行参数中的回调函数,将value和oldValue传到参数中。
所以,其实不管是用户执行的vm.$watch(‘a.b.c’, (value, oldValue) => {}),还是模板中用到的data,都是通过watcher来通知自己是否需要发生变化。
那么parsePath是怎么读取一个字符串的keypath的,下面来解释一下:
const bailRE = /[^\w.$]/; export function parsePath(path){ if(bailRE.test(path)){ return; } const segments = path.split(".") return function (obj){ for(let i = 0; i < segments.length; i++){ if(!obj){ return; } obj = obj[segments[i]]; } return obj; } }
可以看到,先将keypath用.分割成数组,然后循环数组一层一层去读数据,最后拿到的obj就是keyPath中想要读的数据。
### 2.递归侦测所有key
现在,其实已经可以实现变化侦测的功能了,但是前面介绍的代码只能侦测数据中的某一个属性,我们希望把数据中的所有属性(包括子属性)都侦测到,所以要封装一个Observer类。这个类的作用是将一个数据内的所有属性(包括子属性)都转换成getter/setter的形式,然后去追踪它们的变化:
export class Observer{ constructor(value) { this.value = value; if (!Array.isArray(value)){ this.walk(value) } }
walk(obj){
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++){
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
function defineReactive(data, key, val) { if (typeof val == 'object'){ new Object(val); } let dep = new Dep(); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function(){ dep.depend(); return val; }, set: function(newVal){ if (val == newVal){ return; } val = newVal; dep.notify(); } })
}
在上面的代码中,我们定义了Observer类,它用来将一个正常的object转换成被侦测的object。
然后判断数据的类型,只有object类型的数据才会调用walk将每一个属性转换成getter/setter的形式来侦测变化。
最后,在defineReavtive中新增new Observer(val)来递归子属性,这样我们就可以把data中的所有属性(包括子属性)都转换成getter/setter 来侦测变化。
### 3.关于Object的问题
前面介绍了Object类型数据的变化侦测原理,了解了数据的变化是通过getter/setter来追踪的。也正是由于这种追踪方式,有些语法中即便是数据发生了变化,vue.is也追踪不到。
比如,向Object添加属性:
var vm = new Vue({ el: "#el", template: "#demo-tenplate", methods: { action(){ this.obj.name = "berwin"; } }, data: { obj: {} } })
在action方法中,我们在obj上面新增了name属性,vue无法侦测到这个变化,所以不会向依赖发送通知。
在比如,从obj中删除一个属性:
var vm = new Vue({ el: "#el", template: "#demo-tenplate", methods: { action(){ delete this.obj.name; } }, data: { name: 'berwin' } })
在上面的代码中,我们在action方法中删除了obj中的name属性,而vue无法侦测到这个变化,所以不会向依赖发送通知。
vue通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性,所以才会导致上面的例子中提到的问题。
### 4.总结
变化侦测就是侦测数据的变化,当数据吧发生变化时,要能侦测到并发出通知。
Object可以通过Object.defineProperty将属性转换成getter/setter的形式来追踪变化。读取数据时会触发getter,修改数据时会触发setter。
我们需要在getter中收集哪些依赖使用了数据,当setter被触发时,去通知getter中收集的依赖数据发生了变化。
#### 总结一下
面试前要精心做好准备,简历上写的知识点和原理都需要准备好,项目上多想想难点和亮点,这是面试时能和别人不一样的地方。
还有就是表现出自己的谦虚好学,以及对于未来持续进阶的规划,企业招人更偏爱稳定的人。
万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**
为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。


**前端面试题汇总**
