JavaScript for Python Developers¶
Philosophical Differences¶
To understand JavaScript it is helpful to start with where it came from, and why it is important today.
The original version of the language was written by one person, and it took 10 days. That is an impressive feat, but also an indication of the level of planning that went into that first version. In 1995 nobody foresaw what the web would become and the role that JavaScript would play. That is important because it results in a lot of the shortcomings we’re left with 30 years later.
While we're talking history: the name JavaScript was essentially a marketing term. The language has no formal relationship to Java, which at the time was emerging as an important language. Java is closer to C++ or C# in semantics and purpose, a compiled language used for building applications. Java is a by-the-book object-oriented language, and JavaScript is quite a bit different.
You will see JavaScript referred to as ECMAScript or ES. That is the more "correct" name for the modern version of the language as it is written today. (TypeScript is another thing entirely, a language that compiles down to JS/ES).
Backwards Compatibility¶
JavaScript, like all web languages, cannot break backwards compatibility without breaking sites on the web. As a result, instead of old features (and even bugs) being removed, they are often left in, sometimes deprecated, but available nonetheless. On the other hand, JavaScript changes rapidly, introducing new features every year and best practices continue to evolve.
This means JavaScript is a language that has more than one way of doing most things.
You may discover that reading JavaScript code from Stack Overflow or generated by Claude varies from what is presented here. There is a lot of old and not-very-good JavaScript out there. This guide will focus on the currently accepted best practice, and only briefly touch upon alternatives.
Silent Errors¶
As we saw with HTML and CSS, a design principle of the early web was that pages shouldn't show errors.
This means that things that you'd expect to raise an exception in JavaScript will instead fail silently. This can be frustrating, especially as you are learning. The general principle in JavaScript is to execute code unless there is a syntax error.
If you are using type="module" on your script tags, JavaScript will execute in strict mode which means fewer things passing without error.
When errors do occur, they will typically be visible in the developer console, not on the page itself.
Browser Differences¶
When running client-side JavaScript (that is, JavaScript that runs in the browser), there is no single implementation. Each version of each web browser introduces changes to what is supported and what is not, and major browsers do not agree 100% on their implementation.
This can be a major headache, though the situation has improved significantly with the death of Internet Explorer.
Can I Use? is an important site for any developer building anything of complexity with newer JavaScript (or CSS) features.
Common Gotchas¶
As mentioned, JavaScript tends not to raise exceptions where you might expect. It will typically allow some unusual things like calling functions with missing parameters.
Another place that this laxness can be a real pain is in how JavaScript handles type conversions.
In Python you get a TypeError if you try to take an illegal action between two types, such as dividing a string by an integer.
In JavaScript "abc"/3 results in a value called NaN.
Unlike Python where a TypeError would occur, almost any operation between two types will give some result. When a Number is involved, that's often a NaN.
But other types will do surprising things. These expressions are true:
[]*2 === 0[4]*2 === 8[4, 5]*2is aNaN{} * {}is aNaN{} + {} === '[object Object][object Object]'Array(16).join("wat" - 1) + " Batman" === "NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman"
Yeah... it is a mess.
These type coercion rules are learnable, and I've seen JavaScript interviews that ask people to solve the last one.
But as a best practice, we want to avoid triggering any of these.
Instead of relying on this behavior we'll need to be very careful about types, and know that if we're getting a strange bug like output that includes [object Object], we probably used a type incorrectly.
And before we get any further, the usage of === is not a typo, it is actually a key part of avoiding type coercion.
== will do coercion, so some strange things are equal to one another.
1 == "1"0 == []"" == falsefalse == ["0"]
We never want this behavior.
Most JavaScript style guides ban == as a result.
Use === instead.
!!! "=== does not compare within objects or arrays"
On objects and arrays, equality is defined as the two being the same object, not having the same values.
In JS these are both false:
- `[1, 2] === [1, 2]`
- `{x: 1, y: 2} === {x: 1, y: 2}`
Rosetta Stone¶
Simple Python code can be translated to JavaScript without much fuss. The two are more alike than different.
We can start with some superficial differences:
Comments¶
JavaScript uses C-style multiline comments just like CSS
There are also single-line comments that do not need an ending marker:
Most people use // where you would use # in Python. And keep /* multilines */ for docstring-type comments.
Whitespace => {}¶
In Python, whitespace is significant.
The indentation before func1 identifies it as part of the conditional block, whereas func2 will execute no matter what, as it is external to the block.
In JavaScript (or any language with C-inspired syntax), blocks are denoted with { and }, and statements are terminated with ;.
if(condition) {
func1();
} else if(condition2) {
// note: in Python this would be elif
func2();
} else {
func3();
}
func4();
You'll see three differences:
- The conditional statement is surrounded by
(). - The body of the conditional is surrounded by
{}. - Each statement ends with a
;.
We've kept the indentation comparable to the Python version, which is conventional, but no longer necessary.
This code can be placed on a single line, since whitespace is essentially ignored in these languages.
This code has the same meaning, it is just less readable.
One final caveat.
When the body of the conditional is only a single statement, the {} are optional.
You will often see the code written:
To a Python programmer this can be misleading, since it is not the indentation that matters here.
or
Would still be interpreted as:
Because no matter what, if the {} are omitted, only the statement immediately following the conditional is considered the body.
You may see JS code that omits ;.
JS interpreters have strict rules about where they will insert ;, but it is best practice to include them to avoid ambiguity and unpredictable results.
for loops¶
Python for loops take the form:
In JavaScript, for/of is the equivalent:
for in
JavaScript does have a for in form, but it iterates over property names and is less useful than for of for most cases.
There is also a form that resembles C's for loop:
The three clauses within the for:
- initialization,
let i=0 - repeat condition,
i < 100 - iteration step,
i++(same asi += 1in Python)
Types¶
There are some small, but significant differences in the core types between the two languages:
| Python | JavaScript |
|---|---|
None |
null, undefined |
set |
Set |
tuple |
no direct equivalent |
dict |
object or Map |
str |
string |
int, float |
Number (no distinction in JS) |
list |
Array (see below) |
bool |
boolean (see below) |
null is used for missing values similarly to most uses of None, whereas undefined is given when an unknown attribute or variable is accessed that might have raised AttributeError or NameError in Python.
list¶
Some common operations and their JavaScript equivalents:
| Python | JavaScript |
|---|---|
x = [1, 2, 3] |
let x = [1, 2, 3]; |
len(x) |
x.length |
x.append(4) |
x.push(4) |
x[0] |
x[0] |
x[-1] |
x.at(-1) ; at supports negative indexes |
dict¶
Most use cases of dict become object, which is discussed below: Objects.
Some common operations:
| Python | JavaScript |
|---|---|
d = {"x": 1, "y": 2} |
let d = {x: 1, y: 2}; |
d.keys() |
Object.keys(d) |
d.items() |
Object.entries(d) |
d[variable_key] |
d[variable_key] |
d["known_key"] |
d.known_key or d["known_key"] |
bool¶
JavaScript uses C/Java style boolean operators:
| Python | JavaScript |
|---|---|
True |
true |
False |
false |
x and y |
x && y |
x or y |
x \|\| y |
not y |
!y |
Truthiness also differs: {} and [] are considered true in JS.
"", 0, null, undefined, NaN are considered false.
print => console.log¶
print-style debugging can be very useful, especially given JavaScript's lack of error reporting.
You will want to open your developer console whenever working with JavaScript, so you can see the errors that are reported, as well as the output of JavaScript's answer to print debugging: console.log.
console.log is a more powerful tool than print was.
If you log a complex value such as a list or DOM element, you can use your mouse to inspect it in the developer console.
Variables¶
There are two currently-recommended ways to set variables:
const variables cannot be reassigned, while variables set with let can be. const variables can still be mutated, lists and objects for example can have their elements and attributes changed.
When possible, prefer const.
JavaScript also uses camelCaseVariables instead of Python's snake_case_names.
You may also see var x = 0; in older code, but it should not be used as it does not follow the scope rules you would expect coming from Python.
String Differences¶
Strings in JavaScript are similar to Python's in many ways.
One key difference is that instead of f-strings, JavaScript has backtick-surrounded template strings:
// template string example
let word = "fig";
let n = 8;
const nSquared = n * n;
console.log(`Template ${word} -- ${nSquared}`);
Objects¶
JavaScript objects are kind of a hybrid between a dict and a class.
As you've seen with JSON, JavaScript objects use familiar syntax:
In JavaScript, the quotes may be, and typically are, omitted:
In JavaScript this is called an object, and in addition to accessing members with index-like lookup (d["key"]), members are typically accessed with dotted lookup d.key the way you would an object in Python.
There are also classes, which allow creating objects with the new keyword and a class constructor:
Prototypal Inheritance¶
JavaScript supports a form of object-oriented programming, but not the one chosen by C++, Java, Python, C#, Ruby, etc. Instead, JavaScript uses prototypal inheritance. In short, every object has a parent object or "prototype" where it looks up missing attributes.
ES2015 introduced a class keyword which is now the form preferred by most, and looks a lot like Python:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age}.`);
}
}
This is syntactic sugar for:
// Constructor function instead of a class definition.
// A function that adds attributes to an implicit "this" object.
function Person(name, age) {
this.name = name;
this.age = age;
}
// Adding methods on the prototype means all instances
// will immediately be able to use this method.
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name} and I'm ${this.age}.`);
};
// Create instances of Person using "new Funcname", which initializes
// the implicit "this" variable
const person1 = new Person('Sergei', 25);
const person2 = new Person('Alice', 30);
// instances get the methods shared by the prototype
person1.greet(); // Hello, my name is Sergei and I'm 25.
person2.greet(); // Hello, my name is Alice and I'm 30.
Functions¶
On their surface, functions in JavaScript are quite similar to those in Python.
Just like other blocks, JavaScript uses {} instead of indentation.
Like Python we can make parameters optional by setting default values, as seen with b above.
But in fact, all parameters are optional.
Of course in Python add() would raise an Exception.
This is one of many places where JavaScript is much more "forgiving".
Missing parameters are simply set to undefined, so add() becomes add(undefined, 2), and undefined + 2 is the special value NaN, or "not-a-number".
Ick.
I mainly mention this because it makes JavaScript code harder to debug. When a missing return statement leads to a chain of undefineds being returned, you will likely miss your exceptions.
Pass the right number of arguments to functions.
Keyword Arguments¶
In Python if you needed to pass a lot of parameters to configure a function you'd typically make them keyword-only:
In JavaScript, this would be achieved by taking an object as a parameter.
A function receiving such an object would often then use this pattern to handle defaults:
The ?? operator returns the right operand if the left one is null or undefined, it is useful for situations like this where we want to have default values that can be overridden.
Functional Programming¶
If we mind our types carefully, and remember our ; and {}, we can write JavaScript code that looks a lot like Python code.
But just as we have the concept of "Pythonic" or idiomatic code, there is a way JavaScript code is typically written, and language features to support it.
JavaScript is very oriented towards functional programming.
In many places you will find yourself passing functions into other functions.
We can do this in Python too, though you may not be well-acquainted with this style of programming:
def add_one(x):
return x + 1
# map is a Python built-in that applies a function to all elements of a list
list(map(add_one, [1, 2, 3])) == [2, 3, 4]
In JavaScript map is built in to Array:
let ll = [1, 2, 3];
let a = ll.map(function (x) {
// as much as we want
return x + 1;
});
console.log(a);
Also, you may have encountered lambda in Python, which gives us a shortcut for returning single-line functions to be used as parameters:
In JavaScript, an arrow function in the form (x) => x + 1 serves the same purpose. They can also have bodies inside {}:
// this is an arrow function, similar to a lambda in Python
let b = [1, 2, 3].map((x) => {
console.log(x);
return x + 1;
});
For other common Array methods, see MDN: Array.
Functional Programming 101 by Cassidy Williams introduces functional programming concepts in JavaScript if you'd like to pursue that further.