说到响应式原理,一定会提到JavaScriptES5中的API——Object.defineProperty()方法。也是面试会问的原理,今天我们来好好学习一下!
定义:
Object.defineProperty(),是一个静态方法,通过定义属性的元数据信息精确的控制属性的行为。
这定义讲了跟没讲似的,我来解读一下:
就是用来定义了对象之后,更改其属性值或者枚举其方法,定义中的行为就是指读或者写操作。
也就是说:
对象是由多个键/值对组成的无序的集合。对象中每个属性可以是任意类型的值。
定义对象可以使用构造函数或字面量的形式:
var obj = new Object; //obj = {}
obj.name = "张三"; //添加描述
obj.say = function(){}; //添加行为
除了以上添加属性的方式,还可以使用Object.defineProperty
定义新属性或修改原有的属性。
语法:
Object.defineProperty(obj,prop,descriptor)
参数解读:
obj——表示要在其定义属性的对象
prop——接收String、Symbol类型,表示要定义或者球盖的属性的名称
descriptor——(很重要的属性)被定义或者修改的属性描述符,可以定义属性行为的元数据信息。大白话来讲就是说:通过它来限制属性的读写行为。
返回值:
返回一个Object。返回被传递给函数的对象obj
属性描述符分为两种:数据描述符和存取描述符。
这两者均有的键值有:configurable和enumerable
configurable:
布尔值。设为true时,才能再次修改该属性的属性描述符,同时该属性也能从对应的对象上被删除,默认false。(但我们一般使用Object.defineProperty时,要设置为true,因为我们需要去更改这些键值)
enumerable:
布尔值。设为true时, 该属性才能够出现在对象的枚举属性中,默认false。
也就是说:我们在循环一个对象的时候,把其中一个属性的enumerable设置为false,该属性就不会出现在这个循环中。
数据描述符特有的键值:
value——表示该属性的初始值,可以是任何有效的JavaScript值,默认为undefined
writable——布尔值。设为true时,表示该属性才能被写入值,默认false。
存取操作符特有的键值为:get和set,相当于拦截器,
get——一个给属性提供getter的方法,如果没有getter则为undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象),默认为undefined。
set——一个给属性提供setter的方法,如果没有setter则为undefined。当属性值修改时,出发执行该方法。方法将接受唯一参数,即该属性的新的参数值,默认为undefined。
具体我们看下面的代码示例就会明白。
1、数据描述符的创建示例:
2、存取描述符的创建示例:
再来做一道笔试题吧~
//以下代码的输出结果是:
var o = { a: 1 };
Object.defineProperty(o, "b", {value: 2, writable: false, enumerable: false, configurable: true});
o.a = 2;
o.b = 3;
console.log(o.a, o.b);
答案是:2 2
因为Object.defineProperty的第二个参数表示要定义或者球盖的属性的名称,在前面的知识我们知道, writable: false表示不可擦写,enumerable: false表示不可枚举,configurable: true表示可以再次修改该属性的属性描述符。
所以o.b的初始值为value=2,数据被劫持了,o.b不可擦写,不可枚举 ,即还是o.b还是2;
这里Object.defineProperty修改的是b属性,和a无关,o.a后面被修改为2那么o.a就是2。