Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

es2015引入的元编程是什么 #157

Open
FrankKai opened this issue Aug 21, 2019 · 6 comments
Open

es2015引入的元编程是什么 #157

FrankKai opened this issue Aug 21, 2019 · 6 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Aug 21, 2019

最近在看《深入浅出vue.js》作者屡次提到es6引入的元编程。
引入元编程前vue不能xxx,引入元编程后vue就可以xxx。
Proxy和Reflect倒是听说过,但是没有想过应用场景。
所以需要一探究竟元编程的原理,也为对学习未来版本的vue做一个铺垫。

学习资料:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Meta_programming

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 21, 2019

基本概念

  • 元编程

元编程(英语:Metaprogramming),又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。
编写元程序的语言称之为元语言。被操纵的程序的语言称之为“目标语言”。一门编程语言同时也是自身的元语言的能力称之为“反射”或者“自反”。

引申问题:javascript是自身的元语言吗?es6之后是的,因为es6之后,js增加了反射。

  • es6通过Proxy和Reflect执行元编程,在JavaScript的meta level编程。
  • Proxy和Reflect可以拦截和定义基础余元操作的行为:属性查找,赋值,枚举和函数调用
  • Proxy类 var p = new Proxy(target, handler); trap指的是对象的属性权限,类似操作系统中的trap。target可以是数组,对象或者是proxy。handler是包含了很多函数的对象,在其中定义各种各样的自定义行为,在元级别对对象做定义。经常用到的是handler.set(),handler.get(),除此之外还有:getPrototypeOf,setPrototypeOf,isExtensible,preventExtensions,getOwnPropertyDescriptor,defineProperty,has,deleteProperty,ownKeys,apply,construct。几乎每一个自定义行为在Object原型链上都有对应的trap。
  • Reflect Reflect和Proxy不一样,Reflect类似Math,仅仅是一个内建对象,不是构造函数。它的主要作用是为可拦截JavaScript操作提供方法,这些方法和proxy handler的方法类似。甚至有些时候与Object的方法一致。但是还是有一些细微的区别,可以查看这个对比列表:Reflect和Object的方法对比

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 21, 2019

Proxies

es6的Proxy允许我们拦截一个操作,然后实现自定义的行为。例如获取对象的一个属性:

var handler = {
  get: function(target, name) {
    return name in target ? target[name] : 42;
  }
};
var p = new Proxy({}, handler);
p.a = 1;
console.log(p.a, p.b); // 1, 42

除了上面的get trap proxy了对象的属性获取以外,还有很多可以使用Proxy的情况。

  • 无trap单纯做转发 var target = {};var p = new Proxy(target, {});
  • 校验 set
  • 扩展构造器 construct,apply 类似class的extend
  • 操作DOM节点 set
  • 修正错误值和额外属性
  • 通过属性找数组

上面的例子在Proxy有详细的介绍

@FrankKai
Copy link
Owner Author

术语

如果学习过Proxy,其实已经对这里的术语很熟悉了。

  • handler 包含了trap的对象
  • trap 属性访问的方法 类似于操作系统的trap
  • target 代理虚拟化的对象 代理的存储后端 根据目标验证对象的不可扩展性或者不可配置性的不变量
  • invariants 自定义操作时保持不变的语义

关于Handler,Trap和Invariants。可以参考这个表格:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Meta_programming#Handlers_and_traps

@FrankKai
Copy link
Owner Author

可撤销代理

Proxy.revocable()用来创建可撤销的Proxy对象。
Proxy.revocable()与new Proxy()一样,都是创建了一个新的Proxy对象。

调用proxy.revoke()可以关闭proxy,关闭以后对于proxy的operation都会报出TypeError。

var revocable = Proxy.revocable({}, {
  get: function(target, name) {
    return '[[' + name + ']]';
  }
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"

revocable.revoke();

console.log(proxy.foo);  // TypeError is thrown
proxy.foo = 1;           // TypeError again
delete proxy.foo;        // still TypeError
typeof proxy;            // "object", typeof doesn't trigger any trap

@FrankKai
Copy link
Owner Author

Reflection

提供了拦截js操作的方法,有助于将默认操作从handler转发到target。

Reflect给人的感觉就是,自己对自己做一些操作更加直观。

类似于一个帮手。

in操作符

Reflect.has(Object, "assign"); // true

更好的指定任意对象作为函数上下文的apply

Math.floor.apply(undefined, [1.75]);
Reflect.apply()使得代码更加精简且容易理解:

Reflect.apply(Math.floor, undefined, [1.75]); // 1;
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);// "hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index;// 4
Reflect.apply(''.charAt, 'ponies', [3]);// "i"

检查属性定义是否成功

Object.defineProperty定义成功返回的是对象,而Reflect返回的是Boolean值。

if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}

@FrankKai
Copy link
Owner Author

FrankKai commented Aug 21, 2019

总结

es6引入的元编程概念,个人认为在Proxy方面的体现比较大。
不过Object作为js中的第一公民,Reflect这个内建对象的引入,使得Proxy使用起来更加方便。

个人认为:Proxy主要作用于元编程definition,Reflect主要作用于元编程usage。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant