• 读vue-0.6-observer.js源码


    实现监听数组方法

    var ArrayProxy = Object.create(Array.prototype),
            methods  = ['push','pop','shift','unshift','splice','sort','reverse'];
        function defProtected(obj, key, val, enumerable, configurable) {
            // 如果是用户添加的方法则不监听
            if (obj.hasOwnProperty(key)) return
            Object.defineProperty(obj, key, {
                value        : val,
                // 不可枚举
                enumerable   : !!enumerable,
                // 不可配置
                configurable : !!configurable
            })
        }
            // 监听原生数组方法
        methods.forEach(function (method) {
            // ArrayProxy监听的对象 ,method监听的方法,第三个返回一个value
            defProtected(ArrayProxy, method, function () {
                // 这里面的this表示当前调用的数组
                var result = Array.prototype[method].apply(this, arguments)
                // 调用数组的__observer__里面的emit方法,触发更新。
                this.__observer__.emit('mutate', this.__observer__.path, this, {
                    method: method,
                    args: slice.call(arguments),
                    result: result
                })
                return result
            }, !hasProto)
        });
    

    我们可以看到在这段代码中并没有对数组进行get和set监听,这也是为什么在vue中给数组直接赋值不会触发更新的主要原因。

    数组remove和replace方法

    var hasProto = ({}).__proto__;
    
    function def(obj, key, val, enumerable, configurable) {
        if (obj.hasOwnProperty(key)) return
        Object.defineProperty(obj, key, {
            value        : val,
            enumerable   : !!enumerable,
            configurable : !!configurable
        })
    }
    
    var ArrayProxy = Object.create(Array.prototype);
    
    // 给数组添加remove和replace方法
    var extensions = {
        remove: function (index) {
            /*
                如果index是一个函数,则调用这个函数并且判断返回值,如果返回值为true则删除,false不删除
                
                比如下面这个,删除index大于5的项
                remove(function(index){
                    return index > 5;
                });
    
             */
            if (typeof index === 'function') {
                var i = this.length,
                    removed = []
                while (i--) {
                    if (index(this[i])) {
                        removed.push(this.splice(i, 1)[0])
                    }
                }
                // 将删除的项返回,返回后为新数组
                return removed.reverse()
            } else {
                // 这个判断是为了实现如果数组项是字符串也能删除
                if (typeof index !== 'number') {
                    index = this.indexOf(index)
                }
                if (index > -1) {
                    return this.splice(index, 1)[0]
                }
            }
        },
        replace: function (index, data) {
            if (typeof index === 'function') {
                var i = this.length,
                    replaced = [],
                    replacer
                while (i--) {
                    replacer = index(this[i])
                    /* 
        
            这里之所以不是直接判断if(replacer)是因为这里的目的就是实现替换功能,而如果值不是undefined说明用户有返回值而只要有返回值就应该给它替换。
    
    
                    */
                    if (replacer !== undefined) {
                        replaced.push(this.splice(i, 1, replacer)[0])
                    }
                }
                return replaced.reverse()
            } else {
                if (typeof index !== 'number') {
                    index = this.indexOf(index)
                }
                if (index > -1) {
                    return this.splice(index, 1, data)[0]
                }
            }
        }
    }
    
    for (var method in extensions) {
        // 给ArrayProxy原型添加remove,replace方法,并且监听
        def(ArrayProxy, method, extensions[method], !hasProto)
    }
    

    实现监听对象方法

    /**
     *  根据类型观察对象,入口
     */
    function watch (obj, path, observer) {
        var type = typeOf(obj)
        if (type === 'Object') {
            watchObject(obj, path, observer)
        } else if (type === 'Array') {
            watchArray(obj, path, observer)
        }
    }
    
    /**
     *  监听对象变化,但不监听对象中开头为$和_的属性和方法,入口
     */
    function watchObject (obj, path, observer) {
        for (var key in obj) {
            var keyPrefix = key.charAt(0)
            if (keyPrefix !== '$' && keyPrefix !== '_') {
                bind(obj, key, path, observer)
            }
        }
    }
    
    /**
     *  监听数组方法,并将其原型挂载到ArrayProxy上,入口
     *  ArrayProxy方法实现了一些变异的数组方法以及扩展,这是实现对数组方法监听的基础
     */
    function watchArray (arr, path, observer) {
        def(arr, '__observer__', observer)
        observer.path = path
        if (hasProto) {
            arr.__proto__ = ArrayProxy
        } else {
            for (var key in ArrayProxy) {
                def(arr, key, ArrayProxy[key])
            }
        }
    }
    
    /*
     *  具体实现对象监听的方法  
    */
    function bind (obj, key, path, observer) {
        var val       = obj[key],
            watchable = isWatchable(val),
            values    = observer.values,
            fullKey   = (path ? path + '.' : '') + key
        values[fullKey] = val
        // 触发set事件
        observer.emit('set', fullKey, val)
        Object.defineProperty(obj, key, {
            enumerable: true,
            get: function () {
                // only emit get on tip values
                if (depsOb.active && !watchable) {
                    observer.emit('get', fullKey)
                }
                return values[fullKey]
            },
            set: function (newVal) {
                values[fullKey] = newVal
                ensurePaths(key, newVal, values)
                observer.emit('set', fullKey, newVal)
                // 被赋值,监听新对象
                watch(newVal, fullKey, observer)
            }
        })
        watch(val, fullKey, observer)
    }
    
    /**
     *  只监听数组和对象
     */
    function isWatchable (obj) {
        var type = typeOf(obj)
        return type === 'Object' || type === 'Array'
    }
    
  • 相关阅读:
    thinkphp6查询表达式使用between问题
    机器学习纸质作业1
    磁盘挂载
    SQL Server开启READ_COMMITTED_SNAPSHOT
    视觉开发-相机镜头选型
    使用logstash出现报错com.mysql.jdbc.Driver not loaded. Are you sure you've included the correct jdbc driver in :jdbc_driver_library
    linux安装tomcat(转自https://blog.csdn.net/fukai8350/article/details/80467224)
    linux 安装java(转自https://www.cnblogs.com/wjup/p/11041274.html)
    如何统计自动化测试用例的ROI 【投入产出比/投资回报率】
    老男孩老师的博客地址
  • 原文地址:https://www.cnblogs.com/pssp/p/6752234.html
Copyright © 2020-2023  润新知