Vue双向绑定原理(二)访问器属性defineProperty()

来源:互联网 发布:淘宝联盟的东西便宜吗 编辑:程序博客网 时间:2024/04/27 01:15

访问器属性的介绍

参考资料:《JavaScript高级程序设计》(第三版)第六章

js的对象有两种属性: 数据属性和访问器属性。

1.数据属性

数据属性包含一个数据值的位置。这个位置可以读取和写入值。数据属性也就是我们最常见的对象属性。数据属性有4个描述他行为的特性:

  • Configurable: 能否用delete删除属性从而重新定义属性。默认为true
  • Enumerable: 能否通过for-in遍历,即是否可枚举。默认为true
  • Writable: 是否能修改属性的值。默认为true
  • Value: 包含这个属性的数据值,读写属性的时候其实就在这里读写。默认为undefined

要修改属性的上述4个默认特性,就必须使用ECMAScript的Object.defineProperty()方法,该方法包含3个参数:属性缩在的对象,属性名,描述符对象。描述符对象的属性必须在上述4个属性中。例如:

var person = {};Object.defineProperty(person,"name",{    writable: false,    value: "Nicholas"});alert(person.name);   // "Nicholas"person.name = "Tom";alert(person.name);  // "Nicholas"

上例创建了一个不可写的name属性并赋值。所以无法修改。

注意,一旦把Configurable属性设置为false,就无法再将其变回true了,此时再想修改特性,就都会报错了。

2.访问器属性

访问器属性不包含数据值,他们包含一对getter和setter函数(非必须)。在读写访问器属性的值的时候,会调用相应的getter和setter函数,而我们的vue就是在getter和setter函数中增加了我们需要的操作。

访问器属性有以下4个特性:

  • Configurable: 能否用delete删除属性从而重新定义属性。默认true
  • Enumerable: 能否通过for-in遍历,即是否可枚举。默认true
  • get: 读取属性时调用的函数,默认undefined
  • set: 写入属性时调用的函数,默认undefined
var book = {    _year: 2004,    edition: 1};Object.defineProperty(book,"year",{    get: function(){        return this._year;    },    set: function(newValue){        if(newValue>2004){            this.year= newValue;            this.edition += new Value-2004;        }    }});book.year = 2005;alert(book.edition); //2

上例创建了一个book对象,并定义了两个默认属性 _year 和 edition 。_year前边的下划线表示只能通过对象方法访问的属性。而访问器属性year则包含getter和setter函数。修改year属性,会导致_year和edition改变。这就是访问器属性的常用方式,即设置一个属性值会导致其他属性发生变化。

当然,不一定非要同时制定getter和setter,只有getter则不能写,只有setter则不能读。

在Vue中的作用

Vue会遍历实例的data属性,把每一个data都设置为访问器,然后在该属性的getter函数中将其设为watcher,在setter中向其他watcher发布改变的消息。这样,配合发布/订阅模式,改变其中的一个值,会发布消息,所有的watcher会更新自己,这些watcher也就是绑定在dom中的显示信息,比如 v-text=”year” 和 {{ year }} 这些节点。从而达到改变浏dom,在浏览器中实时变化的效果,代码如下:

   //遍历传入实例的data对象的属性,将其设置为Vue对象的访问器属性    function observe(obj,vm){        Object.keys(obj).forEach(function(key){            defineReactive(vm,key,obj[key]);        });    }    //设置为访问器属性,并在其getter和setter函数中,使用订阅发布模式。互相监听。    function defineReactive(obj,key,val){        //这里用到了观察者(订阅/发布)模式,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。        //实例化一个主题对象,对象中有空的观察者列表        var dep = new Dep();        //将data的每一个属性都设置为Vue对象的访问器属性,属性名和data中相同        //所以每次修改Vue.data的时候,都会调用下边的get和set方法。然后会监听v-model的input事件,当改变了input的值,就相应的改变Vue.data的数据,然后触发这里的set方法        Object.defineProperty(obj,key,{            get: function(){                //Dep.target指针指向watcher,增加订阅者watcher到主体对象Dep                if(Dep.target){                    dep.addSub(Dep.target);                }                return val;            },            set: function(newVal){                if(newVal === val){                    return                }                val = newVal;                //console.log(val);                //给订阅者列表中的watchers发出通知                dep.notify();            }        });    }    //主题对象Dep构造函数    function Dep(){        this.subs = [];    }    //Dep有两个方法,增加观察者  和  发布消息    Dep.prototype = {        addSub: function(sub){            this.subs.push(sub);        },        notify: function(){            this.subs.forEach(function(sub){                sub.update();            });        }    }

Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

0 0
原创粉丝点击