Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【面经】字节跳动 二面9:实现简易版 MVVM #5

Open
liam61 opened this issue Mar 20, 2019 · 2 comments
Open

【面经】字节跳动 二面9:实现简易版 MVVM #5

liam61 opened this issue Mar 20, 2019 · 2 comments
Labels
面经 面经

Comments

@liam61
Copy link
Owner

liam61 commented Mar 20, 2019

实现简易版 MVVM

关键词:Object.defineProperty

class Observer {
  constructor(data) { this.data = data; }

  observe(prop, cbObj) { // 调用了 observe 才会对属性 prop 进行观察
    if (!prop || typeof prop !== 'string') return;
    this.defineReactive(this.data, prop, this.data[prop], cbObj);

    // 由于 new Observer() 出的对象没有 data.a 属性,必须通过 data.data.a 调用,熟悉 vue 的应该知道这一点
    // 为了方便我们直接调用 data.a,把 this.data 的属性代理到 this 上
    this.proxyData(prop, this.data);
  }

  defineReactive(obj, key, value, { getCallback, setCallback }) { // 抽离的对属性进行监控的方法
    Object.defineProperty(obj, key, {
      get() {
        getCallback(value);
        return value;
      },
      set(newValue) {
        if (newValue !== value) {
          setCallback(newValue, value);
          value = newValue;
        }
      }
    });
  }

  proxyData(key, data) {
    Object.defineProperty(this, key, {
      get() { return data[key]; }, // 对调用 data.a 进行劫持,返回内部的 data[key]
      set(newValue) { data[key] = newValue; } // 对 data.a = 2; 进行劫持,实际上的在操作内部的 data[key] = 2;
    });
  }
}

// 测试
const data = new Observer({ a: 1, b: 2 });

const callbackObj = {
  getCallback(value) { console.log(`get: value: ${value}`); },
  setCallback(newValue, oldValue) { console.log(`set: newValue: ${newValue}, oldValue: ${oldValue}`); }
}
data.observe("a", callbackObj);
data.a; // console: 1
data.a = 2; // console: 2 1
data.b; // undefined 因为没有进行 data.observe('b', ...)
@liam61 liam61 added the 面经 面经 label Mar 20, 2019
@liam61 liam61 changed the title 【面经】字节跳动 一面7(3):深度克隆任何基本类型 【面经】字节跳动 二面9:实现简易版 MVVM Mar 21, 2019
@anwenyao
Copy link

冒昧的说一句哈 ,看了下题目,如果按照原题目的话,感觉这个您这个有一点点小问题 就是
const data = new Observer({ a: 1, b: 2 });
这句之后,直接访问data.adata.b应该是能够得到值的。
题目:

const data = new Observer({ a: 1 });
console.log(data.a); // 1 <---这里

但是您这个在构造函数中没有把{ a: 1, b: 2 }的属性赋值给实例 直接访问data.a/b就是undefined。
参照了您的思路,我自己也写了一个,感觉也有点问题。有什么不对的地方欢迎指出。

class Observer {
    constructor(data){
        this.data = data;
        let keys = Object.keys(data);
        this.observer = {};//调用了$on 的对象的属性
        for(let key of keys){
            Object.defineProperty(this,key,{
                set(val){
                    this.$emit(key,this.data[key],val);
                    this.data[key] = val;
                },
                get(){
                    return this.data[key];
                }
            })
        }
    }

    $on(name,callBack) {
        if(!this.observer[name]){
            this.observer[name] = callBack;
        }
        
        
    }
    
    $emit(name,...args){
        if(this.observer[name]){
            this.observer[name](...args);
            return;
        }
        return;
    }

  }

  let data = new Observer({a:1,b:2});
  console.log(data.a);//1
  data.$on('a',(newVal,oldVal)=>{
      console.log(`newValue:${newVal},oldValue:${oldVal}`);
  });
  data.a = 2;//newValue:1,oldValue2
  data.b = 4;
  console.log(data.b);//4
  data.$on('b',(newVal,oldVal)=>{
    console.log(`第一次:${newVal},第二次:${oldVal}`);
});
  data.b = 5;//第一次:4,第二次:5

最后,非常感谢您的面经!

@liam61
Copy link
Owner Author

liam61 commented Mar 28, 2019

@anwenyao 你这样理解是对的,当时面试时我也是这个思路。写面经时想尽量简单些,就直接以 显示监听属性后才数据劫持 为基准,如 data.observe("a", callbackObj); data.b; // undefined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
面经 面经
Projects
None yet
Development

No branches or pull requests

2 participants