JavaScript和TypeScript中的元编程

序言


我想向您的法院展示一些微型雕像,它们将描述元编程的技术和基本原理。 我将主要介绍在JavaScript或TypeScript中使用某些技术的情况。
这是该系列的第一篇(希望不是最后一篇)。


那么什么是元编程


元编程是一种编程技术,其中计算机程序具有将其他程序视为其数据的能力。 这意味着可以将程序设计为读取,生成,分析或转换其他程序,甚至在运行时对其进行修改。 在某些情况下, 这使程序员可以最大程度地减少表达解决方案的代码行数,从而减少开发时间

一个相当混乱的描述,但是元编程的主要好处是可以理解的:


...这使程序员可以最大程度地减少实施解决方案的代码行数,从而减少开发时间


实际上,元编程有很多面子和幌子。 而且,您可以就“元编程的结束位置和编程本身的开始位置”进行长时间的讨论。


对于我自己,我接受以下规则:


  1. 元编程不会处理业务逻辑,不会更改它,也不会以任何方式影响它。
  2. 如果删除所有元编程代码,则这不会(根本)影响程序。

在JavaScript中,元编程是一个相对较新的趋势,其基本组成部分是描述符。


JavaScript描述符


描述符是对象中某种属性或方法的一种描述(元信息)。


理解和适当地操纵该对象( 描述符 )不仅可以创建和更改对象中的方法或属性,还可以做更多的事情。
描述符也将有助于理解装饰器的工作(但在下一篇文章中有更多介绍)。


为了清楚起见,假设我们的对象是公寓的描述。
我们描述了我们公寓的目的:


let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} }; 

让我们确定哪些属性是可修改的,哪些属性是不可修改的。


例如,楼层或公寓的总大小无法更改,但是房间或浴室的数量是完全可能的。
因此,我们有以下要求:在apt对象中,无法更改属性: floorsize


为了解决这个问题,我们只需要每个属性的描述符即可。 要获取描述符 ,我们使用静态方法getOwnPropertyDescriptor ,该方法属于Object类。


 let descriptor = Object.getOwnPropertyDescriptor(todoObject, 'floor'); console.log(descriptor); // Output { value: 12, writable: true, enumerable: true, configurable: true } 

让我们按顺序分析:
值:任何 -实际上在某个时候分配给floor属性的值相同
可写:布尔值 -确定是否更改
枚举:布尔值 -确定floor属性是否可以列出-(稍后会详细介绍)。
可配置:布尔值 -定义对描述符对象进行更改的能力。


为了防止更改floor属性的可能性,初始化后,必须将writable的值更改为false
要更改描述符的属性有一个静态方法defineProperty ,它使用对象本身,属性的名称和描述符


 Object.defineProperty(apt, 'floor', {writable: false}); 

在此示例中,我们不传递整个描述符对象,而仅传递一个值为false的 可写属性。
现在,让我们尝试更改floor属性的值:


 apt.floor = 44; console.log(apt.floor); // output 12 

该值未更改,使用'use strict'时,我们收到一条错误消息:


无法分配为只读对象的属性“ floor” ...

现在,我们不能再更改该值。 但是,我们仍然可以返回writable-> true ,然后更改floor属性。 为了避免这种情况,有必要在描述符中将可配置属性的值更改为false


 Object.defineProperty(apt, 'floor', {writable: false, configurable: false}); 

如果我们现在尝试更改描述符的任何属性的值,则...


 Object.defineProperty(apt, 'floor', {writable: true, configurable: true}); 

作为回应,我们得到:


TypeError:无法重新定义属性:floor
换句话说,我们既不能更改floor的值也不能更改其描述符

总结一下


要使对象中的属性值不变,必须注册此属性的配置: {writable:false,configurable:false}


这可以在属性初始化期间完成:


 Object.defineProperty(apt, 'floor', {value: 12, writable: false, configurable: false}); 

或之后。


 Object.defineProperty(apt, 'floor', {writable: false, configurable: false}); 

最后,考虑一个带有类的示例:


 class Apartment { constructor(apt) { this.apt = apt; } getFloor() { return this.apt.floor } } let apt = { floor: 12, number: '12B', size: 3400, bedRooms: 3.4, bathRooms: 2, price: 400000, amenities: {...} }; 

更改getFloor方法:


 Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be changed. 44 

现在更改getFloor()方法的描述符


 Object.defineProperty(Apartment.prototype, 'getFloor', {writable: false, configurable: false}); Apartment.prototype.getFloor = () => { return 44 }; let myApt = new Apartment(apt); console.log(myApt); // output will be original. 12 

我希望本文能进一步说明什么是描述符以及如何使用它。


上面写的所有内容均不主张绝对正确或唯一正确。

Source: https://habr.com/ru/post/zh-CN450540/


All Articles