2017年4月

JavaScript 之元编程

本文介绍 JavaScript 元编程中常使用的一些语法和简单应用. 有兴趣的可以参考文末给的链接了解更多的关于 JavaScript 元编程方便的知识.

1. Symbol

class MyClass {
    static [Symbol.hasInstance](lho) {
        return Array.isArray(lho);
    }
}
assert([] instanceof MyClass);

class Collection {
  *[Symbol.iterator]() {
    var i = 0;
    while(this[i] !== undefined) {
      yield this[i];
      ++i;
    }
  }

}
var myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;
for(var value of myCollection) {
    console.log(value); // 1, then 2
}

Symbol 是 ES6 引入的新的数据类型, 应用范围十分广泛.

2. Proxy

一次关于 Proxy 的尝试.

规定要读取环境变量的值用形如 ENV:[A-Z0-9._]+ 的形式表示

let config = {
  "host": "127.0.0.1",
  "name": "admin",
  "pass": "ENV:DB_PASS" // 要求转为环境变量返回
}
config = new Proxy(config, {
  get (target, property) {
    if (property in target) {
      if (/^ENV:[A-Z0-9._]+$/.test(target[property])) {
        return process.env[target[property].slice(4)]
      } else {
        return target[property]
      }
    } else {
      throw new ReferenceError()
    }
  }
})

类似的可以自己约定并使用 Proxy 实现一些特性, 如私有属性规定 _ 开头, 使用 Proxy 拦截请求返回 undefined. 也可以使用 Proxy 实现访问日志.

Proxy 方法有很多, 而且和 Reflect 一一对应.

proxy = new Proxy({}, {
  apply: Reflect.apply,
  construct: Reflect.construct,
  defineProperty: Reflect.defineProperty,
  getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor,
  deleteProperty: Reflect.deleteProperty,
  getPrototypeOf: Reflect.getPrototypeOf,
  setPrototypeOf: Reflect.setPrototypeOf,
  isExtensible: Reflect.isExtensible,
  preventExtensions: Reflect.preventExtensions,
  get: Reflect.get,
  set: Reflect.set,
  has: Reflect.has,
  ownKeys: Reflect.ownKeys,
})

另一个例子:

function Foo() {
  return new Proxy(this, {
    get: function (object, property) {
      if (Reflect.has(object, property)) {
        return Reflect.get(object, property);
      } else {
        return function methodMissing() {
          console.log('you called ' + property + ' but it doesn\'t exist!');
        }
      }
    }
  });
}

Foo.prototype.bar = function () {
  console.log('you called bar. Good job!');
}

foo = new Foo();
foo.bar();
//=> you called bar. Good job!
foo.this_method_does_not_exist()
//=> you called this_method_does_not_exist but it doesn't exist

- 阅读剩余部分 -