JS中的双向数据绑定及Object.defineProperty方法

双向数据绑定的常规实现方式

  • 脏检查
  • 观察机制
  • 封装属性访问器

封装属性访问器

在php中有 魔术方法 这样一种概念,比如php中的 __get() 和 __set() 方法。在javascript中也有类似的概念,不过不叫魔术方法,而是叫做访问器。我们来看个示例代码,

var data = {
    name: "erik",
    getName: function() {
        return this.name;
    },
    setName: function(name) {
        this.name = name;
    }
};

从上面的代码中我们可以管中窥豹,比如 data 中的 getName() 和 setName() 方法,我们可以简单的将其看成 data.name 的访问器(或者叫做 存取器 )。
其实,针对上述的代码,更加严格一点的话,不允许直接访问 data.name 属性,所有对 data.name 的读写都必须通过 data.getName() 和 data.setName() 方法。所以,想象一下,一旦某个属性不允许对其进行直接读写,而必须是通过访问器进行读写时,那么我当然通过重写属性的访问器方法来做一些额外的情,比如属性值变更监控。使用属性访问器来做数据双向绑定的原理就是在此。
这种方法当然也有弊端,最突出的就是每添加一个属性监控,都必须为这个属性添加对应访问器方法,否则这个属性的变更就无法捕获。
Object.defineProperty 方法
国产mvvm框架avalon.js实现数据双向绑定的原理就是属性访问器。不过它当然不会像上述示例代码一样原始。它使用了ECMAScript5.1(ECMA-262)中定义的标准属性 Object.defineProperty 方法。针对国内行情,部分还不支持 Object.defineProperty 低级浏览器采用VBScript作了完美兼容,不像其他的mvvm框架已经逐渐放弃对低端浏览器的支持。
Object.defineProperty 方法提供了一种直接的方式来定义对象属性或者修改已有对象属性。其方法原型如下,
Object.defineProperty(obj, prop, descriptor)
其中,

  • obj待修改的对象
  • prop 待修改的属性名称
  • descriptor待修改属性的相关描述
    descriptor 要求传入的对象
/**
 * @{param} descriptor
 */

{
    configurable: false,//属性是否可以配置
    enumerable: false,//属性是否可枚举
    writable: false,//属性是否可重写
    value: null,//属性的默认值
    set: undefined,//属性的重写器(暂且这么叫)。一旦属性被重新赋值,此方法被自动调用
    get: undefined//属性的读取器(暂且这么叫)。一旦属性被访问读取,此方法被自动调用
}

下面来一段示例代码:

var o = {};

Object.defineProperty(o, 'name', {
    value: 'erik'
});

console.log(Object.getOwnPropertyDescriptor(o, 'name')); // Object {value: "erik", writable: false, enumerable: false, configurable: false}

Object.defineProperty(o, 'age', {
    value: 26,
    configurable: true,
    writable: true
});

console.log(o.age); // 26

o.age = 18;
console.log(o.age); // 18. 因为age属性是可重写的

console.log(Object.keys(o)); // []. name和age属性都不是可枚举的

Object.defineProperty(o, 'sex', {
    value: 'male',
    writable: false
});

o.sex = 'female'; // 这里的赋值其实是不起作用的
console.log(o.sex); // 'male';

delete o.sex; // false, 属性删除的动作也是无效的

经过上述的示例,正常情况下 Object.definePropert() 的使用都是比较简单的。
不过还是有一点需要额外注意一下, Object.defineProperty() 方法设置属性时,属性不能同时声明访问器属性( setget )和 writable 或者value 属性。 意思就是,某个属性设置了 writable 或者value 属性,那么这个属性就不能声明 getset 了,反之亦然。
因为 Object.defineProperty() 在声明一个属性时,不允许同一个属性出现两种以上存取访问控制。 示例代码:

var o = {},
    myName = 'erik';

Object.defineProperty(o, 'name', {
    value: myName,
    set: function(name) {
        myName = name;
    },
    get: function() {
        return myName;
    }
});

上面的代码看起来貌似是没有什么问题,但是真正执行时会报错,报错如下,

TypeError: Invalid property.  A property cannot both have accessors and be writable or have a value, #<Object>

因为这里的 name 属性同时声明了 value 特性和 set 及 get 特性,这两者提供了两种对 name 属性的读写控制。这里如果不声明 value 特性,而是声明 writable 特性,结果也是一样的,同样会报错。

http://www.tuicool.com/articles/nA36neR

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值