项目作者: ukari

项目描述 :
decorators for let
高级语言:
项目地址: git://github.com/ukari/javascript-let-decorators.git
创建时间: 2017-12-24T10:01:09Z
项目社区:https://github.com/ukari/javascript-let-decorators

开源协议:

下载


Decorators for let

Summary

Nowadays, we have the proposal which enable decorators on Class and Object, but it don’t support on Function due to the probably difficult when implement this feature, especially for the exist of function declaration hoisting.

The let variable declaration doesn’t have a declaration hoisting, and it could bind a reference to a identifier. If enable to use decorators on let, it means decorators would works on everything which could be bind to a identifier.

The let decorators not works on the left expression in a let statement, it works on the right identifier in the way of rebind it.

Why Let

let is the only thing which has actual semantic and implement nearly to the class.

Javascript is a language where function hava a first class standing and some people programming in the function way. If we have some good things new for class, the same thing for functional way as a replacement should always be include too.

For let foo = () => {}, if print the foo.name then a “foo” will got. let foo = () => {} is the reality that guys programming in functional ways like me must face to because we don’t have a non-hoisting function defination syntax. And let has been well prepared to play such a role.

If the syntax for non-hoisting function like def is added into the language, decorators could work to def in the same way as let.

Works well with commonjs’s require.

the same

for example

  1. class A {
  2. }
  3. A = 5;
  1. let A = () => {};
  2. A = 5;
  • they won’t be hoist
  • they could be re-assign to another value

Examples

simple let decorator

  1. @inc
  2. let a = 1;
  3. function inc(value) {
  4. return value + 1;
  5. }

custom let decorator

  1. @incn(10)
  2. let a = 1;
  3. function incn(step) {
  4. return function (value) {
  5. return value + step;
  6. }
  7. }

works with Promise

  1. let Async = f => p => p.then(r => f(r), j => j);
  2. let inc = v => v + 1;
  3. let log = v => (console.log(v), v);
  4. (async () => {
  5. @Async(log)
  6. @Async(inc)
  7. let a = new Promise((re, rj) => setTimeout(() => re(1), 1000));
  8. })();

Due to it’s behavior of not working on the right expression instead of working on the left identifier, let decorators could easily be comfortable with the other stuffs.

And easily to understand it’s usage.

works with JSX

  1. let store = {};
  2. @connect(store)
  3. let a = store => () => (
  4. <button onClick={() => store.i += 1}>
  5. + {store.i}
  6. </button>
  7. )
  8. function connect(store) {
  9. return function (comp) {
  10. // blablabla, magic...
  11. return (props) => comp(new Proxy(store, someMagicConfig))(props);
  12. }
  13. }

with the help of connect function, the variable a will be turns into a functional stateful react component.

works with react router v4

  1. @Path("/article/:path*/:title", {
  2. exact: true,
  3. title: ({match: {params: {title}}}) => title,
  4. onload: ({match: {params: {path, title}}}) => fetchArticle({path, title}),
  5. })
  6. @Connect(store)
  7. let Article = () => (
  8. <div>
  9. <div class={title}>{store.title}</div>
  10. <div class={content}>
  11. {Loading(Markdown(store.content), store.loading)}
  12. </div>
  13. </div>
  14. );

Syntax

normal

from

  1. [@decorator1]
  2. [@decorator2]
  3. [... @decoratorM]
  4. let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];

to

  1. let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
  2. [[var1 = decoratorM(var1); ...]
  3. [var1 = decorator2(var1);]
  4. [var1 = decorator1(var1);]]
  5. [[var2 = decoratorM(var2); ...]
  6. [var2 = decorator2(var2);]
  7. [var2 = decorator1(var2);]]
  8. [... [varN = decoratorM(varN); ...]
  9. [varN = decorator2(varN);]
  10. [varN = decorator1(varN);]]

styles

the syntax transforms the decorator in the reassign-style instead of the wrap-style.

for example

  1. @foo
  2. let a = 1;

reassign-style

  1. let a = 1;
  2. a = foo(a);

wrap-style

  1. let a = foo(1);

the reason choosing reassign-style

transform won’t happen in wrap-style due to the advantage of do it reassign-style in some conditions.

the condition: get function name

for example

  1. let foo = fn => (console.log(fn.name), fn);
  2. @foo
  3. let fun = () => {};
  • in reassign-style, the function fun‘s name could be accessible.
  1. let fun = () => {}
  2. fun = foo(fun)
  • in wrap-style, the function fun‘s name couldn’t be logged.
  1. let fun = foo(() => {})

here is a example which needs the feature to get let function’s name.

the condition: a Non-Independent decorator could be valid in wrap-style while invalid in reassign-style
  1. @boo
  2. @bar
  3. @foo
  4. let {a, b} = {a: 1, b: 2}

image that if there are three function foo, bar, boo. When their defination is

  1. function foo(x) {
  2. return null;
  3. }
  4. function bar(x) {
  5. return null
  6. }
  7. function boo(x) {
  8. return {a: 2, b: 3}
  9. }

now, @foo, @bar is actually not valid for the ObjectPattern, if things happen in wrap-style, it won’t be checked, and it means @foo, @bar needs a implicit dependent on the last excute @boo, so they are actually not a independent decorator which could be add or remove alone.

corner

from

  1. @f
  2. let a = b = c = 1;

to

  1. let a = 1;
  2. a = f(a);
  3. b = a;
  4. c = a;

Run examples

I provides some babel plugins and a fork babylon to help preview and experience this syntax, the config could be find in package.json and .babelrc.

install dependencies

  1. npm install

compile by babel

  1. npm run example

view and run example

  1. cat examples/xxx-example.js
  2. cat dist/xxx-example.js
  3. node dist/xxx-example.js