Overview of JavaScript ES6 features
adrianmejia.com
adrianmejia.com
The well-known good practice: use const by default; use let when it's needed. At the release of ES6, it was the way to go. But everyday I notice libraries—and some really famous— that use let everywhere in their docs, or some really influent developers from Google or Facebook who share samples of code on Twitter using let when it's not needed [1]. I don't know why. Seems like most people now think that const is for declaring constants (in the traditional meaning, like const API_URL) when it's just the normal way to declare variables that don't need to be reassigned (so basically most variables).
Dan Abramov said: "some people say const is ugly" [2]. Well, if it's a matter of appearance...
[1] https://twitter.com/addyosmani/status/789126892402204673
[2] https://twitter.com/dan_abramov/status/783708858803978240
When I have to go back to programming in a language without const declarations it feels bad.
It took about two weeks to train my hands/brain to type const before let/var.
I think running eslint --fix will even change your "let"s to "const"s where appropriate, but don't quote me on that.
Yep, this is the relevant rule - http://eslint.org/docs/rules/no-var
Though it just replaces all var with let. I expected it to intelligently use either const or let depending on how the variable is used ¯\_(ツ)_/¯
It does exactly what you describe :)
e.g.
const x = {};
x.foo = 'it works!' immut x = {};
x.foo = 'it doesn't work! :)'
At the moment, we can use these libs to achieve itJust use `const` + `Object.freeze()`; it'll get you 99.9995% of exactly what you want.
Fair enough though I'm not convinced you should be hitting this type of use case in your code (kinda inefficient and sounds awkward to make a small change to a large object and yet need both objects to continue to be in memory, separated). At least not typically / frequently.
> if you're trying to keep your data truly immutable, that copy operation is going to be a pain to write with the built-in tools, whereas it's super easy to return a copy of an object with a change to a single, deeply nested property with Immutable.js.
While it is a little bit of a pain almost every framework and probably half of the libraries in existence on npm have a copy function. I'd like to think it's a rare use case but when it's needed you likely don't have to install a new library to handle a copy operation.
> I agree that it's premature to reach for a library before you need it, but let's not pretend there aren't rather large drawbacks to using Object.freeze and Object.assign.
I'm not sure anyone was pretending anything of the sort here and I don't understand the assumption of such. There are plenty of drawbacks but I'm also not convinced it doesn't fit the 95% use case.
> While it is a little bit of a pain almost every framework and probably half of the libraries in existence on npm have a copy function.
The problem isn't just the usability of the copy operation (and even if it were, you still have the performance issue); there is also the problem of JavaScript not enforcing immutability of nested objects with Object.freeze. So if you want to enforce that, you're going to have to call Object.freeze on every child object, which becomes a nightmare to write and maintain.
Sure. That's why I made sure to say it'll give you 99% of what you want. Not 99% of immutable data structure capabilities :).
Though I'm sure folks may disagree with what I'm suggesting they "want" but I do not believe it's a good pattern to follow where a complex object needs to be immutable in JavaScript.
> JavaScript not enforcing immutability of nested objects with Object.freeze. So if you want to enforce that, you're going to have to call Object.freeze on every child object, which becomes a nightmare to write and maintain.
Which is why you should never ever do that. It's a bad pattern in a dynamic language to try and make complex structures completely immutable. It's best to simply find alternative ways of accessing them if you do not want to expose it to modification.
const x = Object.freeze({
y: {
foo: 'bar'
}
});
x.y.foo = 'baz'
console.log(x.y.foo) //baz
immutablejs might be overkill, but not having, and using, a recursive freeze is going to bite a lot of people if the advice is just 'const + Object.freeze'.No one should be trying to use a recursive freeze (if they are I would argue their data structure is poorly suited to be immutable).
I'm not saying `const` + `Object.freeze()` gets you Immutablejs I'm saying it gets you, likely, what you want / need.
const x = Object.freeze([
{id: 1, value: 'foo'},
{id: 2, value: 'bar'},
{id: 3, value: 'baz'},
])
...
//Bug in the code
x[0].value = 'test'
First comment on the bug report was:'This shouldn't be possible as the array is const and frozen'.
their data structure is poorly suited to be immutable
An array of object seems completely reasonable. Even an array of objects, where those objects themselves have keys which are objects/arrays doesn't seem unreasonable.
We'll just have to agree to disagree.
Yeah const and Object.freeze() are not exactly the most intuitive depending on your level of JavaScript internals knowledge (and even then I don't think const is very intuitive but I digress).
> We'll just have to agree to disagree.
Fair enough! I would just caution trying to make complex objects immutable; it can be handy if you're developing a library and don't trust the dev on the other side (to a degree, copying may be preferable depending on the context) but it can complicate things and lead to some performance and development pattern issues.
type const * const
Which is a const pointer to a const type, making both the pointer and the data immutable.Now I am going back to change all the object declarations to const.
If it affected the value it pointed to, what would happen in this type of situation?
let x = {};
const y = x;
x.a = 5;The answer is it works fine. x.a === y.a === 5
This is because you are simply declaring the binding of y to the object bound to x constant. This does not impact your ability to rebind x or to alter the contents of the object, it simply prevents you from rebinding y.
let x = {a:5}
x = {}
console.log(x.a) // undefined
--- const y = {a:5}
y = {} // Uncaught TypeError: Assignment to a constant variable.
--- let x = {a:2}
const y = x
x.b = 12
x = {}
x.b = 13
console.log(y) // {a: 2, b:12}
console.log(x) // {b: 13}I guess one alternative idea for how const would work could be an implementation where `x.a = 5` worked but `y.a = 5` failed. But then what happens if you pass `y` into a function which then tries to assign the `a` property on it? Is the const-ness part of the value passed into the function, or is it a property on variable bindings, and you could only pass `y` to functions that accepted a const variable? That kind of function type checking isn't something usual to javascript currently. And then is the const-ness deep? Is `y.foo.a = 5;` blocked? Mutable DOM elements are a big part of javascript. If the object happened to contain a reference to an element that needed to be manipulated, then you won't be able to do something like `y.foo.element.textContent = "new foo content";`. Going down this road it's now getting to be a pretty big feature that doesn't cleanly fit with the rest of the language or common tasks.
Maybe the naming is a little unfortunate: Javascript's `const` has more in common with Java's `final` than C's `const`.
While I agree that const/let is a useful convention for communicating mutability, it isn't nearly a big enough deal to warrant the attention it receives from the community.
It's not just const/let; I rarely make a front end PR that isn't bike-shedded to death over subjective styling choices, single quotes vs double quotes, etc. It often feels like conforming to flavor-of-the-week, subjective decrees from frontend trendsetters is more important than the substantive work that your code is doing.
- Tabs vs spaces.
- Vi vs Emacs
- Weak vs strong typing
- where to place {} in block statements
- where to put commas
No, programming in general is susceptible to these trivial holy wars.
Not trying to derail the convo or take sides, but one of these is not like the others ;)
The ONLY holy war in JS is the semi-colon one
:^)
The point still stands
but yeah.
Sorta. I mean you're right but at the same time if you're using const for an object or array then it doesn't really matter without `object.freeze()` because it's not immutable only the reference is and I don't think most JavaScript developers understand that (at least not most of the ones I've run into).
const foo = {bar: 1}
foo.bar = 2;
It will only avoid having the pointer re-pointed to another object. It's better to just try avoiding global variables, and use a naming convention like uppercase and/or underscore for constants and global variables.Const is probably only meant for constants like the name suggests.
I think something like this is pretty safe from reassigning:
{
let foo = 1
...
}
...
If you want more control over your properties, you can use the ES5 feature "defineproperty" where you can set writeable: false const foo = {bar: 1}
The only thing const about it is the reference `foo` cannot be reassigned: foo = something_else; // error
Unlike `const` in C++, `const` in javascript in not very useful in my opinion. `let` is shorter and more readable.> it can help you write stateless code if you assume immutability
What constitutes holy wars are often the most accessible[1]
I agree that pull requests that mass change from let to const are largely a waste of time, but usage of const vs let is a legitimate discussion within your team.
Const very rarely saves you from bugs and the bugs that saves you from are very easy to find and fix. On the other hand the time wasted by thinking where whether to write const or let and the time wasted by switching consts for lets (and other way around) outweighs the time saved by potentially preventing these easy to find bugs. To sum it up, if consts does not really provide any extra value, why not make your life easier and just use let everywhere.
It was from very senior c++ programmer, so I am not sure how well that translates to JavaScript.
JavaScript const is just a matter of typing 2 more characters and in exchange you make your code more readable (because you communicate "this stuff will never change from here on", which makes understanding the stuff that does mutate easier). There are no far-reaching consequences. Just type "const" everywhere.
I'm not familiar with C++, can you explain more?
First pointers and references. For simplicitly I'm going to ignore references and focus on pointers - the difference is not very interesting in this context. In most languages you probably know, referring to objects is done implicitly: variables that "are" objects are actually references to objects located elsewhere, and variables that are primitive types (numbers, booleans, strings) are just right there. Because of this, in JavaScript you can do this:
var a = {};
var b = a;
b.moo = 6;
a.moo === 6; // true
In here, a and b point to some object that "is" neither a or b - the object itself just floats around in memory and it'll exist until neither a, b, nor anyone else points to it anymore and the GC decides it has to go.In C++, you'll need pointers or references for this, eg.
somestruct a;
somestruct* b = &a; //b now points to a
a.moo = 6;
b->moo == 6; // true
(-> is just C++ shorthand for "follow the pointer and then do a property lookup).Ok now const.
somestruct const a;
a.moo = 6; // error! can't modify a const.
Ok well how about somestruct a;
somestruct* const b = &a;
b->moo = 6;
a.moo === 6; // true
This means the pointer is const. That works because we never change where b is pointing to. So how about: somestruct a;
somestruct const* const b = &a;
b->moo = 6;
a.moo === 6; // true
Oh damn. Crying baby. Const functions will have to wait for some other day.Rust makes it much harder to make these kinds of erros by making variable ownership, reference lifetime, and const-by-default core to the language design. https://doc.rust-lang.org/book/ownership.html ... You do end up spending a lot of time "fighting the compiler," even compared to template-heavy C++ codebases, but the systems you create are free from an entire class of errors.
clang++: error: cannot assign to variable 'b' with const-qualified type 'const somestruct *const'
g++: error: assignment of member ‘somestruct::moo’ in read-only object
const array = [1, 2, 3];
array.push(100); // unexpected?
I prefer to use let for bindings to objects that get mutated, even if the variable is not re-bound.For anyone not in the know, declaring `const` on an object only makes it where you can't re-assign the object, but you can still mutate its 'members'.
Personally, I use `let` just to symbolize that it isn't a constant and does change even though I could declare it a `const`.
I think it makes the code more readable.
The problem is that, while a lot of things can be expressed elegantly this way, sometimes the most maintainable thing, or the most efficient thing, is to have some side effects in a deep loop in a certain algorithm, and it's very difficult to do this with purely functional JS.
The real solution is to allow your const bindings to be mutated when you need to, and comment explicitly to make things perfectly clear!
const a = "SELECT id FROM foo WHERE name CONTAINS r'\\n'";
const b = String.raw `SELECT id FROM foo WHERE name CONTAINS r'\n'`;
You could even assign `String.raw` to a variable first, and then make strings look like raw string literals of other languages: const r = String.raw;
const s = r`SELECT id FROM foo WHERE name CONTAINS r'\n'`;
Another good use of template strings is automatic HTML encoding (with a small module of mine on npm): https://www.npmjs.com/package/auto-html function log(...args) { return args; }
log `abc ${'blah'} fooo\n ${12+3} bar`;
Or you could even shorten the above down to this: ((...args)=>args) `abc ${'blah'} fooo\n ${12+3} bar`;
The expression will evaluate to this: [["abc ", " fooo\n ", " bar"], "blah", 15]
The first argument is an array of the parts of the literal text, and then the rest of the arguments are the values that were passed in. Additionally, the first argument (the array of string parts) has the `raw` property set to point to an array of string parts with the backslash escapes uninterpreted.Here's an example re-implementation of `String.raw`:
function raw({raw}, ...values) {
const parts = new Array(raw.length*2-1);
parts[0] = raw[0];
for (let i=0, len=values.length; i<len; i++) {
parts[2*i+1] = values[i];
parts[2*i+2] = raw[i+1];
}
return parts.join('');
}Example:
var userCollection = db.collection('_users');
var role = 'admin';
db.query(aql`
FOR user IN ${userCollection}
FILTER user.role == ${role}
RETURN user
`)
The template handler returns an object with the parameterized query string and the parameter values, which the `query` method understands. Because collection parameters are syntactically distinct from regular parameters this also avoids accidentally passing in arbitrary strings as collection names -- and of course it completely avoids injection attacks as a category.In my experience this is actually much more comfortable to use than a "fluent" API that tries to map the query language to the programming language (i.e. `select('id').from('foo')` and so on).
Full disclosure: I wrote that library.
It allows passing in a set of supported browsers and transpiling what is required using the lowest common denominator env (uses https://github.com/kangax/compat-table for data).
// .babelrc with preset options
{
"presets": [
["env", {
"targets": {
"chrome": 52,
"ie": 11,
"browsers": ["last 2 versions", "safari 7"]
}
}]
]
}
If you have questions, ask below (or make an issue)e.g: http://caniuse.com/#search=let
const does not have block scope in those browsers either, but will work, adding to your debugging confusion.
template literals and multiline string has no IE support at all (you need edge: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...) so you can forget most most enterprise clients
Similar things are missing for most features from the article.
I wonder how many JS dev have to answer to actual customers because none of my clients in the last decade who would have accepted to have a possible failure on 1/5 of their visitors. Are they all bloggers or working in hype start ups ?
I'd actually recommend avoiding the still-not-standardized features as you'll have less/no change when it lands in browsers.
Regardless, you can easily transpile a codebase to support those browsers.
If you aren't doing that already, you probably should be. Otherwise you're supporting a codebase that is quickly going to become very dated and (in comparison to a more modern codebase) much messier.
A transpiler just adds another layer of complexity that it's best to avoid as much as possible, in my opinion. It's not a perfect example, but if you've ever worked with CoffeeScript and run across a bug or unexpected side effect in the final rendered code . . . that should curb some of the transpiler enthusiasm.
Some of the ES6 features look handy enough, but I'd prefer waiting to see which ones shake out as actually useful over time, versus which are just momentary novelties.
https://babeljs.io/docs/learn-es2015/
One of the features I like is default parameters. I'd like to add a usage that isn't mentioned in the article. It's really handy to pass optional parameters since default parameters can be nested.
class Hoge {
foo({active=true, collapsed=false, silent=false}={}) {
}
}
let hoge = new Hoge();
hoge.foo({collapsed: true}); du -ch ./babel*
from my `node_modules` directory yields 3.2M total
so I'm gonna need a citation on that 600MB claim.node_modules/babel node_modules/babel-core node_modules/babel-eslint node_modules/babel-plugin-array-includes node_modules/babel-plugin-transform-runtime node_modules/babel-preset-node5 node_modules/babel-register node_modules/babel-runtime
Of course you can just pretend I'm lying.
> or example, updating a minor version of babel generated an 800,000-line commit that was difficult to land and triggered lint rules for invalid utf8 byte sequences, windows line endings, non png-crushed images, and more. Merging changes to node_modules would often take engineers an entire day.
I just did a fresh install in a new directory and ended up with 114M worth of dependencies, so I'm not entirely sure what the difference is.
My point is. 50MB, 114MB, or 500MB worth of javascript dependencies is a massive footprint. It works and I'm relatively happy with what it does, but I don't see this as a stable, long term thing.
mkdir babel-size-check
cd babel-size-check
babel-size-check yarn init -y
yarn init v0.16.0
warning The yes flag has been set. This will automatically answer yes to all questions which may have security implications.
success Saved package.json
Done in 0.05s.
babel-size-check yarn add babel babel-core babel-eslint babel-plugin-array-includes babel-plugin-transform-runtime babel-preset-node5 babel-register babel-runtime
yarn add v0.16.0
info No lockfile found.
[1/4] Resolving packages...
warning babel@6.5.2: Babel's CLI commands have been moved from the babel package to the babel-cli package
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 80 new dependencies.
...
Done in 1.94s.
babel-size-check du -ch -d1
17M ./node_modules
17M .
17M totalWe get a lot of millage out of it even when our target platforms support all the language features we need. Your millage may vary.
Have you tried Minify(Babili) by the way?
E.g.: until some months ago just putting "let foo" into a function would cause V8 to bailout (meaning the whole function gets executed slowly, even if the actual let statement gets removed as dead code).
Unfortunately I've never found any good references on the optimizability of ES5+ features, so I've been avoiding them so far.
The best suite I've seen is https://kpdecker.github.io/six-speed/ which measures node and the various modern browsers which Sauce Labs supports and appears to be run semi-regularly.
This might just be an isolated incident, but it shakes my confidence in the utility of the six-speed suite. I actually do want to know whether there's a speed difference between const { a } = obj vs const a = obj.a, and the suite does not test that. (Worse, it kind of claims that it does, but reports something else instead.)
If 'let' prevented inlining, I would want to know, but I'd have to look very closely at the six-speed benchmarks to figure out whether it's detecting that. And the range of subtle reasons for deoptimization is vast, so despite working on a JS engine myself, I doubt I'd be able to tell whether a given microbenchmark is meaningful or not.
(Note that the Firefox devtools does have a "Show JIT Optimizations" that can tell you why things aren't getting optimized, but it's incredibly cryptic, undocumented, and scaremongering.)
I think for-of is getting pretty good. It's a bit of a pain to optimize because the iteration protocol in ES6 is designed in such a way that you have to do heroics (scalar replacement) to have any hope of optimizing it well. But engines are getting there.
That's the planning doc for v8 optimization of ES2015+ and an interesting read.
At least I haven't received any complaint yet, and if you can optimize early on without any short-term cost then it's the best thing you can do.
Thank god Chrome and Firefox dominate the market.
For example, in my opinion
const Header = ({ children, iconName, iconSize, title }) => { ... };
is more readable than const Header = (props) => { ... }; const Header = ({ children, iconName, iconSize, title }) => { ... };
Once you get used to the destructuring parameter idiom, sure. It also conveys more information.But that statement is overloaded in that it makes use of implicit object shortcuts which has a bit of a learning curve for longtime ES5 users.
const Header = ({
children: children,
iconName: iconName,
iconSize: iconSize,
title: title }) => { ... };
When object destructuring is nested it can be confusing and more verbose. function thisIsBad ({
someKey: renamedSomeKey,
someOtherKey: renamedSomeOtherKey = 'otherDefaultValue',
meta: { innerMetaKey = 'someInnerMetaKeyValue', otherInnerMetaKey, ...remainingMeta }
} = {
someKey: 'defaultValue',
someOtherKey: SOME_CONSTANT,
meta: {}
}) {
// ...
return { someKey: renamedSomeKey }
}
My favourite messed up part of the syntax is the way the `:` character is used to describe either the default value of a property or a way of renaming the name of a property internally (depending on whether it is to the left or right of an `=`). And also, the way you can destructure the inside of an object, and automatically lose the original value. For example `meta` is not accessible within the function defined above.This is syntax which can simplify your code if you apply it carefully, but will ruin your code if you over-use it.
Like others are saying, though, the array destructuring seems fine.
const getObj = (id, store) => { return { id: id name: store.something.name }; };
The linter gave an error on it because I used {id: id}. It was like the linter was trying to make my code harder to read.
I think es6 in the wrong hands quickly falls prey to the problems of ruby/scala where it can become incredibly terse and hard to parse unless you are used to the author's particular style.
const getObj = (id, store) => { return { id } }
You could write: const getObj = (id, store) => ({ id })I find `return` to be quite distracting and annoying for a small function that spans part of a line, while you and some others may prefer the explicitness of the `return` keyword.
I would agree that nested destructuring should be used quite cautiously. Code legibility is incredibly important to the success of any serious project.
This is because of hoisting. Not quite right as described.
Who comes up with these "rules"? This is not a hard and fast rule. Manipulating the prototype of an object is not "dangerous".
I'm sick and tired of some loud mouth saying something is dangerous without explaining why. WHY? Why it it dangerous? Don't talk at me. Provide me a sound reason and case to justify what you say. Talk is cheap.
It makes me not trust anything else in this article when someone is flippant.
This has some warnings at the top as to why.
One points to the parent of an object and the other points to the object's prototype (which in turn has it's own `__proto__ property). Accessing `prototype` is just fine, but if you want to access `__proto__` then you should use `Object.create()` instead.
Cool stuff: Tail call elimination. Arrow functions. Assignment spread for multiple return values and "rest" / default parameters. Proxying (combined with computed get/set on properties) for easy decoration.
Classes and let: meh.
I agree with you on classes, but that's probably more because I've never been a big fan of OO in practice. It doesn't really seem to have seen much use in the greater js ecosystem though, unlike let/const.
What is the difference between let and global variables? There are hundreds of articles written about the doom associated with PHP globals, but let appears to be universally lauded. I must be missing something, but I can't tell where.
var foo;
var bar;
{
let foo = "hello";
var bar = "world";
}
console.log(foo);
console.log(bar);
This produces: undefined
world
The reason being that the `let` statement restricted that variable to the block it was in (defined by the { and }). `var` declares the variable globally, allowing it to be accessed outside of the {}.We prefer now to use `let` and `const` over `var` because it doesn't pollute the global namespace. With the asynchronous nature of Javascript, it's theoretically possible for you to declare a variable with `var`, assign it a value, then immediately use that value and find that it's different than what you expected because of another function using the same variable name. This isn't possible with `let`.
let x = 'outer';
function test(inner) {
if (inner) {
let x = 'inner';
return x;
}
return x; // gets result from line 1 as expected
}test(false); // outer
test(true); // inner
This makes it seem like let creates global variables. Why would you want to return a variable from outside the function? Doesn't that create massive overhead in terms of keeping track where variables are initially set? Easy to understand in this example, but what if let x = 'outer'; is defined at the top of a 5000 line script and this function appears near the bottom?
Edit: Turns out I don't know how to format code. This is in the first example of section 3.1 Block Scope Variables.
This is mostly just a case of 'let' restricting a variable to the block it is in, and the child blocks. In your example, `let x = 'outer';` is sort of acting like a global variable, but the importance is that if another script were to be running, it could not access that instance of 'x'.
Somebody writing stuff, into the global namespace, directly in <script> tags, has bigger problems :-)
I'll read more into uses of let over var. Function level scoping a la var feels like less mental overhead, but as I read more I'm sure my opinion will change.
Can you give an example ? My understanding is that the closure freeze the variable in the time the function was called. It can happen if you do not use a closure (function) though.
for(var i=0; i<=3; i++) count(i);
Or a real world example: for(var i=0; i<texture.length; i++) createPattern(i);
function createPattern(i) {
texture[i].onload = function() {
pattern[i] = ctx.createPattern(texture[i], 'repeat');
}
}
I've made a blog post about closures: http://www.webtigerteam.com/johan/en/blog/closure_en.htmIt would be cool to write like this (but you cant):
pattern = texture.map(t => t.onload => ctx.createPattern(t, 'repeat'))Not quite. var's are hoisted to the top of their most local function.
(function(){
(function(){
var x = 123;
})();
{
var y = 123;
}
// Here, x is not defined, but y is
})();
// Here, neither x nor y are defined
The above code essentially gets translated to the following: (function(){
var y;
(function(){
var x;
x = 123;
})();
{
y = 123;
}
// Here, x is not defined, but y is
})();
// Here, neither x nor y are defined var a = function () {
var foo
var b = function () {
foo = 3
}
b()
}
a()
console.log( foo ) // undefined
In the code above, the "foo" in function a is set to 3, rather than creating a global. (changing the "vars" to "lets" would do the same thing, FWIW)Also, "use strict" mode will not let you make a global that way. If in strict mode, you have to put the "var" outside of any function, then assign it (either on that line, or later) to make a global.
You could use a variable in a callback for an event that was assigned on the line above, and it since has changed, but it's important to remember that the callback (or promise, etc) does not actually run until an arbitrary time "later".
Special bonus to couple decomposing assignment with IIFE so that any variables/symbols which "escape" from the nested function are explicit (and the locals simply disappear with the nested function).
Const would be nice, if it went further - treating the const declared identifier as if it were deeply frozen, at least within the scope of the const identifier. Alas, that touches on what is wrong with most programming languages in common use today: mutable by default, but we should be requiring "groveling" to make something mutable.
What does `for element of arr` buy me over `arr.forEach(element => ...)`
I don't find the for...of syntax particularly appealing or useful, but I might be missing something. Is it a matter of preference?
function *myIterable (v) {
while(--v) yield v
}
let launchCountdown = myIterable(60)
for (let i of launchCountdown)
console.log(`t minus ${i} seconds`)
So in effect arrays can be thought of as iterables in ES6. So IMHO it allows for more consistent behavior with the way iterators in other languages like Java and C++arr.map(...).filter(...).forEach(...) which allows me to iterate over the filtered result? One would assign the result of filter to a variable and call for...of on that?
EDIT: Also, I never saw any mention of for...of working for object literals (à la `for (let [key, value] of obj`), I suppose that's out of scope, correct?
Just like that. It would be pretty nice to have generic filter/map that can work on arbitrary iterables, but we don't have that right now. :(
> Also, I never saw any mention of for...of working for object literals (à la `for (let [key, value] of obj`)
for (let [key, value] of Object.entries(obj)) {
}
it's not in "ES6"/ES2015, but it's in ES2017 drafts and is implemented in Chrome and Firefox but so far not elsewhere. The need to do Object.entries instead of obj.entries() is annoying, but needed in case your literal has a property named "entries"... There's also Object.keys() and Object.values(), of course. function* map(iterator, mapper) {
for (const elem of iterator) {
yield mapper(elem);
}
}
function* filter(iterator, filterer) {
for (const elem of iterator) {
if (filterer(elem)) {
yield elem;
}
}
}
function forEach(iterator, eacher) {
for (const elem of iterator) {
eacher(iterator);
}
}
function reduce(iterator, reducer, initial) {
let current = initial;
for (const elem of iterator) {
current = reducer(current, elem);
}
return current;
}However, even in just arrays there is arguably a benefit. Namely, an extra function being created and invoked with forEach. While in a JITing compiler, the performance difference between for...of and forEach might be negligible (or non existent), you can't always be sure what the JIT is going to do. For hotspots, I would probably prefer for..of
[1] https://developer.mozilla.org/en/docs/Web/JavaScript/Referen...
In a JIT, forEach is actually _easier_ to optimize well (just need to inline the callback function, though of course that can fail for various reasons) than for-of (need to inline the calls to next() on the iterator, need to do escape analysis on the return value of next(), need to do scalar replacement on the return value of next; note that this is a strict superset of the work needed to optimize forEach).
for-of can be nicer to read, and can work on arbitrary iterables. Those are its key strengths. Performance just isn't, unfortunately.
break/return/throw work as expected.
I imagine (eventually if not already) for-of will be slightly more performant, but that's just a hunch.
Personally I find the for-of syntax more readable.
The lack of inner functions means that you can await during part of a loop without a bunch of fuckery with the inner function with forEach.
http://files.meetup.com/11421852/WhatsNewInJavaScript.pdf
The slides are mostly code examples and I tried to go for completeness rather than detail, so there are some mentions you don't normally see in these overviews (e.g. the article doesn't mention proxies).
Ex:
It would be fantastic to make all the code snippet interactive with the klipse plugin.
http://blog.klipse.tech/javascript/2016/06/20/blog-javascrip...
For those interested:
import: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
export: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Things like `with` and a bunch of ugly parts were hidden with the introduction of "use strict", and i'm more than confident it will happen again in the future with something similar that can let you "opt in" to an even stricter JS that leaves behind the "current" warts of javascript in favor of much better ways.
The point of constructor functions is not having to write new. So classes does nothing besides syntactic sugars over the prototype system, witch actually makes it more complicated and the code harder to maintain.
Async programming is hard, but not because of callbacks. Promises is just a wrap around callbacks, witch just adds complexity and more boilerplate. It will get better with async/await but I will still prefer simple callbacks.
Arrow functions are very nice for one liners, but will ruin your code if you use them everywhere instead of named functions. You should always name your closures, then it's easier to debug and lift them out.
I do understand the technical difference very well - I have a PhD in implementing programming languages with function scoping like JavaScript.
If you agree with the person I was replying to maybe could you humour me and explain why you think function-scoping var is a useful feature?
I am happy for your PhD and that it is for implementing programming languages like JavaScript.
My assertion is the advantage of having function scoping is apparent to those who understand function scoping. Block scoping-style programming in JavaScript was always, in my experienced, shoe-horned in by people who wanted to make JavaScript more like Java. That is all there is to my point.
But even if it should be apparent to me, why can't you explain the reason to me? What is this - some kind of argument that is impossible to comprehend unless you already agree with it?
I can understand your argument that block scoping was shoe-horned into JavaScript, post hoc, but that isn't a technical argument for the benefit of function scoping, is it?
if(...) // use angel brackets
foo = 1; // declare variables
if(foo) ...Personally, I think the benefits of let are worth killing off var. I have honestly never seen an example where hoisting has been more clear than the alternative (declaring variables before they're used).
I assert that if you can't say what the advantage is then you don't understand it yourself.
"You'd see the advantages if you understood this, and since you don't you must not understand it" is not a reasonable thing to say to people.
Please provide an example of what you believe to be a good usage of hoisting.
Yes. Why? Because you should have readable tracebacks. Why? Because you should receive all meaningful errors reports from the devices.
probably splitting hairs here but imo "hoisting" as in using variables before they are declared is generally a bad practice especially considering that init assignments are not "hoisted".
>>>Arrow functions are very nice for one liners, but will ruin your code if you use them everywhere instead of named functions.
Based on arrow functions' relationship with "this" pretty sure the intent is to primarily use arrow functions instead of anonymous functions especially when passing them around as params. Even with one-liners you have to be wary of what "this" is when dealing with an arrow function inside of an object for example.
Promises are more than just a wrapper around callbacks because they also standardize behavior between "stacks" of callbacks, by instead "chaining" them and creating a standard infrastructure for things like error propagation down a chain. That error propagation and the ability for simple action chaining is worth having over "simple" callbacks, even without the syntactic sugar of async/await.
let/const have similar benefits to function hoisting in that you don't need to declare let/const variables until when you use them. In this case the runtime behavior throws an exception if you try to use them before they are declared (in the so-called hoisting dead zone).
If you want to call everything in serial and wait between each step, why not make it synchronous instead of .then chaining ?
The "magic" that Promises bring to code is that they "just look serial" with .then() chains. That's part of the point of Promises and that's part of the infrastructure what powers the ability to write async/await code. Just because it looks nice and serial doesn't mean it is synchronous. (In High Functional speak, Promises are the Continuation monad, and the near isomorphism with synchronous code (especially async/await) is a wonderful product of standardizing the monad.)
Which is to say: tl;dr: Promises spreading like a virus is a feature, not a bug.
For the vast majority of variables, you don’t need hoisting (i.e. you can declare them in the appropriate scope and only end up using them after the declaration).
> The point of constructor functions is not having to write new.
Um, what? This is completely wrong. You have to write new with constructor functions unless you specifically add code to check whether you’re in a constructor and re-call with new, which is bad practice.
> Async programming is hard, but not because of callbacks. Promises is just a wrap around callbacks, witch just adds complexity and more boilerplate. It will get better with async/await but I will still prefer simple callbacks.
No, async/await is just a wrapper around promises. Promises are pretty great overall, and if your Promise code looks like callbacks then you might be using them wrong. (I would always use bluebird[1] over native promises, though.)
I agree with you that large parts of ES6 add unnecessary complexity, but I don’t think your examples are well-chosen.
Sorry for the confusion, the ES5 example looked like a "factory" function, so I assumed that was what he meant. You do not have to wrap the constructor function in another function! ...
function Animal(name) {
// This code is run when the object is created. No need to wrap another function around Foo
var animal = this;
animal.name = name;
}
Animal.prototype.speak = function speak() {
var animal = this;
console.log(animal.name + ' makes a noise.');
}
Here is a "factory" function: function AnimalFactory(name) {
var animalName = name;
function speak() {
console.log(animalName + ' makes a noise.');
}
return {speak: speak}
}
var animal = AnimalFactory('Animal'); // You can omit new
animal.speak(); // animal makes a noise
setTimeout(animal.speak, 1000); // you can also do thisGreat !
const info = [1,2,3,4]
const newInfo = info.splice(2);
'info' has changed
If you know C/C++, the code 'const info = []' makes 'info' a constant pointer to a list not a pointer to a constant list.
If you want to stop a variable from being changed, use Object.freeze() - https://developer.mozilla.org/en/docs/Web/JavaScript/Referen...
This also means that objects defined with const can be mutated:
const x = { y: 2 }; x.y = 3;
Doesn't error. However with types that are immutable, like numbers and strings, const really is a constant: const HELLO = "hi" will never change.
const number = 1337;
number = 10; // fails
But objects/arrays are not immutable in js, so you can do this:
const person = { name: 'Dude' };
person.name = 'Dudette';
Which is perfectly valid. If you want full immutability, i recommend you check out https://facebook.github.io/immutable-js/, been running it in production, a real pleasure to work with.
Only in development mode, by freezing the objects.
function deepFreeze(obj) {
const out = Object.freeze(obj);
Object.keys(out).forEach(key => {
if (typeof out[key] === 'object') {
out[key] = Object.freeze(out[key]);
}
});
return out;
}(recursive)
Rust has similar semantics: https://is.gd/hsRjPh versus https://is.gd/ROgzXl
IIRC you can also declare const objects in C++, but constness is more complex and overridden there.
Is it? Personally I'd say that was bad code. What so wrong with using the original objects?
Putting aside the need to variable swap once a year or so, all the other examples look really confusing to me and unclear what they're doing. The `Deep Matching` especially.
var lst = [[1,2], [3,4], [5,6]];
for (var [a,b] of lst) { console.log(a + b); }
You could write a `zip` function to merge multiple iterables together and iterator them. Or use something like `.enumerate()` to yield indices along with the values.Yes, but not in the silly example showed.
Consider something like :
zip(fooArray, barArray, bazArray).map(([foo, bar, baz]) => foo + bar + baz)
zip(fooArray, barArray, bazArray).map((arr) => arr[0] + arr[1] + arr[2]);Consider the case of finding all companies along with their hires from the last week, but only if the company has a hire from the last week:
companies.map(c => [c, employeeStorage.getEmployees(c)])
.map(([company, employees]) => [company, employees.filter(e => e.hireDate > aWeekAgo)])
.filter(([company, employees]) => employees.length > 0)