What is metaprogramming?

Write programs that change syntactic or runtime features of the language. In other words, you can program a language to do something it can’t do, which is metaprogramming.

The concept of meta in programming can be understood as the program itself.” Metaprogramming gives you the ability to extend the program itself

Here’s an example:

if (a == 1 && a == 2 && a == 3) {
    console.log("done");
}
Copy the code

How do I make this condition so that I can print done. This cannot be done by normal logic. After all, a value cannot be equal to 1, 2, and 3 at the same time

This is where you can use metaprogramming to change the impossible

let a = {
    [Symbol.toPrimitive]: ((i) = > () = > ++i)(0)}if (a == 1 && a == 2 && a == 3) {
    console.log("done");
}
// done
Copy the code

Symbol. ToPrimitive is called when the object is converted to its original value. The initial value is 1, and a +1 is called to a == 1&&a == 2&&a == 3. Hint can be number, string, or default

let obj = {
    [Symbol.toPrimitive](hint) {
        switch (hint) {
            case "number":
                return 123;
            case "string":
                return "str";
            case "default":
                return "default"; }}}console.log(1-obj); / / - 122
console.log(1+obj); // 1default
console.log(`${obj}`); // str
Copy the code

What else is metaprogramming?

proxy

Es6 upgraded version of es5’s object.defineProperty () method, which is used to customize the behavior of objects

let leon = {
    age: 30
}
const validator = {
    get: function(target, key){
        // Return 37 without this attribute
        return key in target ? target[key] : 37;
    },
    set(target,key,value){
        if(typeofvalue! ="number" || Number.isNaN(value)){
            throw new Error("Age has to be a number."); }}}const proxy = new Proxy(leon,validator);
console.log(proxy.name);
/ / 37
proxy.age = "hi";
// Error: Age must be a number
Copy the code

reflect-metadata

You can use decorators to add custom information to a class. This information is then extracted by reflection. Of course, you can also add this information through reflection

require("reflect-metadata")
class C {
    // @Reflect.metadata(metadataKey, metadataValue)
    method(){}}Reflect.defineMetadata("name"."jix", C.prototype, "method");

let metadataValue = Reflect.getMetadata("name", C.prototype, "method");
console.log(metadataValue);
// jix
Copy the code

application

Extend array index access

Negative index access makes array[-n] the same as array[array.length -n]

let array = [1.2.3];

array = new Proxy(array, {
  get(target, prop, receiver) {
    if (prop < 0) {
      console.log(prop, 'prop')
      prop = +prop + target.length;
    }
    return Reflect.get(target, prop, receiver); }});console.log(array[-1]); / / 3
console.log(array[-2]); / / 2
Copy the code

The data was hijacked

let handlers = Symbol('handlers');

function makeObservable(target) {
  // Initializes an array of handlers
  target[handlers] = [];

  // Store handler functions in an array for future calls
  target.observe = function(handler) {
    this[handlers].push(handler);
  };

  // Create an agent to handle changes
  return new Proxy(target, {
    set(target, property, value, receiver) {
      // Forward write operations to the target object
      let success = Reflect.set(... arguments);// If there is no error when setting the property
      if (success) {
        // Call all handlers
        target[handlers].forEach(handler= > handler(property, value));
      }
      returnsuccess; }}); }let user = {};

user = makeObservable(user);

user.observe((key, value) = > {
  console.log(`SET ${key}=${value}`);
});

user.name = "John";
// SET name=John
Copy the code