对数据响应式的理解

222 阅读3分钟

什么是数据的响应

当我们对某个事物进行刺激,这个事物做出反应,这个反应的行为叫做响应,如果一个数据会对操作他的行为做出反应,那么他就是个可响应的数据。

如何制造可响应的数据

ES6 提供了一些方法,可以把对象的属性变成可响应的

  • getter
  • setter
  • Object.defineProperty()

getter 的使用

getter 可以给对象的某个属性添加一个状态,即当这个属性被读取时,我们可以对他做一些操作

来个栗子 ↓

let obj1 = {
    a: 0,
    get b() {
    	return 'Hello-----' + this.a;
    }
}

// 当我们读取属性 b 时,他返回属性 a 的值给我们,并为其添加了一个 Hello 的前缀
console.log(obj1.b);	// Hello-----0

// 这个 b 属性如果不添加 get 关键字,那就是一个函数,我们直接调用也可也得到上面一样的结果
let obj2 = {
    a: 0,
    b() {
    	return 'Hello-----' + this.a;
    }
}
console.log(obj2.b());	// Hello-----0

setter 的使用

如果说 getter 是读取操作,那 setter 就是修改

let obj1 = {
    a: 0,
    set b(value) {
    	console.log('b属性被触发,并修改了属性 a');
    }
}
// 修改 b 属性
obj1.b = 5; // b属性被触发,并修改了属性 a
// 我们打印 a 可以发现其值已经被修改为 5
console.log(obj1.a);

这里的 gettersetter 我们都是在操作他自身对象上的一些属性,既然能操作属性,那我们也能调用一些事先定义好的函数。

但现在我们都是在对象声明时让属性变成响应式的,但很多时候,定义对象的并不是我们,所以 ES6 提供了另一个方法,用来给已经定义的对象添加 gettersetter 操作

Object.defineProperty() 的使用

Object.defineProperty() 就是 ES6 提供给我们用来给已经声明过的对象添加 gettersetter 操作的方法

我们现在尝试给 obj 添加一个属性 num,值为 0 ,这个属性 num 是可响应的,当我们读取他时,他不但返回他的值,还会顺手打印一个属性正在被读取,当我们修改他的值,他会返回修改后的值,并顺收打印一个已经修改好了

// 声明一个对象
let obj = {}
// 声明一个属性,用来存储 num 的值
obj.hide = 0;

// 使用 Object.defineProperty 给 obj 对象添加一个可响应的属性 num
Object.defineProperty(obj, 'num', {
    get() {
        // 读取时触发的操作
        console.log('属性正在被读取');
        return this.hide;
    },
    set(value) {
        // 修改时触发的操作
        this.hide = value;
        console.log('属性已经修改好了');
        return this.hide;
    }
});
// 读取 obj.num
console.log(obj.num); // 属性正在被读取 0
obj.num = 5;
console.log(obj.num); // 属性已经修改好了 5

使用代理制造可响应的数据

刚才我们在做 Object.defineProperty 的栗子中,给 obj 创建了一个 hide 属性,用来存储 num 的值,我们其实可以发现,在对 num 进行读写操作时,都是在操作 hide ,因为如果我们直接用 this.num 作为返回值,就会陷入死循环,因为 this.num 被返回之前,浏览器会读取这个属性,就再一次触发了 get 操作,无限循环,但我们不能不给返回值,不然你读取的就是一个 undefined

所以我们另外一个属性 hide 来存储 num 的值,这样我们在 return 的时候就不会陷入死循环了,但会遗留一个问题,如果有人直接操作 hide 的话也可也修改 num ,但不会触发我们设置的 setter/getter

let obj = proxy( { data: { num:0 } } );

// proxy 就是代理,他返回一个和 data 拥有一摸一样属性的对象
function proxy({data}){
	const obj1 = {} // 这个对象代理对象
	
	Object.defineProperty( obj1 , 'num' , {
		get (){
			return data.num;
		},
		set (value){
			return data.num = value;
		}
	} );
	
	// 返回代理对象
	return obj1;
}

// 读取 num 属性
console.log(obj.num); // 0
// 修改 num 属性
obj.num = 5;
console.log(obj.num); // 5