FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

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

Open FrankKai opened 5 years ago

FrankKai commented 5 years ago

最近在看《深入浅出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 commented 5 years ago

基本概念

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

FrankKai commented 5 years ago

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的情况。

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

FrankKai commented 5 years ago

术语

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

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

FrankKai commented 5 years ago

可撤销代理

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 commented 5 years ago

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 commented 5 years ago

总结

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

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