First and foremost: if you've got the Warhammer40k references please press the unicorn, and leave a comment. Just wanna see how many loyal servants of the Emperor are here amongst us :D
No frontend without javascript
There was a point in my developer career about a year ago, when I realized that I just can't really avoid learning javascript if I want to create interactive web applications.
The revelation hit me hard, like a ramming Lunar Class Cruiser.
I must admit, I used to believe that javascript is an old, cumbersome, and even dangerous tool. If the topic came up at work, my colleagues (who are mostly backend and DevOps people) were all about how problematic and silly this language was in may ways.
What I didn't know back then is that they had experience with JS prior ES6, so with old javascript. And man, old javascript had its rough edges. It's clear why my colleagues had grudges.
Consequently, when I realized that I need JS I started looking for a shortcut. I really wanted to learn some fancy framework instead of doing it with vanilla javascript. And so came to React. Naively I thought React will save me from javascript.
It didn't. And you know what? It was still a good choice!
Why learn to swim in deep waters
I know I probably should say that "nah you must start with vanilla javascript", but for me starting with React resulted in an accelerated learning process! As a DevOps person, I'm originally more of a python/bash developer and I was never exposed to the webdev world. React pushed me to learn all about the javascript toolchain, and more. I needed to learn nodeJS, graphQL, Sass, a lot of nice tools and new libraries. It really worked out for me. Now one year has passed, and I've just finished my first official freelance frontend project!
So I figured I make a post about javascript for python devs!
Maybe I can save some struggle to those who happen to find themselves in my shoes, struggling why that empty array is not falsy. Or stuff like that.
Okay, lets, go!
Into the warp
I want to start with just a small list of peculiar things that might hit you when coming from python you start to write javascript code. These are things that you face pretty much straight on.
The void:
There are two ways in Javascript to express nothingness: undefined
and null
. Python has one: None
.
Undefined
is for variables or properties that do not exist or have not yet been assigned a value, while null
represents the intentional absence of any value - when, say, you explicitly set something to null.
Type mess:
Python has a great type system. You've got lists, sets, tuples, dictionaries, not even mentioning things like Path
from pathlib (types for paths) or the collections library. Javascript is not that versatile. - Python’s list
and JavaScript’s Array
are quite similar but probably that's where similarities end.
- Since JavaScript has no mutable/immutable distinction, there is no JavaScript type corresponding to Python’s
tuple
. - Also, javaScript has no built-in hash-table support. JavaScript code that builds objects with curly brackets looks very similar to Python dictionaries, but what’s going on behind the scenes is completely different.
- In javascript, mixing types can be dangerous. You never know what implicit conversion happens in the background.
The why!?
Semicolons. What about the semicolons? Should I use them? No? Cool. But wait. There are still some scenarios when I should? WTF?
Okay chill out. Actually, javascript does not strictly require semicolons. When there is a place where a semicolon was needed, a process called Automatic Semicolon Insertion adds it behind the scenes, while parsing of the source code happens. The problem is that it sometimes misinterprets what should be done.
Interpolation
In python there are a ton of ways to do interpolation, parameter expansion, and string concatenation. In javascript, you can use template strings almost like in bash
but with backticks, which I think is pretty neat!
Booleans:
JavaScript has true
and false
. Python has True
and False
.
The Ritual
In python, we favor snake_case
while in javascript it's camelCase
.
Variable Scopes:
Python variables have global and functions scopes depending on where they were declared, and this can be further complicated with global
and nonlocal
keywords. Javascript variables, on the other hand, can be declared by using keywords let
, const
, var
. Here let and const have a block scope but var has function scope. So for example, defining let outside of any function - contrary to var - does not create a global variable.
The Heresy:
In python, using global
is heresy. In javascript using var
is heresy.
List operations:
Let's see how some basic list operations differ.
Python | Js |
---|---|
len(l) |
l.length |
l.append(item) |
l.push(item) |
l.pop() |
l.pop() |
l.pop(0) |
l.shift() |
l[start:end] |
l.slice(start, end) |
[start:end] = […] |
l.splice(start, howMany, […]) |
Looping around
The more interesting issues came up when I was trying to do some basic data manipulation. I can't say that it wasn't a struggle sometimes.
For example, when I first encountered a problem like this:
for weapon, damage in strike_map.items():
print(
"With battery {} dealt {} damage!".format(
weapon, ", ".join(str(x) for x in damage)
)
)
# Where strike_map is:
# strike_map = {
# "thermal lance": [6, 5, 4, 8],
# "macrobattery": [4, 8, 9, 7]
# }
# Prints:
# With battery thermal lance dealt 6, 5, 4, 8 damage!
# With battery macrobattery dealt 4, 8, 9, 7 damage!
I really like this example as it encompasses a lot about how smoothly one can manage loops in python. Let's see what we have here:
- We have the hashmap
strike_map
-in python, called a dictionary-, that collects starship weapons and the damages dealt by them. - Then we use
items()
method which returns a list of a given dictionary’s (key, value) tuple pair. Then we iterate on it. - In the cycle using string
format()
method we print the weapons and the corresponding damages as a string. - The tricky part is the list comprehension, one of the best features in python, and in many functional programming languages.
str(x) for x in damage
This will return the list of the numbers in the damage list, but it also executes the str()
function on every element to stringify them, so they can be joined together to a single string, separated by commas.
Beautiful right?
Now let's see this in Javascript. We don't have hashmaps, so our data can be represented by an object.
const strike_map = {
thermal_lance: [6, 5, 4, 8],
macrobattery: [4, 8, 9, 7]
}
Now you have Object methods so you can return keys or values with Object.keys()
, Object.values()
. Maybe the closest could be Object.entries()
that returns an array of a given object's own enumerable string-keyed property [key, value] pairs.
So this:
[ [ 'thermal_lance', [ 6, 5, 4, 8 ] ],
[ 'macrobattery', [ 4, 8, 9, 7 ] ] ]
With this we can have a not so neat, but still effective solution:
for (let [weapon, damage] of Object.entries(object1)) {
console.log(`With battery ${weapon} dealt {damage} damage!`);
}
// prints:
// thermal_lance: 6,5,4,8
// macrobattery: 4,8,9,7
Now you could say we've made it, but this way we'd let javascript to do implicit type conversions in the background - which we should not rely on. Now when I'm writing this post array comprehensions are deprecated in javascript and the new solution is still in an experimental phase. I didn't find a better way than to use an inner for...of
loop, or the Array map()
method which can be quite as compact using arrow functions.
Here I challenge you JS gurus, to show me how you would do this simple nested iteration problem the cleanest way.
Arrow functions
In javascript, there are regular functions, which are not prefered right now.
function name(parameter1, parameter2, parameter3) {
// code to be executed
}
And there are these new arrow functions. They can take many forms, so at first, I did find them really messy because of the loose syntax.
See a few cases:
// Assigning:
const exampleFunc = function() {
//...
}
// or:
const exampleFunc = () => {
//...
}
// or:
const exampleFunc = (param1, param2) => doStuff(param1, param2)
// without parentheses
const exampleFunc = param => doStuff(param)
// Implicit returning?!
const exampleFunc = () => 'test'
// add parentheses when returning an object? Ok.
const exampleFunc = () => ({ value: 'test' })
Another tricky thing with arrow functions is that the this
scope is inherited from the execution context. Due to this, arrow functions are not suited as object methods.
So this will not work:
const IronTide = {
class: 'Tempest',
size: 'frigate',
wholeType: () => {
return `${this.class} class ${this.size}`
}
}
// THIS WILL RETURN "undefined class undefined" :
Async
If you're using javascript you're probably developing web apps, and in web apps sometimes you need to run queries and operations that you want to run in the background, without blocking the user who's clicking around. So obviously you will need async operations.
Coming from python this was the first thing that gave me some hard time. Even though async was not totally foreign to me, as I actually had experience with asyncio
in python backend projects, but it's still weird sometimes especially if your brain is used to the synchronous world.
If you're getting into the javascript world you should definitely read up on these things. I promise it will be really beneficial.
Packages
I must admit, package management is probably harder in python. Managing venv
, and your requirements.txt is just a little more tedious than doing it in javascript.
In JS you've got a lot of options, I'm personally using yarn
. With it, it's super easy to manage your libraries and keep them updated.
That was all about it! I know we've just scratched the surface but I hope it could help you Pythonistas in this ruthless age of web applications! Take care in the fathomless void of web development my friend. And remember. In the darkness, follow the holy light of Terra Clean Code!
Top comments (1)
Actually I usually stick to npm for package manager due to the amount of support for it and audit command that helps a lot in allowing me to view vulnerable packages.