原文地址:Metaprogramming in ES6: Part 2 - Reflect
原文作者:Keith Cirkel
译文出自:掘金翻译计划
转自:https://juejin.im/post/5a0e66386fb9a04523417418
作者:吴晓军
译者:yoyoyohamapi
校对者:IridescentMia ParadeTo
Reflect 是通过自省(introspection)实现反射(Reflection through introspection) ,反射是元编程的一个方面。
—— 通常用来探索非常底层的代码信息。
元编程又是个啥:元编程(笼统地说)是所有关于一门语言的底层机制,而不是数据建模或者业务逻辑那些高级抽象。如果程序可以被描述为 “制作程序”,元编程就能被描述为 “让程序来制作程序”。
Reflect 是一个新的全局对象(类似 JSON 或者 Math),该对象提供了大量有用的内省(introspection)方法(内省是 “看看那个东西” 的一个非常华丽的表述)。内省工具已经存在于 JavaScript 了,例如 Object.keys,Object.getOwnPropertyNames 等等。
“内置方法”
内置方法能够有效地让 JavaScript 引擎在对象上执行一些基础操作。如果你通读了规范,你会发现这些方法散落各处,例如 [[Get]]、[[Set]]、[[HasOwnProperty]] 等等。
其中一些 “内置方法” 对 JavaScript 代码是隐藏的,另一些则应用在了其他方法中,即使这些方法可用,它们仍被隐藏于难于窥见的缝隙之中。例如,Object.prototype.hasOwnProperty 是 [[HasOwnProperty]] 的一个实现。
另一个例子,[[OwnPropertyKeys]] 这一内置方法能获得对象上所有的字符串 key 和 Symbol key,并作为一个数组返回。在不使用 Reflect 的情况下,能一次性获得这些 key 的方式只有连接 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 的结果。
var s = Symbol('foo');
var k = 'bar';
var o = { [s]: 1, [k]: 1 };
// 模拟 [[OwnPropertyKeys]]
var keys = Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o));
assert.deepEqual(keys, [k, s]);
所以,为什么我们仍然新建 API ,而不是直接在 Object 上做扩展呢?且看下节。
反射方法
反射是一个非常有用的集合,它囊括了所有 JavaScript 引擎内部专有的 “内部方法”,现在被暴露为了一个单一、方便的对象 —— Reflect。你可能会问:“这听起来不错,但是为什么不直接将内置方法绑定到 Object 上呢?就像 Object.keys、Object.getOwnPropertyNames 这样”。
原因:
1、反射拥有的方法不仅针对于 Object,还可能针对于函数,例如 Reflect.apply,毕竟调用 Object.apply(myFunction) 看起来太怪了。
2、用一个单一对象贮存内置方法能保持 JavaScript 其余部分的纯净性,这要优于将反射方法通过点操作符挂载到构造函数或者原型上,更要优于直接使用全局变量。
3、typeof、instanceof 以及 delete 已经作为反射运算符存在了 —— 为此添加同样功能的新关键字将会加重开发者的负担,同时,对于向后兼容性也是一个梦魇,并且会让 JavaScript 中的保留字数量急速膨胀。
常见的 Reflect 方法
更详细的说明可以参见原文。
Reflect.apply ( target, thisArgument [, argumentList] )
// ---------------------------------------------------------------------------//
Reflect.construct ( target, argumentsList [, constructorToCreateThis] )
// ES5 风格的工厂函数:
function greetingFactory(name) {
var instance = Object.create(Greeting.prototype);
Greeting.call(instance, name);
return instance;
}
// ES6 风格的工厂函数:
function greetingFactory(name) {
return Reflect.construct(Greeting, [name], Greeting);
}
// ---------------------------------------------------------------------------//
Reflect.defineProperty ( target, propertyKey, attributes )
// ---------------------------------------------------------------------------//
Reflect.getOwnPropertyDescriptor ( target, propertyKey )
// ---------------------------------------------------------------------------//
Reflect.deleteProperty ( target, propertyKey )
var myObj = { foo: 'bar' };
delete myObj.foo;
assert(myObj.hasOwnProperty('foo') === false);
myObj = { foo: 'bar' };
Reflect.deleteProperty(myObj, 'foo');
assert(myObj.hasOwnProperty('foo') === false);
// ---------------------------------------------------------------------------//
Reflect.getPrototypeOf ( target )
// ---------------------------------------------------------------------------//
Reflect.setPrototypeOf ( target, proto )
// ---------------------------------------------------------------------------//
Reflect.isExtensible (target)
// ---------------------------------------------------------------------------//
Reflect.preventExtensions ( target )
// ---------------------------------------------------------------------------//
Reflect.get ( target, propertyKey [ , receiver ])
// ---------------------------------------------------------------------------//
Reflect.set ( target, propertyKey, V [ , receiver ] )
// ---------------------------------------------------------------------------//
Reflect.has ( target, propertyKey )
// 不使用 Reflect.has:
assert(('foo' in myObject) === true);
assert(('bing' in myObject) === false);
// 使用 Reflect.has:
assert(Reflect.has(myObject, 'foo') === true);
assert(Reflect.has(myObject, 'bing') === false);
// ---------------------------------------------------------------------------//
Reflect.ownKeys ( target )
var myObject = {
foo: 1,
bar: 2,
[Symbol.for('baz')]: 3,
[Symbol.for('bing')]: 4,
};
// 不使用 Reflect.ownKeys:
var keys = Object.getOwnPropertyNames(myObject).concat(Object.getOwnPropertySymbols(myObject));
assert.deepEqual(keys, ['foo', 'bar', Symbol.for('baz'), Symbol.for('bing')]);
// 使用 Reflect.ownKeys:
assert.deepEqual(Reflect.ownKeys(myObject), ['foo', 'bar', Symbol.for('baz'), Symbol.for('bing')]);