We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
相信玩过vue的都知道,vue的数据和视图都是双向绑定的,也就是说当数据(data)发生更改时,vue会自动将更改diff到视图层上。那么vue是怎么自动检测到他的数据变动的呢?在这个问题上,angluar用的是脏检查(dirty check),也就是轮询检测,性能较低,而knockout用的是ko.observable函数(兼容IE6还要什么自行车),而vue则用的是Object.defineProperty。
ko.observable
Object.defineProperty
其实在很久之前就听说过Object.defineProperty这个属性,但是只知道是个es5新属性(这也就是为什么vue不兼容IE9的原因之一),具体能干什么没有深究。直到上个学期末,考完试后有两个星期的空余时间,于是打算造个mvvm轮子(也就是后来的Zeta),当时深挖vue双向绑定原理的时候也好好研究了一番这个Object.defineProperty。
正如他的名字一样,Object.defineProperty是为对象设置一些默认的属性,如writeable(可写)和getter(访问器)等,也就是说,Object.defineProperty是用作扩展原生对象的一种方法。
writeable
getter
Object.defineProperty(obj, prop, descriptor);
obj
prop
descriptor
描述符
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。
我们先创造一个对象,用作监听:
const obj = { name: 'phenom', age: 20 };
这个对象里有两个属性,一个是name,一个是age。
name
age
接着我们用for in来遍历一下obj`,让其每一个属性都装配上拦截器:
for in来遍历一下
for(let key in obj) { let oldVal = obj[key]; Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: () => { return oldVal; }, set: newVal => { if(newVal != oldVal){ console.log(`${key}从${oldVal}改变为${newVal}`); oldVal = newVal; } } }); }
可以很清楚看到这个拦截器是怎么工作的,首先设置enumerable和configurable都为true(不然怎么被遍历到),然后把当前的值保存到oldVal(这个步骤并不是必要,只是在很多时候都要用到上一次修改的值,这里是为了演示)。然后getter很直接地返回当前的值,在setter里面有一个判断,如果新设置的值不等于当前的值才会把新值赋应用到当前。
enumerable
configurable
true
oldVal
setter
注:上面的let不能直接改成var,这里涉及到js的作用域和闭包问题
console.log(obj.name); console.log(obj.age);
控制台输出: 可以正常获取到,说明getter是没问题的。
之后我们来改动一下obj的属性:
obj.name = 'Marshmallow'; obj.name = 'Nougat'; obj.age = '30'; obj.age = '40';
控制台输出:
十分神奇哈哈,现在setter能够捕获到属性的每一次更改情况。
说了这么多,那么究竟Object.defineProperty能用在什么场景呢?就我平时在写轮子的时候总结出来我用到Object.defineProperty的场景有:
1. 在设计MVVM框架的时候做数据双向绑定。 2. 使对象内的所有属性变成只读(const只能使对象变成只读,不能影响对象内的属性) 3. 限制state(状态)的修改权限,阻止直接赋值修改,限制只能用setState方法修改(在设计类React框架的时候很有用)。
state
setState
The text was updated successfully, but these errors were encountered:
No branches or pull requests
相信玩过vue的都知道,vue的数据和视图都是双向绑定的,也就是说当数据(data)发生更改时,vue会自动将更改diff到视图层上。那么vue是怎么自动检测到他的数据变动的呢?在这个问题上,angluar用的是脏检查(dirty check),也就是轮询检测,性能较低,而knockout用的是
ko.observable
函数(兼容IE6还要什么自行车),而vue则用的是Object.defineProperty
。其实在很久之前就听说过
Object.defineProperty
这个属性,但是只知道是个es5新属性(这也就是为什么vue不兼容IE9的原因之一),具体能干什么没有深究。直到上个学期末,考完试后有两个星期的空余时间,于是打算造个mvvm轮子(也就是后来的Zeta),当时深挖vue双向绑定原理的时候也好好研究了一番这个Object.defineProperty
。Object.defineProperty是什么
正如他的名字一样,
Object.defineProperty
是为对象设置一些默认的属性,如writeable
(可写)和getter
(访问器)等,也就是说,Object.defineProperty
是用作扩展原生对象的一种方法。使用方法
obj
需要定义属性的对象。prop
需被定义或修改的属性名。descriptor
需被定义或修改的属性的描述符。问题来了,
描述符
是个什么东西,有什么用?我们先看看官方定义:官方描述已经很清楚了,我们可以为一个对象单独设置访问器和设置器,限制读写限权或者设置默认的值。这些特性都将十分有用,我们可以重写对象的`getter`和`setter`,拦截对象的读写情况,也就是等于在对象外面包了一层机关,所以也有人将`Object.defineProperty`作为**对象拦截器**。
vue也是通过改写data的`getter`和`setter`,监听data对象里面所有属性的变动。vue在`getter`里面收集所有属性依赖,然后`setter`里面发布更新信息,做到同步更新视图。根据这个思路我们可以尝试做一个简单的对象读写拦截器。
用Object.defineProperty监听对象的读写
我们先创造一个对象,用作监听:
这个对象里有两个属性,一个是
name
,一个是age
。接着我们用
for in来遍历一下
obj`,让其每一个属性都装配上拦截器:可以很清楚看到这个拦截器是怎么工作的,首先设置
enumerable
和configurable
都为true
(不然怎么被遍历到),然后把当前的值保存到oldVal
(这个步骤并不是必要,只是在很多时候都要用到上一次修改的值,这里是为了演示)。然后getter
很直接地返回当前的值,在setter
里面有一个判断,如果新设置的值不等于当前的值才会把新值赋应用到当前。之后我们来走一波试试,首先我们获取`obj.name`:
控制台输出:
![](https://github.com/phenomLi/myBlog/raw/master/photos/WX20171007-162105.png)
可以正常获取到,说明
getter
是没问题的。之后我们来改动一下
obj
的属性:控制台输出:
![](https://github.com/phenomLi/myBlog/raw/master/photos/WX20171007-162201.png)
十分神奇哈哈,现在
setter
能够捕获到属性的每一次更改情况。总结
说了这么多,那么究竟
Object.defineProperty
能用在什么场景呢?就我平时在写轮子的时候总结出来我用到Object.defineProperty
的场景有:1. 在设计MVVM框架的时候做数据双向绑定。
2. 使对象内的所有属性变成只读(const只能使对象变成只读,不能影响对象内的属性)
3. 限制
state
(状态)的修改权限,阻止直接赋值修改,限制只能用setState
方法修改(在设计类React框架的时候很有用)。The text was updated successfully, but these errors were encountered: