1. v-model是什么?
- 在表单控件或者组件上实现双向绑定
2. v-model 原理:
vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情。
2-1. Object.defineProperty()
- Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
- obj 需要定义属性的当前对象
- prop 当前需要定义的属性名
- desc 属性描述符
- 注意: 一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。
属性描述符
通过Object.defineProperty()为对象定义属性,有两种形式,且不能混合使用,分别为数据描述符,存取描述符,下面分别描述下两者的区别:
1>. 数据描述符 - 特有的两个属性(value,writable)
let Person = {}
Object.defineProperty(Person, 'name', {
value: 'jack',
writable: true // 是否可以改变
})
2>. 存取描述符 - 是由一对 getter、setter 函数功能来描述的属性
- get:一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。
- set:一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。
let Person = {}
let temp = null
Object.defineProperty(Person, 'name', {
get: function () {
return temp
},
set: function (val) {
temp = val
}
})
数据描述符和存取描述均具有以下描述符:
- configrable 描述属性是否配置,以及可否删除
- enumerable 描述属性是否会出现在for in 或者 Object.keys()的遍历中
2-2. 如何使用Object.defineProperty实现数据劫持
function render() {
console.log('模拟试图渲染');
}
let obj = {
name: 'jiajia',
location: {
x: 100,
y: 100
}
}
let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push'];
// 先获取原先原型上的方法
let arrayProto = Array.prototype;
// 创建一个自己的原型 并且重写methods这些方法
let proto = Object.create(arrayProto);
methods.forEach(method => {
proto[method] = function () {
render();
arrayProto[method].call(this, ...arguments);
}
})
// 定义响应式
function defineReactive(data, key, value) {
observer(value);
Object.defineProperty(data, key, {
get() {
return value;
},
set(newValue) {
observer(newValue);
if (newValue !== value) {
render();
value = newValue;
}
}
})
}
function observer(obj) {
// 重写数组方法
if (Array.isArray(obj)) {
obj.__proto__ = proto;
return;
}
// 把所有的属性定义成set/get的方式
if (typeof (obj) == 'object') {
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
}
observer(obj);