A brief introduction to JavaScript for Python and Java programmers
Steve Marschner
Cornell CS 4620 Fall 2020
These notes are a quick tour of JavaScript with an emphasis on what matters for CS 4620. I’ll compare directly to Python because the languages are fundamentally much more similar than either is to Java (despite the name JavaScript and the syntactic resemblance).
This page has a pretty nice (and longer) take on this topic, and links to some nice resources (check out Mattias Petter Johansson’s entertaining and clear video explanation of prototypes linked in there).
I’ll progress generally from the bottom up.
Interpreter
Python is (mainly) an interpreted language; the interpreter is called python
and you can get an interactive prompt just by running it in the console.
JavaScript is also a (mainly) interpreted language. The most important interpreter is the one included in your browser, but the interpreter used by Chrome is also packaged as a command line tool called node
(sometimes referred to as node.js or NodeJS), and you can get an interactive prompt just by running it in the console.
In both cases the usual facts of interpreted languages apply: there are no inherently required compile-and-build steps, and you can expect performance of code that executes a lot of statements (as opposed to calling lower level libraries for the heavy lifting) to be pretty slow, compared to languages like C++.
Syntax
JavaScript syntax is more conventional than Python, in that it is mostly just like C and Java (the operators, precedence rules, flow control statements, comments, etc. are very similar to those languages).
Types and values
Both langauges are pretty loose about types (they are dynamically typed). Values (numbers, objects, etc.) have types but variables do not. The language does not help you by ensuring that you know the type of the values you are working with, but it also doesn’t have a lot of bureaucracy that gets in the way. This is different from Java where variables have to be declared with types and it’s an error to assign something to a variable that might not be the right type.
Python has the usual integer and floating-point numeric types, as well as built-in complex numbers. It uses //
for integer division, /
for floatint-poing division, and **
for exponentiation. It has a mod (remainder) operation %
that works for integers and floating point numbers, with mathematician-friendly semantics (sign of the remainder is the sign of the divisor, -4 % 10 == 6
).
JavaScript has a single numeric type called Number whih is always a double-precision floating point number. Therefore it has only one kind of division, /
; it also uses **
for exponentiation. JavaScript has literals Infinity
and NaN
for the special floating point values. It has a mod (remainder) operation %
, with traditional C semantics (sign of the remainder is the sign of the dividend, -4 % 10 == -4
).
It seems strange that there is no integer type, but double precision floats can represent all integers exactly that can be represented by 32-bit integers.
Both languages have strings delimited by single or double quotes, and the escaping rules for including special characters, etc. are pretty similar. Both languages use +
for string concatenation.
Python has tripled quotes that be used to make multi-line strings. JavaScript has strings delimited back-tics that can be used to make multi-line strings.
Python has formatted strings prefixed with f
like f"The sum of {a} and {b} is {a+b}"
, and JavaScript has formatted strings delimited by back-tics like `The sum of ${a} and ${b} is ${a+b}`
. Both of them allow you to put the values of essentially arbitrary expressions into a string, and each has its ways for controlling the formatting.
In both languages you can access characters in strings using array notation, but JavaScript does not have a slice notation like Python’s s[3:5]
; rather there is a method s.slice(3,5)
. Strings have lots of methods, as well as a built-in String
class that provides more functions.
Python has a boolean type with literals True
and False
. The logical operators are and
, or
, and not
.
JavaScript has a boolean type with literals true
and false
. The logical operators are like Java, &&
, ||
, !
. JavaScript has a conditional operator like Java like min = a < b ? a : b
whereas Python has min = a if a < b else b
.
Python has the value None
to indicate absence of a value. JavaScript has two values undefined
and null
that serve a similar purpose.
In both languages, many types will be implicitly treated as truth values in boolean expressions; empty strings, numbers that are zero, empty lists, null values, etc. are false, and most other things are true. On both languages, boolean operators return original value of the leftmost operand that determines the result, so for instance False or 4
in Python or false || 4
in JavaScript will return 4, rather than the boolean value for true. This is more idiomatic in JavaScript than in Python (i.e. you see it used more).
Generally both languages are pretty liberal with the automatic conversions, but JavaScript is even more so. The language favors making every input mean something over making likely-wrong inputs result in errors. This can lead to surprising results that can often cause forehead-smacking bugs. For instance, '4' + 3
means the same thing in both languages, but '4' - 3
.
Partly due to the possibly over-enthusiastic conversion rules, JavaScript has two operators for equality comparison. There is ==
which first applies all the conversion rules to get the types to match, then compares; and there is ===
which does not do any conversions and just returns false if the types do not match. Many people prefer to use ===
by default because the conversions can be surprising – for instance, 0 == []
, 0 == ''
, 0 == '0'
, '' == []
, false == 0
are all true but '' == '0'
and [] == '0'
are false. ===
is false for all of these. Of course there are !=
and !==
that behave correspondingly.
Statements, functions, and control flow
Both languages have statements and expressions and run programs by executing statements sequentially. Both have the familiar conditionals and loops: if/else statements, for and while loops, switch statements. In Python, statements are delimited by line breaks and blocks of code are introduced by colons and delimited by indentation:
for i in range(3):
print("The value is")
print(i)
print("This happens after")
JavaScript is much more conventional, with a Java-like (and therefore C-like) syntax using braces to delimit blocks, and statements separated by semicolons:
for (i = 0; i < 3; i++) {
console.log("The value is");
console.log(i);
}
console.log("This happens after");
JavaScript will silently corret omitted semicolons in many cases, but I recommend using semicolons to separate statements in the places where Java would require them, because you can get into some subtle problems by omitting them. The uncertainty about where semicolons are needed is a kind of ugly corner of JavaScript.
Functions in Python can be written as definitions or as expressions:
def square1(x):
return x * x
square2 = lambda x: x * x;
and the same is true in JavaScript:
function square1(x) {
return x*x;
}
let square2 = x => x*x;
In both languages the result in either case is an ordinary variable that has a function bound to it; there is nothing special about the binding of function to name and you can assign a new value to it if you want:
square1 = 42
square1(3) # TypeError: 'int' object is not callable
square1 = 42
square1(3) // TypeError: square1 is not a function
Both languages do remember the name the function was declared with, though:
def f():
return 3
g = f
print(g) // <function f at 0x7f886124ed08>
function f() {
return 3;
}
g = f;
console.log(g); // [Function: f]
In both languages the expression version is frequently used to make anonymous functions that are directly passed to some other function rather than being bound to a variable. In Python, you cannot make a multi-statement anonymous function, but in JavaScript there are two ways to do this:
function callMe(f) {
console.log(f(3));
}
callMe(x => x*x); // 9
callMe(x => {
x = x + 3;
return x*x;
}); // 36
callMe(function (x) {
x = x + 3;
return x*x;
}); // 36
The indentation is not needed but is typically used for multi-statement functions unless they are very short.
The functions created by the function
and =>
syntax are subtly different; more on this under classes below.
Closures
Both Python and JavaScript allow a function to access names from the scope that encloses its definition:
def multiply(n):
return lambda x: x*n;
f = multiply(3)
g = multiply(4)
print(f(10), g(10)) # 30 40
function multiply(n) {
return x => x*n;
}
f = multiply(3);
g = multiply(4);
console.log(f(10), g(10)); // 30 40
This feature exists in both languages but is used very heavily in JavaScript, particularly with callbacks. In particular, in many places where in Java or C++ you’d find yourself needing to provide extra “user data” to be supplied to a callback, the same problem is idiomatically solved in JavaScript with a closure (you put the data in a local variable and supply a callback function that accesses it).
Variables and scopes
In Python you don’t and can’t declare variables; they come into existence in the local scope when you assign to them.
In JavaScript you don’t have to declare variables but you really should, because undeclared variables come into existence in the global scope when you assign to them. You can declare variables with let
or const
. let
creates a variable scoped to the block it appears in, and const
is like let
but the variable’s value can’t be changed after initialization. There is a third kind of declaration using var
, which is mostly a historical artifact; it creates a variable scoped to the function it appears in, and there’s not much reason to use it in new code.
Arrays, dictionaries, and objects
Python and JavaScript have arrays that are pretty simiar in everyday use.
x = [3,1,4,1,5,9]
x[3] = 'foo'
x.append([42])
print(x, len(x))
x = [3,1,4,1,5,9]
x[3] = 'foo'
x.push([42])
console.log(x, x.length)
As in Python there is special for
…of
syntax for iterating over lists:
for (let x of [1,2,'foo']) {
console.log(x);
}
// output:
Watch out for for
…in
, which behaves differently and is not usually what you want.
Python has dictionaries for key-value data storage, and it has objects that are implemented with dictionaries. JavaScript has objects that also function as key-value storage, though with some limitations.
x = { 'a': 1, 'b': 4, 4: 5 }
x['a'] = 42 # update
x['c'] = 29 # insert
del x['b'] # remove
print(x) # {'a': 42, 4: 5, 'c': 29}
x = { a: 1, 'b': 2, 4: 5 }
x['a'] = 42 // update
x['c'] = 29 // insert
delete x['b'] // remove
console.log(x)
In JavaScript you can use plain names (without quotes) to denote property names, or use strings. Note that in Python the integer 4 is the key for the value 5, but in JavaScript the keys are always convert to strings. In JavaScript you can also use the more concise property accessor notation when the key is an allowable a JavaScript name, but if not you have to use the indexing notation:
console.log(x.a) // 42
console.log(x['a']) // 42
console.log(x.4) // error
console.log(x['4']) // 5
Lists and objects are mutable in JavaScript the same way they are in Python, and numbers, strings, and booleans are immutable. However, in JavaScript, trying to change an immutable object just fails silently (ugh!):
s = 'foobar';
s[3] = 'x'; // no error, but no effect either
console.log(s); // foobar
Python has tuples and sets; JavaScript does not. There are also some reasons not to use objects as general-purpose key-value stores (the features that support inheritance can collide with this use). But JavaScript has standard collection APIs Map
and Set
that fill many roles normally served by Python dicts and sets.
In Python, math functions are found in the math
module, but in JavaScript they are found in a predefined object called Math
. All the usual things are in there.
import math
print(math.sin(3 * math.pi / 2)) # -1.0
print(math.atan2(math.sin(0.4620), math.cos(0.4620))) # 0.462
console.log(Math.sin(3 * Math.PI / 2)); // -1
console.log(Math.atan2(Math.sin(0.4620), Math.cos(0.4620))); // 0.462
Methods and Inheritance
Both languages provide support for object oriented programming, which loosely means that objects can be associated with the functions that operate on them. In Python this is achieved using classes, and in JavaScript this is achieved using prototypes; although many people will claim the models are totally different, the mechanisms are surprisingly similar. Both support a number of ways to implement inheritance, and in both cases this includes a model that is fairly similar to Java’s inheritance. In both languages you can find a lot of arguing over the “correct” way to set up classes, where “correct” often seems to mean “achieves Java-like sematics”. Both languages have tended towards providing syntax that makes it easy to achieve Java-like behavior without a minimum of surprises; but it’s worth understanding the underlying mechanisms.
In both languages inheritance rests on a simple name lookup mechanism: when looking up names in an object, if they are not found right there, there is a backup place to look, and methods that should be shared by a bunch of related objects are put in that backup place. In Python the backup place is a class; in JavaScript it is a prototype, and the fundamental difference is that in Python classes are a special category of object, whereas in JavaScript any object can serve as a prototype.
In Python one might declare a simple class hierarchy like this:
class Cup:
# class variables can be declared in the class
material = 'ceramic'
# initializer is a method of the class Cup
def __init__(self, capacity):
self.capacity = capacity
def type(self):
return 'generic cup'
# all names will be looked up at the time of execution
def describe(self):
print(f'This cup is a {self.material} {self.type()} that holds {self.capacity} ounces.')
class TeaCup(Cup):
def __init__(self):
super().__init__(5)
def type(self):
return 'teacup'
class Mug(Cup):
def __init__(self, capacity):
super().__init__(capacity)
# This method overrides (hides) the one in Mug
def type(self):
return 'mug'
class TravelMug(Mug):
# This variable overrides (hides) the one in Cup
material = 'metal'
def __init__(self, capacity):
super().__init__(capacity)
tc = TeaCup() # 'capacity' in the instance is initialized to 5
tm = TravelMug(20) # 'capacity' in the instance is initialized to 20
tc.describe() # finds 'capacity' in the instance, 'type' in class TeaCup, 'material' in class Cup
tm.describe() # finds 'capacity' in the instance, 'type' in class Mug, 'material' in class TravelMug
# instances are looking at the same copy of the class variables
Cup.material = 'porcelain'
tc.describe() # still finds 'material' in class Cup, but value changed
Mug(12).describe() # also finds 'material' in class Cup
# instance variables can be added at any time, by anyone, and will hide class variables
tc.material = 'bone china'
tc.describe() # now finds 'material' in the instance
TeaCup().describe() # but other instances still find it in the class
This creates four classes: Cup
and its two subclasses Mug
and TeaCup
, the subclass TravelMug
of Mug
, and several instances. When the method describe
is called, it has a reference self
to the object it was called on, and when it looks up self.type
, self.material
, and self.capacity
, it looks first in the instance, then falls back to the class of that instance, then falls back to the superclass of that class, and so forth.
In JavaScript, the corresponding code looks like this:
class Cup {
// the constructor *is* the class Cup
constructor(capacity) {
this.capacity = capacity;
}
// other functions are added to the prototype object
// all names will be looked up at the time of execution
// note that 'this' is implicit (doesn't appear in argument list)
describe() {
console.log(`This cup is a ${this.material} ${this.type()} that holds ${this.capacity} ounces.`);
}
}
// class data members can be added to prototype directly (but this is not so typical)
Cup.prototype.material = "ceramic"
class TeaCup extends Cup {
constructor() {
super(5); // syntax more like Java
}
type() {
return 'teacup';
}
}
class Mug extends Cup {
constructor(capacity) {
super(capacity);
}
// This method overrides the one in Cup
type() {
return 'mug';
}
}
class TravelMug extends Mug {
constructor(capacity){
super(capacity);
}
}
// This property overrides (hides) the one in Cup
TravelMug.prototype.material = 'metal';
tc = new TeaCup(); // 'capacity' in the instance is initialized to 5
tm = new TravelMug(20); // 'capacity' in the instance is initialized to 20
tc.describe() // finds 'capacity' in the instance, 'type' in TeaCup prototype, 'material' in Cup prototype
tm.describe() // finds 'capacity' in the instance, 'type' in Mug prototype, 'material' in TravelMug prototype
// instances are looking at the same prototype object
Cup.prototype.material = 'porcelain'
tc.describe() // now finds 'material' in the prototype
new Mug(12).describe() // also finds 'material' in class Cup
tc.material = 'bone china'
tc.describe() // now finds 'material' in the instance
new TeaCup().describe() // but other instances still find it in the prototype
In JavaScript classes are not explicitly part of the language; the class syntax is a late-arriving convenience layer that makes it easy to set up prototypes and constructors in a particular pattern. A class declaration creates a function (the constructor) and an associated object (the prototype) that contains method declarations, and it sets the prototype of the prototype to the prototype of the constructor you are extending.
So this code creates four constructors, each with its associated prototype, and several instances, each of which refers to the prototype of the constructor that made it. The prototypes are chained, so Mug.prototype
is the prototype of TravelMug.prototype
, and so on. When the method describe
is called, it has a reference this
to the object it was called on, and when it looks up this.type
, this.material
, and this.capacity
, it looks first in the instance, then falls back to the prototype of that instance, then falls back to the prototype of that prototype, and so forth.
Used in this way, the prototype-based inheritance in JavaScript is not so different from the class-based inheritance in Python. Other uses (not so common) can take advantage of the fact that any object can be a prototype for another object, because there is no formal distinction between classes and objects in JavaScript.
Binding of this
One pitfall to watch out for is the way this
is bound in anonymous functions. Many people want to write code inside a class method that sets a callback or otherwise passes a function somewhere, and that function needs to access data stored in this
. But if you just make an anonymous function using the function(...) {...}
syntax, that function is not one that was invoked on an object so it does not get its this
set; in this case this
ends up looking at the global namespace, which is almost certainly not what was wanted.
There were more complicated solutions before, but in recent versions of JavaScript the solution is to create such functions using the =>
notation. This type of function does not define this
, so it just shows through from the enclosing scope in the way you likely expected.
Modules and packages
In Python larger programs are organized into modules, and you import a module using the import
statement. The module contents are a collection of names you can access via the module (math.sin
) or by importing them directly into your module’s namespace (from math import sin, cos
) and referencing them without the module name.
In JavaScript, modules have a long and colorful history, but the now-standard (and reasonably widely adopted) way looks similar to Python. You write an import statement of the form:
import some_name from "some_module";
or
import {some_name, another_name} from "some_module";
Without the braces you are asking for the “default export” of the module, and many modules are designed to be used this way; with the braces you are picking and choosing particular exported names. You can use the form
import {some_name as my_preferred_name} from "some_module";
to control what the names are called in your module (to shorten or clarify names or to avoid collisions, for example). Modules are allowed to export whatever they want, so some modules export functions, others export objects, and the ways of invoking the modules’ features can look a bit different.
More differences
This has been a tour of some of the differences you’ll encounter first. As with any new language JavaScript comes with a whole library of standard classes and methods that you will learn how to use; there are many small differences but mainly they are not fundamental.
How do you learn this stuff?
I like reading something more structured when starting with a new system, and I initially learned JavaScript from the book Eloquent JavaScript, which I recommend and is available for free on line. For reference I mainly use the Mozilla Developer Network pages, which make a pretty good authoritative reference (they also come up early in web searches for JavaScript keywords). I use StackOverflow and similar resources a lot, though one as always needs to bring a healthy skepticism.