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

defineProperty

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

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

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)方法用于定义(设置)对象并对指定的属性描述符对象进行配置。该方法的第一个参数为目标对象,第二个参数为指定的属性名,第三个参数为配置对象。

/* 备注:给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)方法用于获取对象中所有成员的 详细 配置信息。

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 attrNameset attrName的方式来对属性的设置和读取操作进行拦截和监听。通过下面的代码,我们可以观察到,对象属性的 Getter 和 Setter 的代码并不复杂但却需要借助一个无关的中间变量_age来实现。

/* 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()方法的配置对象中也支持对象属性的 GetterSetter操作。

(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 监听,可以考虑对上述代码进行封装。

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

前端开发系列121-进阶篇之defineProperty的更多相关文章

  1. openlayers5-webpack 入门开发系列一初探篇(附源码下载)

    前言 openlayers5-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载 ...

  2. leaflet-webpack 入门开发系列一初探篇(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 w ...

  3. 【Windows10 IoT开发系列】配置篇

    原文:[Windows10 IoT开发系列]配置篇 Windows10 For IoT是Windows 10家族的一个新星,其针对不同平台拥有不同的版本.而其最重要的一个版本是运行在Raspberry ...

  4. ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266烧录配置

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  5. 【webpack 系列】进阶篇

    本文将继续引入更多的 webpack 配置,建议先阅读[webpack 系列]基础篇的内容.如果发现文中有任何错误,请在评论区指正.本文所有代码都可在 github 找到. 打包多页应用 之前我们配置 ...

  6. iOS开发系列--Swift进阶

    概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...

  7. 旨在脱离后端环境的前端开发套件 - IDT Server篇

    IDT,一个基于Nodejs的,旨在脱离后端环境的前端开发套件,目的就是能让前端开发完全脱离后端的环境,无论后端是什么模板引擎(主流),都能应付自如. IDT主要包括两大部分:Server + Bui ...

  8. 前端开发【第2篇:CSS】

    鸡血 样式的属性多达几千个,但别担心,按照80-20原则,常用的也就几十个,你完全可以掌握它. Css初识 HTML的诞生 早期只有HTML的时候为了让HTML更美观一点,当时页面的开发者会把颜色写到 ...

  9. [置顶]【实用 .NET Core开发系列】- 导航篇

    前言 此系列从出发点来看,是 上个系列的续篇, 上个系列因为后面工作的原因,后面几篇没有写完,后来.NET Core出来之后,注意力就转移到了.NET Core上,所以再也就没有继续下去,此是原因之一 ...

  10. openlayers4 入门开发系列之风场图篇

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

随机推荐

  1. cmd窗口中执行shell文件的方式

    使用cmd打开窗口后,使用powershell切入ps状态,然后使用sh命令即可执行shell文件,切入ps状态所在的目录为Administrator目录 bat设置Path变量 设置好sh.exe的 ...

  2. C 冒泡排序和选择排序

    冒泡排序 理论概念: 从第一个数开始,将相邻的两个数比较,第一个数和第二个数比较,比如说是从小到大的排序,要是后面的数比前面的小则交换两个的位置,这样第一轮比较基数后最大的数就到了最后面,接着进行第二 ...

  3. kubelet 创建 Pod 前发生了什么?

    Kubelet Watch 到新增的 Pod,需要做的主要有以下几件事: 管理 Pod 状态,除了更新本地缓存,还要同步给 API server 计算节点的资源是否足够创建 Pod 创建 Cgroup ...

  4. .Net Core全局的json配置

    .Net Core全局的json配置 参照文档: 怎样将枚举展示成字符串类型:https://code-maze.com/csharp-serialize-enum-to-string/ 全局配置:a ...

  5. pandas 将excle两行或多行文本合并为一行

    原有excle 目的: # j加载另一份数据源 import pandas as pd import xlrd import time from xlutils.copy import copy fr ...

  6. Linux系统的一些基本文件和目录管理命令

    pwd:查看当前目录所在位置. ls:查看当前目录下的文件和目录.例如我们查看根目录下的文件和目录: (注:蓝字的是目录,白字的是文件,绿字的是可执行的文件或装有可执行文件的目录,红字是压缩包) 如果 ...

  7. 11.7K Star!这个分布式爬虫管理平台让多语言协作如此简单!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 分布式爬虫管理平台Crawlab,支持任何编程语言和框架的爬虫管理,提供可视化界面.任务调度 ...

  8. Mac M1 安装python3.6.x

    在mac M1上通过pyvenv 直接安装python3.6.x 会失败. 后来发现其实python官方直接提供了m1的pkg包,就不需要再重新编译安装了. 进入python官方为macos提供的各版 ...

  9. MCP与华为云CSE珠联璧合,打造AI时代微服务生态引擎

    本文分享自华为云社区<MCP与华为云CSE珠联璧合,打造AI时代微服务生态引擎>,作者:华为云社区精选 从 AI 技术的"火热概念"到"实际业务的落地&quo ...

  10. 浅谈log4j2 CVE-2021-44228

    致谢:此问题由阿里云安全团队的 Chen Zhaojun 发现. 什么是Log4j2 Apache Log4j2 <=2.14.1 版本提供的 JNDI 特性用于配置.日志信息.参数位置时,无法 ...