很久之前就想写一点自己对 Vue 的理解,虽然谈不上深刻,但是毕竟对所用的东西有一点好奇,如果你跟我一样,对 Vue 响应式原理有那么一点好奇,那么我们一起来研究一下吧。
在谈 Vue 响应式原理之前,我们需要先了解一下 ES5 的 Object 的一个属性 defineProperty。下面我们来看一段官方的介绍
The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property on an object, and returns the object.
直接在一个对象上定义一个新的属性,或修改一个已经存在的属性。这个方法会返回该对象。
语法:
Object.defineProperty(obj, prop, descriptor)
参数说明:
@params obj // 目标对象 type: Obejct
@params prop // 需要定义的属性 type: String
@params descriptor // 定义的属性所拥有的特性 type: Obejct
通过简单的介绍可以看看到,也就是定义了个对象的某个属性,但是其中的奥秘主要在第三个参数 descriptor。 我们看一下第三个参数可以设置的属性:
- value
- writable
- get ,set
- configurable
- enumerable
分别介绍一下每个属性:
1. value
属性的值, 默认为undefined。例如:
var someOne = {}
Object.defineProperty(someOne, 'name', {
value : 'cover'
})
someOne.name // cover
从表面上看, 貌似和下面的语法是等价的:
someOne.name = 'cover'
那我们继续往下看接下来的属性。
2. writable
该属性是否可写, 如果设置成 false,则任何对该属性改写的操作都无效(但不会报错),默认为 false。
var someOne = { };
Object.defineProperty(someOne, "name", {
value:"coverguo" , // 由于设定了writable属性为false 导致这个量不可以修改
writable: false
});
console.log(someOne.name); // 输出 coverguo
someOne.name = "monkeyWang";
console.log(someOne.name); // 输出coverguo
3. configurable
如果为 false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化,默认为 false。
var someOne = { };
Object.defineProperty(someOne, "name", {
configurable: false, // 由于设定了configurable属性为false导致所有属性不可修改
value: "hello"
});
// 下面的操作想让 configurable 变为 true 但是不会被允许
Object.defineProperty(someOne, "name", {
configurable: true,
value: "hello"
}); // Cannot redefine property: name
var someOne = { };
Object.defineProperty(someOne, "name", {
configurable: true, // 由于设定了configurable属性为false导致所有属性不可修改
value: "hello"
});
// 下面的操作想让 configurable 变为 false 是可以的
Object.defineProperty(someOne, "name", {
configurable: false,
value: "hello"
});
4. enumerable
是否能在for...in循环中遍历出来或在Object.keys中列举出来。默认为 false。
var someOne = {}
Object.defineProperty(someOne,"name",{
value:3445,
enumerable:true
})
console.log(Object.keys(someOne));// 打印["name"]
改为false
var someOne = {}
Object.defineProperty(someOne,"name",{
value:3445,
enumerable:false //注意咯这里改了
})
console.log(Object.keys(someOne));// 打印[]
for...in 类似,不赘述了。
接下来看看更加关键的几个属性:set 和 get
5. set 和 get
在 descriptor 中不能同时设置访问器(get 和 set)和 wriable 或 value,否则会错,就是说想用 get 和 set,就不能用 writable 或 value 中的任何一个。
set 和 get,他俩干啥用的的。
var someOne= {}
Object.defineProperty(someOne,"name",{
set: function (newValue) {
console.log('你设置了name,新的值是' + newValue);
},
get: function (value) {
console.log('你访问了name');
}
})
someOne.name = 'monkeyWang'// 你设置了name,新值是monkeyWang
console.log(someOne.name) // 你访问了name
简单来说,这个 “b” 赋值或者取值的时候会分别触发 set 和 get 对应的函数。
演习:
了解了上面这么多,那我们便可以开始 Vue 的基本响应式之旅了:我们从最简单的开始。其中,动态数据绑定就是 Vue 最为基础,最为有用的一个功能。这个系列将分成5部分,一步一步来理解和实现这一功能。ok,我们从最简单的开始。给定任意一个对象,如何监听其属性的读取与变化?也就是说,如何知道程序访问了对象的哪个属性,又改变了哪个属性?
let app1 = new Observer({
name: 'youngwind',
age: 25
});
let app2 = new Observer({
university: 'bupt',
major: 'computer'
});
// 要实现的结果如下:
app1.data.name // 你访问了 name
app.data.age = 100; // 你设置了 age,新的值为100
app2.data.university // 你访问了 university
app2.data.major = 'science' // 你设置了 major,新的值为 science
感兴趣的小伙伴可以动手去实现这样一个简单的数据绑定。
我的实现可以在这里看到:https://github.com/monkeyWangs/javascript-/blob/master/monkeyWang/unit3/unit3.js
下一篇我们继续介绍:如果传入参数对象是一个“比较深”的对象(也就是其属性值也可能是对象),那该怎么办呢?考虑传递回调函数。在实际应用中,当特定数据发生改变的时候,我们是希望做一些特定的事情的,而不是每一次都只能打印出一些信息。所以,我们如何支持传入回调函数的功能?