Object.defineProperty()和Object.defineProperties()都是Object构造函数的静态方法,作用是定义数据!!!
defineProperty()
:::info
参数1: 需要被定义的对象
参数2: 需要被定义的对象属性
参数3: 描述配置对象
:::
// 假如我现在要对 obj 对象的 test 属性进行定义var obj = {};Object.defineProperty(obj, "test", {value: 1 // value 属性表示 test 属性的值,后面还会讲到其他的配置属性});console.log(obj); // {test: undefined}
以上代码的obj从明面上看和字面量定义的对象没有任何的区别:
var obj = {test: undefined}console.log(obj); // {test: undefined}
属性配置
可事实真的如此吗?下面我们将两种方式对象进行对比。
var obj1 = {a: 1};var obj2 = Object.defineProperty({}, "b", {value: 2});console.log(obj1); // {a: 1}console.log(obj2); // {a: 2}
这样我们就生成了obj1和obj2两个对象。
我们想两个对象的属性值进行修改尝试:
obj1.a = 2;obj2.b = 3;console.log(obj1); // {a: 2}console.log(obj2); // {a: 2}
结果:使用
Object.defineProperty()定义的属性无法更改属性值。
然后我们去遍历两个对象:
for (const key in obj1) {console.log(obj1[key]);}// 1for (const key in obj2) {console.log(obj2[key]);}// 无任何输出
结果:使用
Object.defineProperty()定义的属性无法枚举 枚举就是将项列举出来。
最后我们去删除两个对象的属性:
delete obj1.a;delete obj2.b;console.log(obj1); // {}console.log(obj2); // {b: 2}
结果:使用
Object.defineProperty()定义的属性无法删除
:::info
🌴 总结:
使用Object.defineProperty()定义的属性无法更改、删除和遍历。
:::
到现在我们发现Object.defineProperty()的参数 3 一直没有配置,其实参数 3 是个对象用于对对象的属性进行相关的配置:
var obj = Object.defineProperty({}, "test",{value: 1, // 配置属性的值writable: true, // 配置属性是否可以被更改,默认是 falseenumerable: true, // 配置属性是否可以被枚举,默认是 falseconfigurable: true // 配置属性是否可以被删除,默认是 false})// 然后在对 obj 的属性进行操作console.log(obj); // {test: 1}obj.test = 123;console.log(obj); // {test: 123}for (const key in obj) {console.log(obj[key]);}// 123delete obj.test;console.log(obj); // {}
get()/set()
每个属性在定义的时候都会存在getter和setter的机制,所以Object.defineProperty()的参数 3 还有set()和get()方法。
var value = 1;var obj = Object.defineProperty({}, "test",{get: function(){console.log("访问 obj.test 属性")return value;},set: function(newVal){console.log("设置 obj.test 属性")value = newVal;}})console.log(obj.test);// 访问 obj.test 属性// 1obj.test = 456;// 设置 obj.test 属性console.log(obj.test);// 访问 obj.test 属性// 2consoloe.log(obj);

这里可以发现test属性是展开三角后才能看到的,猜测这是因为浏览器要和普通属性作区分特意而为之。
利用get和set方法就可以实现对对象属性的数据劫持!!!
var obj = {};Object.defineProperty(obj, "a", {get() {return "this is a";},set(newVal) {// 当我们对象 obj.a 进行设置时候,p 元素的内容也会发生变化var oP = document.getElementsByTagName("p")[0];oP.innerHTML = newVal;}});
defineProperty()本身是Object构造函数的静态方法,所以该方法本身只可以对对象具备劫持,那么如何对数组也进行劫持呢?
function DateArr() {var _arr = [],_val = null;Object.defineProperty(this, "val", {get: function () {return _val;},set: function (newVal) {_val = newVal;_arr.push({ val: _val });console.log("A new value " + _val + "has been pushed to _arr");},});this.getArr = function () {return _arr;};}var dateArr = new DateArr();dateArr.val = 123;dateArr.val = 234;console.log(dateArr.getArr()); // [{val: 123}, {val: 234}]
互斥
:::danger
注意 ⚠️
如果配置中只有enumerable``configurable的时候该配置是对数据描述。
如果配置中同时存在 「value、writable」和 「get、set」 是互斥的!!!
:::
var obj = {};// 以下的情况会发生错误var obj1 = Object.defineProperty({}, "a" ,{value: 1, // errorwritable: true, // errorget() {return "this is a";},set(newVal) {var oP = document.getElementsByTagName("p")[0];oP.innerHTML = newVal;}});// 以下的情况会正常执行var obj2 = Object.defineProperty({}, "a" ,{value: 1,writable: true,enumerable: true,configurable: true,});// 以下的情况也会正常执行var obj3 = Object.defineProperty({}, "a" ,{enumerable: true,configurable: true,get() {return "this is a";},set(newVal) {var oP = document.getElementsByTagName("p")[0];oP.innerHTML = newVal;}});
defineProperties()
Object.defineProperty()方法只能对对象的一个属性进行定义,那么如何定义对象的多个属性呢?
可以使用Object.defineProperties()来定义多个属性。
defineProperties()和defineProperty()写法的不同在于defineProperties接收两个参数,参数1 是要定义的对象,参数 2 是定义对象属性的对象。
var value = 2;var obj = Object.defineProperties({}, {a: {value: 1,writable: true,enumerable: true,configurable: true,},b: {get() {return value;},set(newVal) {value = newVal;},}});console.log(obj);

