维基百科:javascript
元编程
(meta programming)是一种编程技术,编写出来的计算机程序可以将其余程序做为数据来处理。java意味着能够编写出这样的程序:它可以
读取、生成、分析或者转换
其它程序,甚至在运行时修改程序自身
(反射)。es6
元编程中的 元
的概念能够理解为 程序 自己,元编程关注如下的一点或几点:编程
一、运行时修改语言结构,这种现象被称为 反射编程
或 反射
;数组
默认的语言行为
而使其余代码受影响;代码可以自我检查、访问内部属性,得到代码的底层信息!app
// 访问对象自身属性 var users = { 'Tom': 32, 'Bill': 50, 'Sam': 65 }; Object.keys(users).forEach(name => { const age = users[name]; console.log(`User ${name} is ${age} years old!`); }); // 输出结果 User Tom is 32 years old! User Bill is 50 years old! User Sam is 65 years old!
自省在平时的业务开发中很常见,这也是元编程技术的一种使用!ide
顾名思义,代码能够修改自身属性或者其余底层信息!函数
let a = 1; if (a == 1 && a == 2 && a == 3) { console.log("元编程"); }
上述代码在正常状况下是不管如何也没法知足条件输出,ui
由于一个值不可能同时知足等于一、二、3;可是,利用元编程就能够实现:.net
// 修改自身 let a = { [Symbol.toPrimitive]: ((i) => () => ++i)(0); } if (a == 1 && a == 2 && a == 3) { console.log("元编程"); } // 输出 '元编程'
Symbol.toPrimitive 是一个内置的 Symbol 值,它是做为对象的函数值属性存在的;
在对象转换为原始值的时候会被调用,初始值为1,调用一次+1,就能够知足a == 1 && a == 2 && a == 3
;
上述函数变形为:
let a = { [Symbol.toPrimitive]: (function (i){ return function (){ return ++i } })(0) } if (a == 1 && a == 2 && a == 3) { console.log("元编程"); }
在开发过程当中自我修改应该要尽力避免,能够想象:正在使用一个数据的同时又在修改这个数据,后容易形成不可预期的错误!
代码修改默认的语言行为
而使其余代码受影响,最明显的体现为改变其它对象的语义!
在元编程中,调解的概念相似于包装、捕获、拦截。
Object.defineProperty()
就是典型的 调解 的运用:
var sun = {}; Object.defineProperty(sun, 'rises', { value: true, configurable: false, writable: false, enumerable: false }); console.log('sun rises', sun.rises); sun.rises = false; console.log('sun rises', sun.rises); // 输出 sun rises true sun rises true
上面例子中,新建立了一个普通对象 sun
,以后经过Object.defineProperty
改变了它的语义:为其定义了一个不可写的 rises
属性。
MDN:
从ECMAScript 2015 开始,JavaScript 得到了Proxy
和Reflect
对象的支持,容许你拦截并定义基本语言操做的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。借助这两个对象,你能够在 JavaScript 元级别进行编程。
// Proxy的handler 和 Reflect 对象 13 个方法 .apply() // 对一个函数进行调用操做, 和 Function.prototype.apply() 功能相似 .construct() // 对构造函数进行 new 操做,至关于执行 new target(...args) .get() // 获取对象身上某个属性的值,相似于 target[name]。 .has() // 判断一个对象是否存在某个属性,和 in 运算符 的功能彻底相同 .ownKeys() // 返回一个包含全部自身属性(不包含继承属性)的数组,相似于 Object.keys() .set() // 将值分配给属性的函数。返回一个Boolean,若是更新成功,则返回true .setPrototypeOf() // 设置对象原型的函数. 返回一个 Boolean, 若是更新成功,则返回true。 .defineProperty() // Object.defineProperty() 相似 .deleteProperty() // 做为函数的delete操做符,至关于执行 delete target[name]。 .getOwnPropertyDescriptor() //对象中存在该属性,则返回对应的属性描述符,相似于 Object.getOwnPropertyDescriptor() .getPrototypeOf() // 相似于 Object.getPrototypeOf()。 .isExtensible() // 相似于 Object.isExtensible() .preventExtensions() // 相似于 Object.preventExtensions()。返回一个Boolean。
Reflect
是一个内置对象,它提供了可拦截 JavaScript 操做的方法。
该方法和Proxy的handler
相似,但 Reflect
方法并非一个函数对象,所以它是不可构造的。
Reflect
的全部属性和方法都是静态的(就像Math
对象);
以 Reflect.has()
为例,与 in
运算符对比,检测一个对象是否存在特定属性:
Reflect.has(Object, "assign"); // true "assign" in Object; // true
在 ECMAScript 6 中引入的 Proxy
对象能够拦截某些操做并实现自定义行为。
例如获取一个对象上的属性:
let handler = { get: function(target, name){ return name in target ? target[name] : 42; }}; let p = new Proxy({}, handler); p.a = 1; console.log(p.a, p.b); // 1, 42
Proxy
对象定义了一个目标(这里是一个空对象)和一个实现了 get
劫持的 handler 对象。
代理的对象在获取未定义的属性时不会返回 undefined
,而是返回 42。
js中使用元编程技术生成代码最多见的函数 eval()
:函数会将传入的字符串当作 JavaScript 代码进行执行。
let str = "function sayHello(){console.log('hello')}"; eval(str); sayHello(); // 输出 hello
元编程是当你将程序的逻辑转向关注它自身(或者它的运行时环境)时进行的编程,要么为了调查它本身的结构,要么为了修改它。
元编程的主要价值是扩展语言的普通机制来提供额外的能力
。