ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

JS中无处不在的元编程

2021-06-19 07:51:28  阅读:185  来源: 互联网

标签:编程 Object 无处不在 Reflect Proxy https JS JavaScript


@[TOC] # 什么是元编程? 维基百科: > `元编程` (meta programming)是一种编程技术,编写出来的计算机程序能够将其他程序作为数据来处理。 > > 意味着可以编写出这样的程序:它能够`读取、生成、分析或者转换`其它程序,甚至在`运行时修改程序自身`(反射)。 元编程中的 `元` 的概念可以理解为 程序 本身,元编程关注以下的一点或几点: + 1、运行时修改语言结构,这种现象被称为 `反射编程` 或 `反射`; + **自省**:代码检视自己; + **自我修改**:代码修改自己; + **调解**:代码修改`默认的语言行为`而使其他代码受影响; + 2、生成代码; # 一、自省 代码能够自我检查、访问内部属性,获得代码的底层信息! ```javascript // 访问对象自身属性 var users = { 'Tom': 32, 'Bill': 50, 'Sam': 65 }; Object.keys(users).forEach(name => { const age = users[name]; console.log(`User ${name} is ${age} years old!`); }); // 输出结果 User Tom is 32 years old! User Bill is 50 years old! User Sam is 65 years old! ``` **自省**在平时的业务开发中很常见,这也是元编程技术的一种使用! # 二、自我修改 顾名思义,代码可以修改自身属性或者其他底层信息! ```javascript let a = 1; if (a == 1 && a == 2 && a == 3) { console.log("元编程"); } ``` 上述代码在正常情况下是无论如何也无法满足条件输出, 因为一个值不可能同时满足等于1、2、3;但是,利用元编程就可以实现: ```javascript // 修改自身 let a = { [Symbol.toPrimitive]: ((i) => () => ++i)(0); } if (a == 1 && a == 2 && a == 3) { console.log("元编程"); } // 输出 '元编程' ``` [Symbol.toPrimitive](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive) 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的; 在对象转换为原始值的时候会被调用,初始值为1,调用一次+1,就可以满足`a == 1 && a == 2 && a == 3`; 上述函数变形为: ```javascript let a = { [Symbol.toPrimitive]: (function (i){ return function (){ return ++i } })(0) } if (a == 1 && a == 2 && a == 3) { console.log("元编程"); } ``` 在开发过程中**自我修改**应该要尽力避免,可以想象:正在使用一个数据的同时又在修改这个数据,后容易造成不可预期的错误! # 三、调解 代码`修改默认的语言行为`而使其他代码受影响,最明显的体现为改变其它对象的语义! 在元编程中,调解的概念类似于包装、捕获、拦截。 `Object.defineProperty()`就是典型的 调解 的运用: ```javascript var sun = {}; Object.defineProperty(sun, 'rises', { value: true, configurable: false, writable: false, enumerable: false }); console.log('sun rises', sun.rises); sun.rises = false; console.log('sun rises', sun.rises); // 输出 sun rises true sun rises true ``` 上面例子中,新创建了一个普通对象 `sun`,之后通过`Object.defineProperty` 改变了它的语义:为其定义了一个不可写的 `rises` 属性。 # 四、Reflect & Proxy MDN: > 从ECMAScript 2015 开始,JavaScript 获得了 [`Proxy`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy) 和 [`Reflect`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 对象的支持,允许你拦截并定义基本语言操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。借助这两个对象,你可以在 JavaScript 元级别进行编程。 ```javascript // Proxy的handler 和 Reflect 对象 13 个方法 .apply() // 对一个函数进行调用操作, 和 Function.prototype.apply() 功能类似 .construct() // 对构造函数进行 new 操作,相当于执行 new target(...args) .get() // 获取对象身上某个属性的值,类似于 target[name]。 .has() // 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同 .ownKeys() // 返回一个包含所有自身属性(不包含继承属性)的数组,类似于 Object.keys() .set() // 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true .setPrototypeOf() // 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。 .defineProperty() // Object.defineProperty() 类似 .deleteProperty() // 作为函数的delete操作符,相当于执行 delete target[name]。 .getOwnPropertyDescriptor() //对象中存在该属性,则返回对应的属性描述符,类似于 Object.getOwnPropertyDescriptor() .getPrototypeOf() // 类似于 Object.getPrototypeOf()。 .isExtensible() // 类似于 Object.isExtensible() .preventExtensions() // 类似于 Object.preventExtensions()。返回一个Boolean。 ``` ## 1、Reflect [`Reflect`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect) 是一个内置对象,它提供了可拦截 JavaScript 操作的方法。 该方法和[`Proxy的handler`](https://developer.mozilla.org/zh-CN/docs/conflicting/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy)类似,但 `Reflect` 方法并不是一个函数对象,因此它是不可构造的。 `Reflect`的所有属性和方法都是静态的(就像[`Math`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math)对象); 以 [`Reflect.has()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has) 为例,与 [`in` 运算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in)对比,检测一个对象是否存在特定属性: ```javascript Reflect.has(Object, "assign"); // true "assign" in Object; // true ``` ## 2、Proxy 在 ECMAScript 6 中引入的 [`Proxy`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy) 对象可以拦截某些操作并实现自定义行为。 例如获取一个对象上的属性: ```javascript let handler = { get: function(target, name){ return name in target ? target[name] : 42; }}; let p = new Proxy({}, handler); p.a = 1; console.log(p.a, p.b); // 1, 42 ``` `Proxy` 对象定义了一个目标(这里是一个空对象)和一个实现了 `get` 劫持的 handler 对象。 代理的对象在获取未定义的属性时不会返回 `undefined`,而是返回 42。 # 五、生成代码 js中使用元编程技术生成代码最常见的函数 `eval()`:函数会将传入的字符串当做 JavaScript 代码进行执行。 ```javascript let str = "function sayHello(){console.log('hello')}"; eval(str); sayHello(); // 输出 hello ``` # 六、总结 元编程是当你将程序的逻辑转向关注它自身(或者它的运行时环境)时进行的编程,要么为了调查它自己的结构,要么为了修改它。 元编程的主要价值是`扩展语言的普通机制来提供额外的能力`。 # 参考 - [MDN:元编程](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Meta_programming) - [JavaScript 元编程](https://chinese.freecodecamp.org/news/what-is-metaprogramming-in-javascript/) - [浅谈es6中的元编程](https://www.jb51.net/article/201101.htm)

标签:编程,Object,无处不在,Reflect,Proxy,https,JS,JavaScript
来源: https://blog.51cto.com/u_15066867/2927016

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有