Talking about Meta programming in es6

  • 2021-10-11 17:30:51
  • OfStack

What is metaprogramming?

"Write programs that change the syntactic or runtime properties of a language." In other words, what a language can't do, you can modify it by programming, so that it can be done, which is metaprogramming.

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

For example:


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

How can this condition be met and output done? It can't be done according to normal logic. After all, one value can't satisfy equal to 1, 2 and 3 at the same time

It is possible to use metaprogramming to change this impossible


let a = {
  [Symbol.toPrimitive]: ((i) => () => ++i)(0)
}

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

Symbol. toPrimitive will be called when the object is converted to the original value. The initial value is 1. If you call +1 once, you can satisfy a = = 1 & & a == 2 & & a = = 3. At the same time, Symbol. toPrimitive can also accept one parameter hint, and the values of hint are number, string and 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

What other metaprogramming?

proxy

es6 upgrade of Object. defineProperty () method of es5 for custom object behavior


let leon = {
  age: 30
}
const validator = {
  get: function(target, key){
    //  Returns without this property 37
    return key in target ? target[key] : 37;
  },
  set(target,key,value){
    if(typeof value!="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 has to be a number 

reflect-metadata

You can add some custom information to the class through the decorator. Then this information is 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

Application

Extended array index access

Negative index access, making array [-N] identical to 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

Data hijacking


let handlers = Symbol('handlers');

function makeObservable(target) {
 //  Initialization storage  handler  Array of 
 target[handlers] = [];

 //  Storage  handler  Function into an array for future calls 
 target.observe = function(handler) {
  this[handlers].push(handler);
 };

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

let user = {};

user = makeObservable(user);

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

user.name = "John";
// SET name=John


Related articles: