MVVM面试必备

面试回答思路:

基础和核心 Object.defineProperty()的作用、数据页面响应式实现+es5/vue2.x缺点➕+vue3.0/es6/Proxy替换➕ 主要靠内部来实现:如图框框名字+依次说出每个是干嘛的+他们之间的关系➕双绑缺点(失效)

object.deineProperty()相关知识必备⛽️

优点👍:
能够实现对象的属性监听(引申问题👇)
兼容性好,IE9+ 
可通过改变自身的代码(递归)来丰富自身功能,向Proxy看齐


缺点❎:
 1.页面只使用了obj的其中a属性,当修改obj中的b属性时,也是执行。----无脑执行工作,性能低,类似于React Hooks中useMemo方法
 2: 只能监听到基本数据类型的变化,无法监听到引用数据类型的变化-- 功能太片面 验证👇
 3: 像push、length、pop 这些特殊方法确实不能触发setter,这跟obd()其内部实现有关--八字不合
 4: 不想暴露...忘了➕--所以被vue3.x的proxy和Reflect代替
 5: 没有拦截方法

如何封装一个监听属性的方法?
答:外层再套个函数即可https://blog.csdn.net/mapbar_front/article/details/80472395


层级深的obj如何解决?
递归遍历:引用数据类型->处理成基本数据类型
---------------------------------------

想通过object.deineProperty()实现一个obj对象里所有属性值的数据变化,如何实现?

答:遍历👇  详情🔎https://blog.csdn.net/lyh6665/article/details/107929324

function observer(obj) {
  if (typeof obj !== 'object' || obj == null) {
    return
  }
  for (const key in obj) {
    // 给对象中的每一个方法都设置响应式
    defineProperty(obj, key, obj[key])
  }
}

---------------------------------------

相关属性了解下:
let obj = {}
 Object.defineProperty(obj, 'name', {
  configurable: true, // 可删除
  enumerable: true,	//可枚举
  writable: true,	//可修改
  value: '码不停息'
});

需要注意的是:value,writable 和get,set不能同时进行配置

---------------------------------------------------------------------------

#缺点1-代码实现¥:

<body>
  <span id="name"></span>
<body>
<script>
 var data = {
    name: '小红',
    age:18
 };
 
  Object.defineProperty(data, 'name', { 
    get: function(){},
    set: function(newValue){ // 页面响应处理
      document.getElementById('name').innerText = newValue
      data.name = value
    },
    enumerable: true,
    configurable: true
  });
  // 页面DOM listener
  document.getElementById('name').onchange = function(e) {
    data.name = e.target.value;
  }
</script>

-------------------------------------------------------------------------------

#缺点2👆
 
验证🤔️: 在控制台把初始data中,name:'小明' -->改成引用类型:name: {}-->再给{}中添加元素,此时object.deineProperty()中set就监听不到此变化了。
解决方案🌹:typeof验证值的类型+引用类型:先递归遍历成基本类型


新问题🤔️: 下面几种对数组的操作方法,均没有触发数组的setter,页面没有变化响应。
  1.操作数组的第2个下标
  2.改变list的length
  3.push()

解决方案🌹:结论:
 1.直接修改数组中已有的元素是可以被object.deineProperty()监听到的。
 2.像push、length、pop 这些特殊方法确实不能触发setter,这跟object.deineProper的内部实现有关
 3.改变超过数组长度的下标的值时,值变化是不能监听到的。


综上:Object.defineProperty监听不存在的数组元素,并且通过一些能造成数组的方法造成数组改变也不能监听到。

vue2.x源码结论🐈:
Vue2.x中并没有实现将已存在的数组元素做监听,而是去监听造成数组变化的方法,触发这个方法的同时去调用挂载好的响应页面方法,达到页面响应式的效果。


接👆引申问题🤔️:为什么不用Object.defineProperty去监听数组中已存在的元素变化?

尤雨溪答🌹:不建议给每一个数组元素都绑定上监听,受益和消耗很小,不成正比。


详情见🔎:CSDN关注:全栈者/博文链接🔗:
https://blog.csdn.net/qq_27053493/article/details/105355319?spm=1001.2014.3001.5501
  






---------------------------接上文👆-------------------------------
Proxy优点👍:
  1.可以直接监听对象而非属性;明确表示可以监听到数组以及各种数据类型的变化;

  2.可满足此场景要求:
    有个obj对象,不想将自身所有属性完全对外暴露出去,想做一层在原对象操作前的拦截、检查、代理。

  3.可满足此场景要求:异步触发 初始data里的数据变化

  4.有多达13种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等。
    Object.defineProperty 就没有拦截方法...

  5.Proxy 返回的是一个新对象,(类似map)我们可以只操作新的对象达到目的,
    而 Object.defineProperty 只能在遍历对象时,操作原属性;

  6.Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;


Proxy缺点❎: 存在浏览器兼容性问题,而且无法用 polyfill 磨平。所以只能在Vue3.0中才能用。

Proxy&生命周期的交集:
  beforeCreate
  created
  beforeMount
  render:会生成一个Watcher实例: 就是初始化一个模版地方,用来展示data/computed实例的数据。(vue中的组件在挂载前,都会基于组件的render函数,生成watcher并执行render函数 来进行渲染)
  mounted(挂载)

--------Proxy和defineProperty一比,defineProperty功能弱爆了。哈哈哈😂----------------

const myObj = {
  _id: '我是myObj的ID',
  name: 'mvvm',
  age: 25
}
 
const myProxy = new Proxy(myObj, {
  get(target, propKey) {
    if (propKey === 'age') {
      console.log('年龄很私密,禁止访问');
      return '*';
    }
    return target[propKey];
  },
  set(target, propKey, value, receiver) {
    if (propKey === '_id') {
      console.log('id无权修改');
      return;
    }
    target[propKey] = value + (receiver.time || '');
  },
 
});
 
myProxy._id = 34;
// id无权修改
console.log(`age is: ${myProxy.age}`);
//年龄很私密,禁止访问
// age is: *
 
myProxy.name = 'my name is Proxy';
console.log(myProxy);
// { _id: '我是myObj的ID', name: 'my name is Proxy', age: 25}
 
 
const newObj = {
  time: ` [${new Date()}]`,
};
// 原对象原型链赋值
Object.setPrototypeOf(myProxy, newObj);
myProxy.name = 'my name is newObj';
console.log(myProxy.name);
//my name is newObj [Thu Mar 19 2020 18:33:22 GMT+0800 (GMT+08:00)]
Observer是观察者










Dep角色🎭:订阅收集和发布者。


Dep相关的用法的作用?

new Dep()  ---实例化
dep.notity() ---- 当数据发生变化时调用,会去通知订阅的watcher进行更新
if(Dep.target){ dep.depend() } -------- 在此处将当前watcher加入到dep中
dep.addSub(new Watcher()) ------
dep.subs.push(watcher) ------dep实例内有个subs数组,专门按依赖分类,分别存储数据
Dep.target = target; --- 将targetwatcher实例存进Dep
Dep.target ===Watcher实例化对象: 通知订阅的watcher更新
Dep.prototype.notify = function notify () {}
Dep.prototype.removeSub = function removeSub (sub) {};// 取消订阅
 
// 依据MVVM双绑原理图👇,Dep和Watcher互相可以改变的。
Dep通知Watcher进行变化
Warcher通知Dep新增订阅者

watcher是作为订阅者的角色(即new Vue实例data/computed数据展示的地方)


Watcher相关的用法的作用?
pushTarget(this); 设置当前watcher到全局变量上去  
watcher.addDep(); // 订阅者想新增一块地方来展示数据,告诉dep来add


Watch

1.是什么?
    是一个.{ 键名:键值}键名:被观察的表达式;   键值:回调函数/方法名/对象
2.分类?(3种)
  渲染watcher、computed watcher、➕
3.干什么的?
    用于观察和监听页面上的vue实例
4.啥时候用?
    适用于在数据变化的同时执行异步操作(并发)
    适用于比较大的开销
5.代码实现?
...

2张图简单要记得

例子: new Vue()实例,data(){}有数据并应用在了dom上,computed:{return a*b}也应用在了vue模版上,

很常见:脑海中必须反应出的知识点#:

思路1: 监听数据变化->watcher->共3种、并判断出有哪几种(render watcher和computed watcher)->管理watcher->任务队列queenWatcher->func queueWatcher(){} -->特点:重复的 watcher 知会被压入一次;在一个事件循环中 触发了多次的 watcher 只会被压入队列一次;—【内部真正参与者】

思路2:  监听数据变化->Object.defineProperty() —是【基础核心】MVVM/vue原理

为啥要挂在原型上?

Dep.prototype.notify = function notify () {}

Watcher.prototype.update = function update () {}、

Vue2.x: Object.defineProperty()

Vue3.x: 被Polify替换

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值