I see decorators being used today already in some javascript code. My question is really two fold.
First:
If decorators have not even been finalized how is it possible to use them in production code, today? Won't browser support be non-existent?
Second:
Given it is possible to use it today, as some open source projects would suggest, what's a typically recommended setup for getting decorators to work?
You're right, ES2016 decorators are not yet part of the spec. But it doesn't mean we can't use it today.
First let's take a step back and go over "what is a decorator". Decorators are simply wrappers that add behavior to an object. It's not a new concept in javascript (or programming in general), it's actually been around for a while...
Here's a basic example of a decorator that checks permissions:
function AuthorizationDecorator(protectedFunction) {
return function() {
if (user.isTrusted()) {
protectedFunction();
} else {
console.log('Hey! No cheating!');
}
}
}
Using it would look like this:
AuthorizationDecorator(save);
You see all we're doing is simply wrapping up some other function. You can even pass a function through multiple decorators each adding a piece of functionality or running some code.
You can even find some old articles explaining the decorator pattern in javascript.
Now that we understand decorators are actually something we (javascript community) were always able to do, it probably comes as no shock that really when we utilize ES2016 decorators today they are simply just being compiled down to ES5 code hence why you maintain browser compatibility. So for the time being it is simply syntactic sugar (some really sweet sugar I might add).
As for which compiler to use to convert your ES2016 code to ES5 code, you have some choices: Babel and Traceur are the most popular.
Here's further reading on Exploring ES2016 Decorators.
#Class decorators support can be enabled in Babel/Traceur
Babel:
$ babel --optional es7.decorators
Source: Exporing ES7 Decorators - Medium
Traceur:
traceurOptions: {
"annotations": true
}
#Property decorators are not supported
...and since each #Property provides a unique functionality, each requires a different approach to desugaring in ES6/7.
Here's how you use #Inject:
Typescript
exports class ExampleComponent {
constructor(#Inject(Http) http: Http) {
this.http = http;
}
}
ES 6/7
exports class ExampleComponent {
constructor(http) {
this.http = http;
}
static get parameters() {
return [[Http]];
}
}
Source: https://stackoverflow.com/a/34546344/290340
Update:
It looks like Babel changed the way decorators are configured and the article is out-of-date. Here's a link to the new approach.
In short; yes you can use #Class decorators in ES6/7; no property decorators aren't supported in ES6/7 so you'll have to use workarounds.
There are some solutions to use decorators:
babel - an es next to es5 compiler with support of decorators.
traceur - another es next to es5 compiler by google.
typescript - a typed superset of javascript language that supports decorators.
There are some difference how these tools transpile a "modern" javascript to an older one so you can explore it if needed as they have online playgrounds.
Related
I am using ES2015 Modules increasingly.
More often than not, I'm leaning towards loading modules via the dynamic import() syntax, using variations of the following pattern:
import(moduleURL.js).then(importedModule => myFunction(importedModule));
In early 2020, this seems to be an approach without pitfalls, given that import() now has near-universal browser support.
See: https://caniuse.com/#feat=es6-module-dynamic-import
But does this also mean that static import statements like:
import * as myModule from 'moduleURL.js'
import { myNamedExport } from 'moduleURL.js'
import myDefaultExport from 'moduleURL.js'
are now effectively (albeit not officially) deprecated?
If not - and my guess is that they are not effectively deprecated - what are the concrete technical advantages of static import statements over dynamic import()?
In what situations or contexts would I be well advised to use the former over the latter?
Further Reading:
Static import statements:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
Dynamic import():
Dynamically importing ES modules with import() by Leonardo Bruno Lima
Why am I asking for the advantages of ES2015 static import?
It's not just idle curiosity.
I'm looking to standardise certain processes in my workflow and I'm pretty happy with import().
But before I adopt import() everywhere, I want to be sure that I'm not missing out on specific functionality or advantages offered by static import statements but absent from their younger, more dynamic counterpart.
It's easier to work with synchronous code than asynchronous code. While you could use dynamic import everywhere, that would require putting .thens (or top-level awaits, once that's supported) everywhere. This alone would result in an annoying amount of syntax noise. It gets worse when you have to import multiple modules at the same time:
Promise.all([
import('foo'),
import('bar')
])
.then(([foo, bar]) => {
// do stuff with foo and bar
});
compared to
import foo from 'foo';
import bar from 'bar';
Less syntax noise means less surface area for bugs.
Also note that while dynamic import is widely supported, it's not universally supported. A relatively small proportion of users are still on old Edge, or at FF 56 or below, or at Chrome 62 and below, or even on IE11. Some might think that it's better to provide scripts that will work for basically everyone, rather than just the vast majority. (This is one reason why Babel is still in such common usage today)
I've recently been trying the online Babel transpiling tool, and I've noticed that when transpiling a class to ES2015, it doesnt use javascript class and creates var _createClass = function () {... boilerplate instead: Demo
Yet, the javascript class keyword has been added in ES2015. Source
The javascript class is used only when ticking ES2016.
Why is that?
I've noticed that when transpiling a class to ES2015, it doesnt use javascript class: https://babeljs.io/repl#?presets=es2015&…
You weren't transpiling to ES2015, you were transpiling from ES2015 to an older version. The ES2015 preset selects all the transformations that generate ES3/5 code for ES2015 stuff.
The javascript class is used only when ticking ES2016.
Yes, it keeps the class syntax and other features from ES2015 when you only transpile the ES2016 (or higher) stuff.
Yet, the javascript class keyword has been added in ES2015
Yes, the keyword was defined on ES2015 and class was already a reserved word before that, but the actual implementation is a different story. As #AshKander mentions in they comment, the point of using babel with a specific target is to get that code to work on all browsers that support such target.
List of reserved keywords (present and future)
Experimenting with destructuring and found that the same code works on stackoverflow and not Codepen (toy gets "undefined"): http://codepen.io/tsalexey544/pen/VjWxmm?editors=0010#
What does it mean? should I worry when using destructuring in my projects?
let obj = {
species: "Cat",
// toy: "ball",
}
function whatDoTheyDo ({species, toy = "ball"}) {
return `The ${species} playes with a ${toy}`
}
document.write(whatDoTheyDo(obj));
You just need to set the Preprocessor to babel in CodePen, otherwise it will use standard ES5, where destructuring is not supported.
If you want to use ES6/ES7 features you have to "transpile" your code back to ES5 using certain tools such as Babel. Some browsers already support some ES6 features, but full support is still somehow spotty.
Edit - To answer your question: YES, you should worry about serving valid ES5 code, since ES6 is not yet fully supported. At the very minimum you should feed your code to Babel and publish the resulting code, but I strongly suggest looking it Webpack and going for a full toolchain
It says here (http://docs.sonarqube.org/display/PLUG/JavaScript+Plugin) that the SonarQube JS plugin has supported ES6 (which I believe is also know as ES2015) since version 2.0.
I have version 2.8 installed in SonarQube 4.5.6, but I see errors like this in my analysis log -
17:54:58.185 ERROR - Parse error at line 21 column 17:
'individualIndex': -1,
'individualName': {},
'individualSearchResults': [],
'individualEmail': '',
'individualMobile': '',
'dunsNumberRequired': false,
'allDone': false
});
}
static actions = {
^
...and that looks to me like SonarRunner is tripping on the static keyword.
So - does SonarQube really support ES2015? Or do I need to configure it differently, perhaps? My config is as follows -
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=$BUILD_NUMBER
sonar.exclusions=node_modules/**,tests/**,bin/**,config/**,docs/
sonar.sources=.
sonar.javascript.lcov.reportPath=coverage/lcov.info
Thanks.
static actions = {
This is a static class property and those are not part of the ES 2015 spec. So the SonarQube JS plugin is right to complain about it. Class properties are currently at a proposal stage, so it's unclear when (or if) they will make it into the language.
The status and development of the ECMAScript language can be confusing at times. The fact that transpilers support most proposals (i.e. features that are not part of any specification yet) makes the situation even worse.
If you want to target a specific version of ECMAScript, but are unsure about the supported features, then you can, of course, consult the language sepecification. I can also recommend this compatibility table.
When I read the source code of Zombie.js, I found async/await keyword:
before(async function() {
await browser.visit('/streaming');
await browser.pressButton('1');
});
https://github.com/assaf/zombie/blob/41807a39c7aa1a13c4ef51575e0d581be96175bc/test/event_source_test.js#L60
Why can it use such keywords? What is the behaviour of the code? I tried to find some clue from the codebase, but not lucky
If we check out the gulpfile used to build that project, we can see that the source is piped through babel.
gulp.task('build', ['clean'], function() {
return gulp
.src('src/**/*.js')
.pipe(sourcemaps.init())
.pipe(babel({
experimental: true,
loose: 'all',
optional: [
'bluebirdCoroutines',
'runtime'
]
}))
});
Babel is a transpiler which allows you to write ES6+ code and transpile it back to ES5.
Babel will turn your ES6+ code into ES5 friendly code, so you can start using it right now without waiting for browser support.
If we check out the docs on Babel's site, we can see that in the ES7 section of the experimental section, there is an implementation for asyncFunctions.
These keywords are part of the ES7 specification, but they haven't stabilised. Hence them being included as experimental features of Babel.
In simplified terms, an async function will allow you to await a call which returns a promise.
async function() {
// flow will be suspended here until
// the promise returned by someFunction is resolved
await someFunction()
}
ES6 will include what are known as generators, which do a similar thing, but aren't specific to promises. You might start seeing the yield keyword around, or functions declared like this function* () {}. They are what's known as generator functions.
There is a particularly good article from PouchDB which explains a real world use case for these features.
Those keywords aren't available in EcmaScript 5, but are proposed for EcmaScript 7 (the version coming after the upcoming version 6). Right now you can use Babel to transcompile ES6 and some ES7 code into ES5, with some exceptions (notably proxying since it's not possible within ES5). Specifically for this, you can reference Babel's experimental features, specifically Stage 1, es7.asyncFunctions.
It is a new feature planned for ES7 that depends on promises and generators.
Why can it use such keywords?
Beacause they transpile the code with Babel.
What is the behaviour of the code?
It basically means the following in continuation passing style:
before(function() {
browser.visit('/streaming', function() {
browser.pressButton('1');
});
});
It's one of the best things to ever come to JavaScript besides modules and classes. Check out these two articles and library to get an feel for the power:
http://jlongster.com/Taming-the-Asynchronous-Beast-with-CSP-in-JavaScript
http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html
https://github.com/dvlsg/async-csp
Additionally, a search for "javascript async await" will surface some more good articles and examples.