Skip to content

Instantly share code, notes, and snippets.

@mturley
Created June 24, 2018 13:20
Show Gist options
  • Save mturley/49eb55ea27b8f092121d95771e94f56d to your computer and use it in GitHub Desktop.
Save mturley/49eb55ea27b8f092121d95771e94f56d to your computer and use it in GitHub Desktop.
More ranting about JavaScript for Jenn
/*
Jenn, I was looking back on the github examples I gave you and realized one more thing:
If you didn't know it already, the ES6 arrow function syntax, has two shorthands
the first one I showed in my example already:
*/
const arrowFn = () => { };
const arrowFn = () => { return thing; };
const arrowFn = () => thing;
/*
i.e. if your function is only a single return statement, you can drop the `{` and `}` and the `return`.
The next trick is, if you have exactly one argument, you can drop the `()` too:
*/
const functionWithNoArgs = () => something[1]; // or whatever
const functionWithOneArg = arg => something[arg]; // or maybe something.method(arg); or whatever
const functionWithTwoArgs = (arg1, arg2) => something.method(arg1, arg2);
/*
With zero args, you need the empty parens `()`.
With one arg, you can just give the name, with no parens.
With two or more args, you need them in parens again.
So that second example, `const functionName = oneArg => simpleResult;` is a really nice pattern for writing helpers.
Simple helper example for casting strings to numbers in JS (defeating the duck typing problem):
*/
const toNum = str => parseInt(str, 10); // We don't need any of these! ( ) { }
const someInput = "214"
const expectedNumber = 214;
/*
Now, to show off my toNum helper, I'll teach you a thing about JavaScript equality operators.
The short version is, always use triple-equals (===) and never use regular double-equals (==) like you might in other languages.
== is a type-coercion equality check... a BAD JS FEATURE. Avoid this, FYI. "sorta equal" is more harmful than useful.
Code with == is not type-safe, and a good linter configuration in strict mode will not allow you to use it.
Types matter if you don't like errors and warnings with React props and with databases.
=== will check both the type and the value, and is a trustworthy equality operator to use.
*/
console.log(someInput == expectedNumber); // true. Cool, string or number, it doesn't really matter right? ...until it does.
console.log(someInput === expectedNumber); // false. Dang, one is a string and one's a number, so they are not strictly equal (===). Good to know.
// In comes my helper, so I can trust my numUsers variable is a number, not a string.
const numUsers = toNum(someInput);
console.log(someInput === numUsers); // false. Cool, numUsers is a number, not a string, that was expected.
console.log(numUsers === expectedNumber); // true. Unit test passed (or whatever).
console.log(numUsers == expectedNumber); // true. But don't use this. It can be misleading.
/*
Another example: Date() objects.
The instanceof operator is good to know too. Object types in JavaScript are stupid and it lets you check them.
*/
// Here's a helper that I'll use eventually in this example.
const toDate = arg => arg instanceof Date ? arg : new Date(arg);
// And here's some random input from a database (maybe the time something happened in my app).
const twoPmYesterday = '2018-06-23 14:00:00'; // ISO standard string for date-and-time, one of the formats Date() allows.
/*
Let's try something. Assume I really want to know how much time has passed since this twoPmYesterday value.
And assume I got it from a database or a library consumer and I don't know if it's a string or a Date.
But I want to call Date methods on it like getTime(), which returns it as a POSIX timestamp number.
For this example, it's useful to know that `Date.now()` is equivalent to `(new Date()).getTime()`
and it returns the number of milliseconds since UNIX epoch. This is called a POSIX timestamp
It's just an easy number to use for doing math with timestamps, so you don't have to think about months and days.
(UNIX epoch is January 1st 1970, when computer clocks were born. It was a Thursday).
*/
const msSinceTwoPmYesterday = Date.now() - twoPmYesterday.getTime();
/*
Whoops... that gives us:
VM722:12 Uncaught TypeError: twoPmYesterday.getTime is not a function
at <anonymous>:12:69
Because I didn't check the types. I could solve this by writing something like this...
*/
let msSinceTwoPmYesterday;
if (twoPmYesterday instanceof Date) {
msSinceTwoPmYesterday = Date.now() - twoPmYesterday.getTime();
} else {
msSinceTwoPmYesterday = Date.now() - new Date(twoPmYesterday).getTime();
}
/*
Well... that's messy. Uh.. for two reasons. Before I finish my helper function example, let me show you something else..
`let` is annoying. It's not "immutable" like most of React/Redux. It allows reassignment of the variable.
We could clean this up by using `const` instead of `let` and a ternary expression instead of if-else and reassignment....
*/
const msSinceTwoPmYesterday = twoPmYesterday instanceof Date ? (
Date.now() - twoPmYesterday.getTime()
) : (
Date.now() - new Date(twoPmYesterday).getTime()
);
/*
That's better, right? (condition ? trueValue : falseValue) is a ternary expression.
It's still 5 lines though.. Could it be a little smaller? This is perfectly fine too:
*/
const msSinceTwoPmYesterday = twoPmYesterday instanceof Date
? Date.now() - twoPmYesterday.getTime()
: Date.now() - new Date(twoPmYesterday).getTime();
/*
It comes down to personal preference, or team preference if you run eslint in your build.
But... remember my nice short ES6 arrow function helper, in super-short form?
*/
const toDate = arg => arg instanceof Date ? arg : new Date(arg);
/*
Yeah. Forget everything I just taught you, and instead write stuff like that.
Simple functions lead to simple results:
*/
const msSinceTwoPmYesterday = Date.now() - toDate(twoPmYesterday).getTime();
/*
Isn't that so much better?? and it's safe to use, too:
*/
const mysteryInputA = '2018-06-23 14:00:00'; // 2 PM yesterday as an ISO string
const mysteryInputB = 1530122400000; // 2 PM yesterday as a POSIX timestamp
const mysteryInputC = new Date(1530122400000); // 2 PM yesterday as a JavaScript Date object
const now = Date.now(); // 1529845601429, when I ran this on Sunday morning.
console.log(now - toDate(mysteryInputA).getTime()); // 68801429
console.log(now - toDate(mysteryInputB).getTime()); // 68801429
console.log(now - toDate(mysteryInputC).getTime()); // 68801429
/*
Takeaways:
- ES6 Arrow functions are shorter and more readable than ES5 functions.
- You can drop the argument parentheses () if you only have one argument.
- You can drop the function body braces {} if you only have one expression.
- Using these patterns is super helpful for making... well, helper functions.
- Other handy things to know:
* JavaScript is a dynamically-typed language, and a particularly type-loose one.
* The normal equality operator (==) "conveniently" ignores types for you.
* Don't do that. Use strict equality (===). ManageIQ's build server will shame you on GitHub if you don't.
* `a instanceof SomeObject` will return `true` if `a` is of the type you would get by using `new SomeObject()`.
* `condition ? someValue : someOtherValue` is a ternary expression,
and will return someValue if condition is truthy, otherwise someOtherValue.
* `condition && someValue` will similarly return `someValue` if the condition is truthy,
and will otherwise return `condition` (which might be false or some other falsy value).
- Yeah, truthiness is another fun JavaScript feature.
* 1 is truthy, 0 is falsy, null is falsy, "hello" is truthy.
* truthy values trigger the `if () { }` block or the left side of a ternary.
* falsy values trigger the `else { }` block or the right side of a ternary.
Yeah. JavaScript. It's a weird language that we are stuck with.
Here's one last trick for you.
*/
const truthyString = "Mike Turley";
const falsyThing = null;
const truthyNumber = 54;
const actualFalse = false;
// ! is the "not" operator. If an expression is truthy, it returns false, otherwise it returns true.
console.log(!truthyString); // false
console.log(!falsyThing); // true
console.log(!truthyNumber); // false
console.log(!actualFalse); // true
// So... the shorthand for casting to boolean that I like to use?
// .... just do the not operator twice. "not not".
console.log(!!truthyString); // true
console.log(!!falsyThing); // false
console.log(!!truthyNumber); // true
console.log(!!actualFalse); // false
// Yeah. Welcome to JavaScript. It's What Works In The Browser (TM).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment