2024-05-06 20:40:05 +00:00
|
|
|
|
---
|
2024-10-30 17:34:11 +00:00
|
|
|
|
date: 2020-10-22
|
2024-05-06 20:40:05 +00:00
|
|
|
|
id: b866160c-ac93-45af-8faf-1fd40f002f77
|
|
|
|
|
title: JavaScript Metaprogramming
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Introduction
|
|
|
|
|
|
|
|
|
|
In programming, there are levels:
|
|
|
|
|
|
|
|
|
|
- At the *base level* (also called: *application level*), code
|
|
|
|
|
processes user input.
|
|
|
|
|
- At the *meta level*, code processes base level code.
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
An example of this is
|
|
|
|
|
[eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)[^1]:
|
|
|
|
|
|
|
|
|
|
``` javascript
|
|
|
|
|
console.log(eval("5 + 2")); // 7
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
``` javascript
|
|
|
|
|
const obj = {
|
|
|
|
|
hello() {
|
|
|
|
|
console.log("Hello!");
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Meta level
|
|
|
|
|
for (const key of Object.keys(obj)) {
|
|
|
|
|
console.log(key);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
All [Object.\*
|
|
|
|
|
methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#)
|
|
|
|
|
can be considered metaprogramming functionality
|
|
|
|
|
|
|
|
|
|
# Kinds of metaprogramming
|
|
|
|
|
|
|
|
|
|
Reflective metaprogramming means that a program processes itself.
|
|
|
|
|
Kiczales et al[^2]. \[2\] distinguish three kinds of reflective
|
|
|
|
|
metaprogramming:
|
|
|
|
|
|
|
|
|
|
- **Introspection:** you have read-only access to the structure of a
|
|
|
|
|
program.
|
|
|
|
|
- **Self-modification:** you can change that structure
|
|
|
|
|
- **Intercession:** you can define the semantics of some language
|
|
|
|
|
operations
|
|
|
|
|
|
|
|
|
|
## Introspection
|
|
|
|
|
|
|
|
|
|
[Object.keys()](20200826201605-objects.org::*Object.keys) performs
|
|
|
|
|
introspection
|
|
|
|
|
|
|
|
|
|
## self-modification
|
|
|
|
|
|
|
|
|
|
The following function `moveProperty` moves a property from a source to
|
|
|
|
|
a target. It performs self-modification via the bracket operator for
|
|
|
|
|
property access, the assignment operator and the delete operator. (In
|
|
|
|
|
production code, you’d probably use property descriptors for this task.)
|
|
|
|
|
|
|
|
|
|
``` javascript
|
|
|
|
|
function moveProperty(source, propertyName, target) {
|
|
|
|
|
target[propertyName] = source[propertyName];
|
|
|
|
|
delete source[propertyName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const obj1 = { prop: "abc" };
|
|
|
|
|
const obj2 = {};
|
|
|
|
|
moveProperty(obj1, "prop", obj2);
|
|
|
|
|
|
|
|
|
|
console.log(obj1); // {}
|
|
|
|
|
console.log(obj2); // { prop: 'abc' }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## intercession
|
|
|
|
|
|
|
|
|
|
ECMAScript 5 doesn't support intercession.
|
|
|
|
|
[Proxies](20201022094207-javascript_proxies) were created to fill that
|
|
|
|
|
gap.
|
|
|
|
|
|
|
|
|
|
# Footnotes
|
|
|
|
|
|
|
|
|
|
[^1]: Don't ever use eval. If you think you're a clever clogs that can
|
|
|
|
|
work your away around the obvious security issues, then you
|
|
|
|
|
especially should not use eval.
|
|
|
|
|
|
|
|
|
|
[^2]: "[The Art of the Metaobject
|
|
|
|
|
Protocol](http://mitpress.mit.edu/books/art-metaobject-protocol)" by
|
|
|
|
|
Gregor Kiczales, Jim des Rivieres and Daniel G. Bobrow. Book, 1991.
|