由Vue引起的getter和setter思考

公司的新项目决定使用Vue.js来作,当我打印出Vue实例下的data对象里的属性时,发现了一个有趣的事情:vue

 

它的每一个属性都有两个相对应的get和set方法,我觉的这是画蛇添足的,因而去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理彻底不一样,Angular是用的数据脏检测,当Model发生变化,会检测全部视图是否绑定了相关数据,再更改视图。而Vue使用的发布订阅模式,是点对点的绑定数据。缓存

Vue的数据绑定只有两个步骤,compile=>link。框架

我一直在想,vue是经过什么去监听用户对Model的修改,直到我发现Vue的data里,每一个属性都有set和get属性,我才明白过来。函数

在平时,咱们建立一个对象,并修改它的属性,是这样的:this

    var obj = {
        val:99
    }
    obj.val = 100;
    console.log(obj.val)//100

没有任何问题,可是若是要你去监测,当我修改了这个对象的属性时,要去作一些事,你会怎么作?spa

这就要用到getter和setter了。prototype

假设我如今要给一个码农对象添加一个name属性,并且每次更新name属性时,我要去完成一些事,咱们能够这样作:双向绑定

    var Coder = function() {
        var that = this;
        return {
            get name(){
                if(that.name){
                    return that.name
                }
                return '你尚未取名'
            },
            set name(val){
                console.log('你把名字修成了'+val)
                that.name = val
            }
        }
    }
    var isMe = new Coder()
    console.log(isMe.name)
    isMe.name = '周神'
    console.log(isMe.name)
    console.log(isMe)

输出:code

你会发现这个对象和最上面的Vue中的data对象,打印出来的效果是同样的,都拥有get和set属性。对象

咱们来一步步分析下上面的代码,颇有趣。

咱们先建立一个对象字面量:

var Coder = function() {...}

再把this缓存一下:

var that = this;

接下来是最重要的,咱们return了一个对象回去:

{

  get name(){...},

  set name(val){...}

}

 

顾名思义,get为取值,set为赋值,正常状况下,咱们取值和赋值是用obj.prop的方式,可是这样作有一个问题,我如何知道对象的值改变了?因此就轮到set登场了。

你能够把get和set理解为function,固然,只是能够这么理解,这是彻底不同的两个东西。

接下来建立一个码农的实例,isMe;此时,isMe是没有name属性的,当咱们调用isMe.name时,咱们会进入到get name(){...}中,先判断isMe是否有name属性,答案是否认的,那麽就添加一个name属性,并给它赋值:"你尚未取名";若是有name属性,那就返回name属性。

看到这里你必定知道get怎么使用了,对,你能够把get当作一个取值的函数,函数的返回值就是它拿到的值。

我感受比较重要的是set属性,当我给实例赋值:

isMe.name="周神"

此时,会进入set name(val){...};形参val就是我赋给name属性的值,在这个函数里,我就能够作不少事了,好比双向绑定!由于这个值的每次改变都必须通过set,其余方式是改变不了它的,至关于一个万能的监听器。

还有另外一种方法能够实现这个功能。

ES5的对象原型有两个新的属性__defineGetter__和__defineSetter__,专门用来给对象绑定get和set。能够这样书写:

    var Coder = function() {
    }
    Coder.prototype.__defineGetter__('name', function() {
        if (this.name) {
            return this.name
        }else{
            return '你尚未取名'
        }
    })
    Coder.prototype.__defineSetter__('name', function(val) {
        this.name = val
    })
    var isMe = new Coder()
    console.log(isMe.name)
    isMe.name = '周神'
    console.log(isMe.name)
    console.log(isMe)

效果是同样的,建议使用下面这种方式,由于是在原型上书写,因此能够继承和重用,最近想写点小框架,才发现知识不够用,你们一块儿加油吧!