您的位置:首页 > 移动开发 > Objective-C

vue 数据绑定实现的核心 Object.defineProperty()

2017-07-23 13:15 951 查看


1. vue追踪数据变化:

把一个普通 JavaScript 对象传给 Vue 实例的 
data
 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为
getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 
setter
 被调用时,会通知 
watcher
 重新计算,从而致使它关联的组件得以更新。



2. VUE数据变化检测:


受现代 JavaScript 的限制(以及废弃 
Object.observe
),Vue 不能检测到对象属性的添加或删除。由于
Vue 会在初始化实例时对属性执行 
getter/setter
 转化过程,所以属性必须在 
data
 对象上存在才能让
Vue 转换它,这样才能让它是响应的。例如:




var vm = new Vue({  data:{  a:1  }})// `vm.a` 是响应的vm.b = 2// `vm.b` 是非响应的
Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property)。然而它可以使用 
Vue.set(object, key, value)
 方法将响应属性添加到嵌套的对象上:
Vue.set(vm.someObject, 'b', 2)
您还可以使用 
vm.$set
 实例方法,这也是全局 
Vue.set
 方法的别名:
this.$set(this.someObject,'b',2)
有时你想向已有对象上添加一些属性,例如使用 
Object.assign()
 或 
_.extend()
 方法来添加属性。但是,添加到对象上的新属性不会触发更新。在这种情况下可以创建一个新的对象,让它包含原对象的属性和新的属性:
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
3. object.defineProperty()

Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

语法

Object.defineProperty(obj, prop, descriptor)

参数

obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性的描述符。

返回值

    被传递给函数的对象。

描述

该方法允许精确添加或修改对象的属性。一般情况下,我们为对象添加属性是通过赋值来创建并显示在属性枚举中(
for...in
 或 
Object.keys
方法), 但这种方式添加的属性值可以被改变,也可以被删除。而使用 Object.defineProperty() 则允许改变这些额外细节的默认设置。例如,默认情况下,使用
 Object.defineProperty() 增加的属性值是不可改变的。

对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。描述符必须是两种形式之一;不能同时是两者。

数据描述符和存取描述符均具有以下可选键值:

configurable
当且仅当该属性的 configurable 为 true 时,该属性
描述符
才能够被改变,也能够被删除。默认为
false
enumerable
当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。
默认为
false
数据描述符同时具有以下可选键值:

value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 
undefined
writable
当且仅当该属性的 writable 为 true 时,该属性才能被
赋值运算符
改变。
默认为false。存取描述符同时具有以下可选键值:

get
一个给属性提供 getter 的方法,如果没有 getter 则为 
undefined
。该方法返回值被用作属性值。默认为
undefined

set
一个给属性提供 setter 的方法,如果没有 setter 则为 
undefined
。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为
undefined
。记住,这些选项不一定是自身属性,如果是继承来的也要考虑。为了确认保留这些默认值,你可能要在这之前冻结
Object.prototype
,明确指定所有的选项,或者将
__proto__
属性指向
null


var obj = {};
var descriptor = Object.create(null); //
没有继承的属性
// 默认没有
enumerable,没有 configurable,没有 writable
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor);


// 显式
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});

// 循环使用同一对象
function withValue(value) {
var d = withValue.d || (
withValue.d = {
enumerable: false,
writable: false,
configurable: false,
value: null
}
);
d.value = value;
return d;
}
// ... 并且 ...
Object.defineProperty(obj, "key", withValue("static"));

// 如果 freeze 可用, 防止代码添加或删除对象原型的属性
// (value, get, set, enumerable, writable, configurable)
(Object.freeze||Object)(Object.prototype);

示例

如果你想知道如何用 binary-flags-like 
var o = {}; // 创建一个新对象

Object.defineProperty(o, "a", { value : 37,
writable : false });

console.log(o.a); // 打印 37
o.a = 25; // 没有错误抛出(在严格模式下会抛出,即使之前已经有相同的值)
console.log(o.a); // 打印 37, 赋值不起作用。

正如上例中看到的,修改一个 non-writable 的属性不会改变属性的值,同时也不会报异常。


Enumerable 特性

属性特性 
enumerable 
定义了对象的属性是否可以在 
for...in
循环和 
Object.keys()

中被枚举。

var o = {};
Object.defineProperty(o, "a", { value : 1, enumerable:true });
Object.defineProperty(o, "b", { value : 2, enumerable:false });
Object.defineProperty(o, "c", { value : 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的enumerable为true

for (var i in o) {
console.log(i);
}
// 打印 'a' 和 'd' (in undefined order)

Object.keys(o); // ["a", "d"]

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false


Configurable 特性

configurable 特性表示对象的属性是否可以被删除,以及除 writable 特性外的其他特性是否可以被修改。

var o = {};
Object.defineProperty(o, "a", { get : function(){return 1;},
configurable : false } );

// throws a TypeError
Object.defineProperty(o, "a", {configurable : true});
// throws a TypeError
Object.defineProperty(o, "a", {enumerable : true});
// throws a TypeError (set was undefined previously)
Object.defineProperty(o, "a", {set : function(){}});
// throws a TypeError (even though the new get does exactly the same thing)
Object.defineProperty(o, "a", {get : function(){return 1;}});
// throws a TypeError
Object.defineProperty(o, "a", {value : 12});

console.log(o.a); // logs 1
delete o.a; // Nothing happens
console.log(o.a); // logs 1

如果 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: