实现 MVVM (一) - Object.defineProperty 的用法

本文是我通过已经知晓的 Vuejs 实现原理来简单实现 MVVM 的系列博文的第一篇,主要总结归纳了 Object.defineProperty() 的方法特性。

动机 (写在前面)

之前在学习构建自己轮子组件库时,被反复的提到了 MVVM 核心实现方法的问题,其中包括 Vuejs 的原理。逛过一些技术社区。一些前辈和大佬给出了建议。初级水平的人并不推荐去看 Vuejs 的源码,截止到今天,Vuejs 2.5.17 版本的源码高达 10978 行。因此初级水平的人看源码性价比也许真的并不高。一些前辈和大牛给出的建议是,Vuejs 的实现原理尤雨溪已经说得很明白了。建议大家可以使用自己的方式去简单模拟一下 MVVM 的思想,也许可能实现得并不优雅。但这样做比头铁的看源码来得更有价值和意义。

Object.defineProperty

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

1
Object.defineProperty(obj, prop, descriptor)

参数说明(三个参数都是必须的):

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

返回值为传入参数的对象,即第一个参数 obj


使用方法

以下三种方法都可以用来定义/修改一个对象属性,其中包含 Object.defineProperty() 方法。

1
2
3
4
5
6
7
8
var obj = {}
obj.name = 'evenyao'
obj['age'] = 27
Object.defineProperty(obj, 'intro', {
value : 'hello world'
})

console.log(obj) // {name: 'evenyao', age: 27, intro: 'hello world'}


configurable

configurable 是该方法传参中最后一项 descriptor 中的属性描述符。configurable 的值设置为 false 后(如果没设置,默认就是 false)。以后就不能再次通过 Object.defineProperty() 方法修改属性,也无法删除该属性。如果configurable 的值设置为 true 后,能删除该属性,但也不能修改。

举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {}
Object.defineProperty(obj, 'intro', {
configurable: false,
value : 'hello world'
})
obj.intro = 'i wanna change it'
console.log(obj.intro) // "hello world"
delete obj.intro // false, 删除失败
console.log(obj.intro) // "hello world"

Object.defineProperty(obj, 'name', {
configurable: true,
value : 'i wanna change it'
})
delete obj.intro // true , 成功删除


enumerable

enumerable 也是该方法传参中最后一项 descriptor 中的属性描述符。设置 enumerable 属性为false 后,遍历对象的时候会忽略当前属性(如果未设置,默认就是 false 不可遍历)。

举例说明:

1
2
3
4
5
6
7
8
9
var obj = {name: 'evenyao'}
Object.defineProperty(obj, 'age', {
enumerable: false,
value: 27
})

for(var key in obj){
console.log(key) // 只输出 'name', 不输出'age'
}


value 和 writable

valuewritable数据描述符,具有以下可选键值:

  • value: 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined
  • writable: 当且仅当该属性的 writabletrue 时,该属性才能被赋值运算符改变。但不能删除。该属性默认为 false

举例说明:

1
2
3
4
5
6
7
8
var obj = {name: 'evenyao'}
Object.defineProperty(obj, 'age', {
value: 27,
writable: false
})

obj.age = 26
console.log(obj.age) // 27, writable为 false 时,修改对象的当前属性值无效


区别

configurable: truewriable: true 的区别是,前者是设置属性能删除,后者是设置属性能修改


get 和 set

getset存取描述符,有以下可选键值:

  • get: 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined
  • set: 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = {}
var age
Object.defineProperty(obj, 'age', {
get: function(){
console.log('get age...')
return age
},
set: function(val){
console.log('set age...')
age = val
}
})

obj.age = 100 // 'set age...'
console.log(obj.age) // 'get age...', 100



值得一提的是这个案例和之前我在 从「闭包」到 思考人生 那篇文章里面封装的那个闭包函数(中文的案例2)是很相似的。事实上在我看来,他们所达到的效果是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var Age = (function(){
var age = undefined
function set(s){
age = s
console.log('set age...')
}
function get(){
console.log('get age...')
return age
}
return {
set: set,
get: get
}
})()

Age.set(100) // 'set age...'
Age.get() // 100



数据描述符存取描述符 是不能同时存在的。如果同时存在,代码就会报错。

例如下面例子,就是不被允许的。这段代码同时出现了 valueget/ set。所以会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {}
var age
Object.defineProperty(obj, 'age', {
value: 27,
get: function(){
console.log('get age...')
return age
},
set: function(val){
console.log('set age...')
age = val
}
})

本文结束  感谢您的阅读
0%