【javascript】反射Reflect

前言

  • 以前学习javascript反射api没懂为啥这个api叫Reflect?甚至这个api还相当好用,它可以返回布尔值表示正确执行没有。直到我最近学习了java,发现java也是有反射。
  • 后来查了下反射主要指的是在不清楚一个东西内部状态方法时可以枚举其状态或者方法。
  • 当然也有狭义上的理解,就是通过字符串来运行某东西内部状态和方法。
  • 所以在大量说js反射机制的文章里,基本就是把一个对象遍历下,然后说这就是js的反射机制。
  • 本篇的主角时Reflect的api,如果读者还没听过这个api,那真应该好好看看,浏览器除了ie都有这玩意了。

参考资料

前置复习

  • 我们知道,js的对象是有原型链的, 那么在枚举其方法或者状态时,就有好几种方法,同时也有不同效果,可以看一下下面的输出结果:
let a = {};
a.__proto__.myproto = 2;
a.yehuozhili = 3;
a[Symbol.for("yehuozhili")] = 4;
Object.defineProperty(a, "enu", { value: 5, enumerable: true });
Object.defineProperty(a, "notenu", { value: 6, enumerable: false });
Object.defineProperty(a, Symbol.for("a1"), { value: 5, enumerable: true });
Object.defineProperty(a, Symbol.for("a2"), { value: 6, enumerable: false });
Object.defineProperty(a.__proto__, Symbol.for("proto"), {
	value: 22,
	enumerable: true,
});
Object.defineProperty(a.__proto__,  Symbol.for("proto2"), {
	value:21,
	enumerable: false,
});
console.log(a);
console.log("________in__________");
for (let i in a) {
	console.log(i);
}
console.log("________Object.getOwnPropertyNames__________");
console.log(Object.getOwnPropertyNames(a));
console.log("________Object.getOwnPropertySymbols__________");
console.log(Object.getOwnPropertySymbols(a));
console.log("________Object.keys__________");
console.log(Object.keys(a));
console.log("________Reflect.ownKeys__________");
console.log(Reflect.ownKeys(a));
console.log("________Object.hasOwnProperty__________");
console.log(a.hasOwnProperty("myproto"));
console.log(a.hasOwnProperty("yehuozhili"));
console.log(a.hasOwnProperty(Symbol.for("yehuozhili")));
console.log(a.hasOwnProperty("enu"));
console.log(a.hasOwnProperty("notenu"));
console.log(a.hasOwnProperty(Symbol.for("proto")));
console.log(a.hasOwnProperty(Symbol.for("proto2")));

{ yehuozhili: 3, enu: 5, [Symbol(yehuozhili)]: 4, [Symbol(a1)]: 5 }
in__
yehuozhili
enu
myproto
Object.getOwnPropertyNames__
[ ‘yehuozhili’, ‘enu’, ‘notenu’ ]
Object.getOwnPropertySymbols__
[ Symbol(yehuozhili), Symbol(a1), Symbol(a2) ]
Object.keys__
[ ‘yehuozhili’, ‘enu’ ]
Reflect.ownKeys__
[
‘yehuozhili’,
‘enu’,
‘notenu’,
Symbol(yehuozhili),
Symbol(a1),
Symbol(a2)
]
Object.hasOwnProperty__
false
true
true
true
true
false
false

  • 可以看见,如果要遍历不包括原型链的一个对象的可枚举和不可枚举属性,那么Reflect.ownKeys是最全的。

Reflect性能

  • 在java中,Reflect是通过字符串执行做的,所以性能比较差,那么在javascript中,是不是也是性能比较差呢?
  • 这里我简单做了下测试:
const a = {
	yehuozhili: "1231",
	asdsadsad: "SAdsaf",
};
function testOwnKeys() {
	const date1 = Date.now();
	for (let i = 0; i < 100000000; i++) {
		Reflect.ownKeys(a)
	}
	console.log(Date.now() - date1);
}
testOwnKeys();
  • 我分别使用Reflect.ownKeys和Object.keys一直执行,Object.keys平均在1000左右就能结束,而Reflect.keys运行需要20000左右。
  • 说明js的Reflect确实比一般的api更耗性能。

使用场景

1、深拷贝

  • 有个常考的面试题,就是写深拷贝。 遍历时候就可以使用它。

2、替代Object.defineProperty

  • Reflect.defineProperty(target, propertyKey, attributes)对比Object.defineProperty在某些时候有些优势,比如定义不上会抛出错误,而Reflect会返回布尔值。

3、替换apply.call

  • 有时候可能会这么写函数:
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
  • 如果用Reflect会比较易懂点:
Reflect.apply(Math.floor, undefined, [1.75]);

4、装饰器

  • 这个本质上说和Reflect没啥关系,但是这个思路和提案应该是别的语言比如Java搞来的,java中不叫装饰器叫注解,反射可以读取注解,然后根据这个造一个反射框架。而js与ts的装饰器实际是需要编译的,不是很好实现注解效果,装饰器编译后默认调的是Reflect.decorate与Reflect.metadata
var __decorate =
	(this && this.__decorate) ||
	function (decorators, target, key, desc) {
		var c = arguments.length,
			r =
				c < 3
					? target
					: desc === null
					? (desc = Object.getOwnPropertyDescriptor(target, key))
					: desc,
			d;
		if (
			typeof Reflect === "object" &&
			typeof Reflect.decorate === "function"
		)
			r = Reflect.decorate(decorators, target, key, desc);
		else
			for (var i = decorators.length - 1; i >= 0; i--)
				if ((d = decorators[i]))
					r =
						(c < 3
							? d(r)
							: c > 3
							? d(target, key, r)
							: d(target, key)) || r;
		return c > 3 && r && Object.defineProperty(target, key, r), r;
	};
var __metadata =
	(this && this.__metadata) ||
	function (k, v) {
		if (
			typeof Reflect === "object" &&
			typeof Reflect.metadata === "function"
		)
			return Reflect.metadata(k, v);
	};
  • 由于这个装饰器编译出来并不能很好的完成注解的功能,所以微软大神写了个扩展。
  • reflect-metadata
  • 大概看了下这个库,改写了编译后的装饰器方法,然后弄个map存取信息。
上一篇:巧用 -webkit-box-reflect 倒影实现各类动效


下一篇:ES6 Proxy与Reflect实现观察者模式