前端开发系列121-进阶篇之defineProperty

本文介绍`Object.defineProperty()`方法,并基于此简单讨论数据劫持的实现方案。

defineProperty

Object.getOwnPropertyDescriptor(target,attrName)方法用于获取对象的属性描述符对象,该方法的第一个参数为目标对象,第二个参数为指定的属性名。

我们可以利用该方法来查看对象属性的描述符配置项(包括:value值writable可重写enumerable可枚举configurable可配置等)。默认正常的对象属性中,这些配置项的值都是 true

ini 复制代码
var o = { name: "文顶顶", age: 18 };
var des = Object.getOwnPropertyDescriptor(o, "name");
console.log(des);
/* { value: '文顶顶',writable: true,enumerable: true,configurable: true } */

Object.defineProperty(target,attrName,options)方法用于定义(设置)对象并对指定的属性描述符对象进行配置。该方法的第一个参数为目标对象,第二个参数为指定的属性名,第三个参数为配置对象。

javascript 复制代码
/* 备注:给o对象添加address属性,并设置属性值为香悦山 */
/* 说明:默认新添加的属性,属性描述配置项均为false */
var o = { name: "文顶顶", age: 18 };
Object.defineProperty(o, "address", { value: "香悦山" });
console.log(Object.getOwnPropertyDescriptor(o, "address"));
/* { value: '香悦山',writable: false,enumerable: false,configurable: false } */

/* 备注:重新定义age属性,设置属性值为20,该属性值可配置但无法重写和枚举 */
Object.defineProperty(o, "age", { value: 20, enumerable: false, writable: false });
console.log(Object.getOwnPropertyDescriptor(o, "age"));
/* { value: 20,writable: false,enumerable: false,configurable: true } */
o.age = 99;
console.log(o.age); //20
for (var key in o) {
    console.log(key, o[key]);
}
/* name 文顶顶 */
/* 在for...in循环中,age键值对 ,以及新添加的address键值对均没有被枚举 */

Object.defineProperties(target,options)方法用于一次性设置(添加)对象的多个属性,与之对应的Object.getOwnPropertyDescriptors(target)方法用于获取对象中所有成员的 详细 配置信息。

yaml 复制代码
Object.defineProperties(o, {
    "className": {
        value: "H5",
        configurable: true,
    },
    "friends": {
        value: ["胡适", "沈从文", "辜鸿铭"],
        configurable: true,
        writable: true
    }
});
console.log("_____");
console.log(Object.getOwnPropertyDescriptors(o));
/* 
{   name:{ value: '文顶顶', writable: true,enumerable: true,configurable: true },
    age:{ value: 20,writable: false,enumerable: false,configurable: true },
    address:{ value: '香悦山',writable: false,enumerable: false,configurable: false },
    className:{ value: 'H5',writable: false,enumerable: false,configurable: true },
    friends:{ value: [ '胡适', '沈从文', '辜鸿铭' ],
              writable: true, enumerable: false,configurable: true } } */

Object.defineProperty \]()方法主要用于对象中的某个属性进行访问配置,如果需要对整个对象执行类似的操作则可使用\`Object.preventExtensions()\`、\`Object.seal()\`和\`Object.freeze()\` 等方法,它们分别对应着\`禁止扩展\`、\`密封对象\`以及要\`冻结\`对象。 ## Getter and Setter 对于对象字面量创建的对象而言,我们可以直接通过`get attrName`或`set attrName`的方式来对属性的设置和读取操作进行拦截和监听。通过下面的代码,我们可以观察到,对象属性的 Getter 和 Setter 的代码并不复杂但却需要借助一个无关的中间变量`_age`来实现。 ```javascript /* getter 和 setter */ var o = { name: "文顶顶", _age: 17, get age() { console.log("监听到执行了getter方法"); return this._age; }, set age(val) { console.log("监听到执行了setter方法"); this._age = val; } } console.log(o.age); o.age = 100; console.log(o.age); /* 监听到执行了getter方法 17 监听到执行了setter方法 监听到执行了getter方法 100 */ ``` `Object.defineProperty()`方法的配置对象中也支持对象属性的 `Getter` 和 `Setter`操作。 ```javascript (function() { var o = { name: "文顶顶", age: 17 }; var temp = 18; Object.defineProperty(o, "age", { get() { console.log("------getter---------"); return temp; }, set(val) { console.log("------setter---------"); temp = val; } }); console.log(o.age); o.age = 100; console.log(o.age); /* 执行情况 */ // ------getter--------- // 18 // ------setter--------- // ------getter--------- // 100 })(); ``` *** ** * ** *** 利用\`Object.defineProperty()\`方法,来监听对象属性的设置和读取操作,可以不必借助于中间属性来实现而改用一个外部变量即可,这样的处理方式为代码的封装提供了可能。 **注意** 在 `defineProperty` 方法内部使用 `set 和 get` 函数时不能与 `value 和 writable` 共存。上述的代码演示了监听对象单个属性读写的方案,如果需要为对象中所有的属性都添加 `set 和 get` 监听,可以考虑对上述代码进行封装。 ```javascript let observer = (target) => { if (typeof target !== "object" || target == null) return; for (var key in target) { if (target.hasOwnProperty(key)) defineReactive(target, key, target[key]); } } let defineReactive = (target, key, val) => { observer(val); /* 递归解决多层对象解构问题 */ /* val 是外部传入的参数:就是指定属性的默认值 */ Object.defineProperty(target, key, { get() { handler("getter") return val; }, set(_val) { handler("setter") val = _val; } }) } let handler = (text) => { console.log("监听到" + text); } /* 测试代码 */ var o = { name: "文顶顶", age: 18, car: { color: "white" } }; observer(o); o.age = 100; console.log(o.age); o.car.color = "black"; console.log(o.car.color); /* 执行情况 */ // 监听到setter // 监听到getter // 100 // 监听到getter // 监听到setter // 监听到getter // 监听到getter // black ```

相关推荐
金串串7 分钟前
js-day10
javascript
GIS之路16 分钟前
GeoTools 结合 OpenLayers 实现空间查询
前端
轻语呢喃16 分钟前
每日LeetCode:合并两个有序数组
javascript·算法
陳有味_ChenUvi17 分钟前
使用 pnpm 优雅搭建 Monorepo 仓库
前端·npm·前端工程化
旷世奇才李先生19 分钟前
XML DOM 安装使用教程
xml·前端·chrome
程序员秘密基地26 分钟前
基于html,css,vue,vscode,vs2022,asp.net,aspnet,.net,c#,mysql数据库,在线健身,俱乐部管理系统
前端·vue.js·后端·mysql·asp.net
Mintopia29 分钟前
Three.js 画布纹理:像素世界的魔法编织术
前端·javascript·three.js
天天摸鱼的java工程师29 分钟前
当我成为面试官,我才知道当年那些面试官其实并不是在难为我,而是在考察我面对问题的拆解能力
前端·后端·面试
袁煦丞32 分钟前
泰拉瑞亚远程联机魔法:cpolar内网穿透实验室第617个成功挑战
前端·程序员·远程工作
玲小珑36 分钟前
Next.js 教程系列(十二)API Routes:构建轻量级后端服务
前端·next.js