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

一个简单的数据监测 - 类Vue #6

Open
johnsoncheg opened this issue Jun 11, 2018 · 0 comments
Open

一个简单的数据监测 - 类Vue #6

johnsoncheg opened this issue Jun 11, 2018 · 0 comments

Comments

@johnsoncheg
Copy link
Owner

johnsoncheg commented Jun 11, 2018

理解Vue的observer机制,实现一个简单数据观测。

难点解析:

  1. 对象的属性赋值(考虑对象的属性可能也是对象等要素,获取路径)
  2. 数组的内置方法(push, pop, splice ... )
  3. 缓存旧值 -> 当有一个新的赋值操作时,则与旧值做比较,经判断后执行回调

简单的赋值操作监听:

var data = {
  x: 100
}


function callback (val) {
  console.log('val', val)
}

Object.defineProperty(data, 'x', {
  get: function () {
    return data['x']
  },
  set: function (newVal) {
    callback(newVal)
  }
})

data.x = 200

通过Object.defineProperty方法我们能做到非常简单的赋值监听,结合以上的难点解析,慢慢深入实现一个数据监听。

源码:

/*
 *  Object 原型
 */
const OP = Object.prototype;
/*
 *  需要重写的数组方法 OAR 是 overrideArrayMethod 的缩写
 */
const OAM = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];

export class Jsonob{
    constructor(obj, callback){
        if(OP.toString.call(obj) !== '[object Object]'){
            console.error('This parameter must be an object:' + obj);
        }
        this.$callback = callback;
        this.observe(obj);
    }
    
    /**
	 * 监测数据对象
	 * @param   {Object}  obj    [要监测的数据对象]
	 * @param   {Array}   path   [属性路径]
	 */
    observe(obj, path){
        if(OP.toString.call(obj) === '[object Array]'){
            this.overrideArrayProto(obj, path);
        }
        Object.keys(obj).forEach(function(key, index, keyArray){
            var oldVal = obj[key];
            var pathArray = path && path.slice(0);
            if(pathArray){
                pathArray.push(key);
            }else{
                pathArray = [key];
            }
            
            Object.defineProperty(obj, key, {
                get: function(){
                    return oldVal;
                },
                set: (function(newVal){
                    if(oldVal !== newVal){
                        if(OP.toString.call(newVal) === '[object Object]' || OP.toString.call(newVal) === '[object Array]'){
                            this.observe(newVal, pathArray);
                        }
                        this.$callback(newVal, oldVal, pathArray);
                        oldVal = newVal;
                    }
                    
                }).bind(this)
            });
            
            if(OP.toString.call(obj[key]) === '[object Object]' || OP.toString.call(obj[key]) === '[object Array]'){
                this.observe(obj[key], pathArray);
            }
            
        }, this);
        
    }
    
    /**
	 * 重写数组的方法
	 * @param   {Array}   array     [数组字段]
	 * @param   {Array}   path      [属性路径]
	 */
    overrideArrayProto(array, path){
        var originalProto = Array.prototype,
            overrideProto = Object.create(Array.prototype),
            self = this,
            result;

        OAM.forEach(function(method, index, array){
            var oldArray = [];
            
            Object.defineProperty(overrideProto, method, {
                value: function(){
                    oldArray = this.slice(0);
                    
                    var arg = [].slice.apply(arguments);
                    result = originalProto[method].apply(this, arg);
                    
                    self.observe(this, path);
                    self.$callback(this, oldArray, path);
                    
                    return result;
                },
                writable: true,
                enumerable: false,
                configurable: true
            });
        }, this);
        
        array.__proto__ = overrideProto;
        
    }
}
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script type="module">
    import { Jsonob } from './jsonob.js'
    
    var callback = function(newVal, oldVal, path){
      console.log('新值:' + newVal + '----' + '旧值:' + oldVal + '----路径:' + path);
    };
    
    var data = {
        a: 200,
        level1: {
            b: 'str',
            c: [{w: 90}, 2, 3],
            level2: {
                d: 90
            }
        }
    }
    
    var j = new Jsonob(data, callback);
    
    data.level1.c.push(4);
    data.level1.c[0].w = 100;
    data.level1.b = 'sss';
    data.level1.level2.d = 'msn';
  </script>
</body>
</html>

参考:JavaScript实现MVVM之我就是想监测一个普通对象的变化

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

No branches or pull requests

1 participant