关于元编程到底是什么意思,可以参见 怎么理解元编程?
我的理解是,可以改变程序原有的默认行为,例如数组的内置方法、对象的原型链方法、字符串属性等,就叫元编程
在 JavaScript
中,eval
、new Function
、Proxy
、Reflect
、Symbols
都是可用于进行元编程的特性
eval('function a(){console.log(2)};a()')
// 执行输出 => 2
eval
执行了一段字符串,将字符串当成代码执行,却让这段字符串拥有了函数般的能力,所以可以算是元编程
// args 是传入函数的参数,functionBody是 字符串形式的函数体
new Function ([arg1[, arg2[, ...argN]],] functionBody)
const fn = new Fcuntion(a, b, 'return a + b')
fn(2, 3) // ==> 5
new Function
将自己的字符串参数当成代码来执行,拼装成了一个函数
ES6
新增特性
Proxy
用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种 元编程(meta programming
),即对编程语言进行编程
const obj = new Proxy({}, {
get(target, key, receiver) {
return target[key]
},
set(target, key, value, receiver) {
target[key] = value % 2 === 0 ? 2 * value : value
}
})
obj.age = 4
obj.gender = 1
console.log(obj.age) // => 8
console.log(obj.gender) // => 1
Proxy
共支持 13
种拦截操作:
get(target, propKey, receiver)
:拦截对象属性的读取,比如proxy.foo和proxy['foo']
set(target, propKey, value, receiver)
:拦截对象属性的设置,比如proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值has(target, propKey)
:拦截propKey in proxy
的操作,返回一个布尔值deleteProperty(target, propKey)
:拦截delete proxy[propKey]
的操作,返回一个布尔值ownKeys(target)
:拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in
循环,返回一个数组该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性getOwnPropertyDescriptor(target, propKey)
:拦截Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象defineProperty(target, propKey, propDesc)
:拦截Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值preventExtensions(target)
:拦截Object.preventExtensions(proxy)
,返回一个布尔值getPrototypeOf(target)
:拦截Object.getPrototypeOf(proxy)
,返回一个对象isExtensible(target)
:拦截Object.isExtensible(proxy)
,返回一个布尔值setPrototypeOf(target, proto)
:拦截Object.setPrototypeOf(proxy, proto)
,返回一个布尔值如果目标对象是函数,那么还有两种额外操作可以拦截apply(target, object, args)
:拦截Proxy
实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...) construct(target, args)
:拦截Proxy
实例作为构造函数调用的操作,比如new proxy(...args)
关于 Proxy
更多参见 proxy
ES6
新增特性
Reflect
的特性有如下几点:
- 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上 - 修改某些
Object
方法的返回结果,让其变得更合理 - 让
Object
操作都变成函数行为。某些Object
操作是命令式 Reflect
对象的方法与Proxy
对象的方法一一对应,只要是Proxy
对象的方法,就能在Reflect
对象上找到对应的方法。这就让Proxy
对象可以方便地调用对应的Reflect
方法,完成默认行为
Reflect
存在 13
个静态方法,与 Proxy
的 13
个拦截操作一一对应
ES6
新增特性,一种新的原始数据类型
const obj = {
toString() {
return 'abc'
}
}
const sym = Symbol(obj)
console.log(sym) // => Symbol(abc)
Symbol
改变了 obj
默认的 toString
方法,可以认为是一种元编程