Skip to content

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]*2 is a NaN
  • {} * {} is a NaN
  • {} + {} === '[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 == []
  • "" == false
  • false == ["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

/*
    JavaScript comment.
 */

There are also single-line comments that do not need an ending marker:

// one line comment

Most people use // where you would use # in Python. And keep /* multilines */ for docstring-type comments.

Whitespace => {}

In Python, whitespace is significant.

if condition:
    func1()
func2()

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.

if(condition) { func1(); } func2();

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:

if(condition)
    func1();
func2();

To a Python programmer this can be misleading, since it is not the indentation that matters here.

if(condition)
  func1();
  func2();

or

if(condition) func1();
  func2();

Would still be interpreted as:

if(condition) {
  func1();
}
func2();

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:

for item in iterable:
    statement1
    statement2

In JavaScript, for/of is the equivalent:

for(let item of iterable) {
    statement1;
    statement2;
}

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:

for(let i=0; i < 100; i++) {
    statement1;
    statement2;
}

The three clauses within the for:

  • initialization, let i=0
  • repeat condition, i < 100
  • iteration step, i++ (same as i += 1 in 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-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:

let radius = 3;
const PI = 3.14159;

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:

{"key": "value", "arrays": ["work", "too"], "and_items": {"can": "nest"}}

In JavaScript, the quotes may be, and typically are, omitted:

{key: "value", arrays: ["work", "too"], and_items: {can: "nest"}}

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:

// the new keyword must precede constructor calls
let d = new Date('January 1, 2024 01:23:45');

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.

function add(a, b = 2) {
  return a + b;
}

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:

encode(x="mpg", y="year", color="make", shape="country")

In JavaScript, this would be achieved by taking an object as a parameter.

encode({x: "mpg", y: "year", color: "make", shape: "country"})

A function receiving such an object would often then use this pattern to handle defaults:

function encode(options = {}) {
  let xDatum = options.x ?? "x";
  let yDatum = options.y ?? "y";
  ...
}

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:

list(map(lambda x: x + 1, [1, 2, 3]))

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.