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.
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
.
for example
class A {
}
A = 5;
let A = () => {};
A = 5;
- they won't be hoist
- they could be re-assign to another value
@inc
let a = 1;
function inc(value) {
return value + 1;
}
@incn(10)
let a = 1;
function incn(step) {
return function (value) {
return value + step;
}
}
let Async = f => p => p.then(r => f(r), j => j);
let inc = v => v + 1;
let log = v => (console.log(v), v);
(async () => {
@Async(log)
@Async(inc)
let a = new Promise((re, rj) => setTimeout(() => re(1), 1000));
})();
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.
let store = {};
@connect(store)
let a = store => () => (
<button onClick={() => store.i += 1}>
+ {store.i}
</button>
)
function connect(store) {
return function (comp) {
// blablabla, magic...
return (props) => comp(new Proxy(store, someMagicConfig))(props);
}
}
with the help of connect function, the variable a will be turns into a functional stateful react component.
@Path("/article/:path*/:title", {
exact: true,
title: ({match: {params: {title}}}) => title,
onload: ({match: {params: {path, title}}}) => fetchArticle({path, title}),
})
@Connect(store)
let Article = () => (
<div>
<div class={title}>{store.title}</div>
<div class={content}>
{Loading(Markdown(store.content), store.loading)}
</div>
</div>
);
[@decorator1]
[@decorator2]
[... @decoratorM]
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
[[var1 = decoratorM(var1); ...]
[var1 = decorator2(var1);]
[var1 = decorator1(var1);]]
[[var2 = decoratorM(var2); ...]
[var2 = decorator2(var2);]
[var2 = decorator1(var2);]]
[... [varN = decoratorM(varN); ...]
[varN = decorator2(varN);]
[varN = decorator1(varN);]]
the syntax transforms the decorator in the reassign-style
instead of the wrap-style
.
for example
@foo
let a = 1;
let a = 1;
a = foo(a);
let a = foo(1);
transform won't happen in wrap-style
due to the advantage of do it reassign-style
in some conditions.
for example
let foo = fn => (console.log(fn.name), fn);
@foo
let fun = () => {};
- in
reassign-style
, the functionfun
's name could be accessible.
let fun = () => {}
fun = foo(fun)
- in
wrap-style
, the functionfun
's name couldn't be logged.
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
@boo
@bar
@foo
let {a, b} = {a: 1, b: 2}
image that if there are three function foo, bar, boo. When their defination is
function foo(x) {
return null;
}
function bar(x) {
return null
}
function boo(x) {
return {a: 2, b: 3}
}
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.
@f
let a = b = c = 1;
let a = 1;
a = f(a);
b = a;
c = a;
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.
npm install
npm run example
cat examples/xxx-example.js
cat dist/xxx-example.js
node dist/xxx-example.js