ecmascript for python programmers

Disclaimer: I have written very little ECMAScript, I'm using this post as a place to store my notes. The odds that I have some of the details wrong is high. See http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-3 (and related) videos for a nice description of different aspects of ECMAScript, and https://developer.mozilla.org/en/JavaScript/Reference and http://javascript-reference.info/ for terse language references.

Like Python, ECMAScript is an interpreted language, but in the case of ECMAScript the interpreter is typically a browser (leading to a lot of headaches associated with different implementation offering different features). ECMAScript version 5 is coming, but not supported by current browsers. One should ideally target the subset of ECMAScript that plays well with sandboxed systems (such as http://code.google.com/p/google-caja) which promise to make it much easier to write secure code.

Objects and Inheritance

  • Both languages are object-oriented and dynamically typed.
  • Both have immutable strings, and numbers
  • Python has primitive types that do not act like other objects (you cannot add attributes to them)

Python has user-defined types (via class definitions). ECMAScript uses prototypal inheritance, which means that each object has a "prototype" object. There are cases in which these types of inheritance can have very different behaviors, but in many contexts the languages are very similar because they both follow a cascade when looking for an attribute within an object.

In both languages you can add key/value pairs to an object (in python you cannot extend the primitive types or any user-defined class that uses the slots declaration). In Python these are referred to as attributes (and are stored in a __dict__ dictionary for the instance and class). In ECMAScript these key/value pairs are called properties, and you can think of every object as the equivalent of a python dictionary. In either language, o.x has a syntactic equivalent. In python it is getattr(o, 'x'), in ECMAScript it is o["x"]. Python's o.__dict__[ 'x') only checks the instance-specific dictionary of attributes, and there is no (simple) equivalent of this in ECMAScript.

If you try to "read" one of these attributes, then the interpreter checks the instance's dictionary. If the attribute is not found, then the interpreter checks the next object in the cascade. In Python, this "next object in the cascade" is the class of the object (and if it is not found there it checks for the attribute in the class' superclass...). In ECMAScript the "next object in the cascade" is the prototype (and if the property is not there then the interpreter checks the prototype of the prototype...).

Python's attribute retrieval cascade for o.x is equivalent to getattr(o, 'x'). The full cascade in python is better described as:

  1. If the o.__class__.__getattribute__ is defined call return o.__getattribute__('x')
  2. If the 'x' in o.__dict__ return o.__dict__['x']
  3. Try to return getattr(o.__class__, 'x'), but if this raises an AttributeError and o.__class__.__getattr__ is defined, then return return o.__getattr__('x')

(It is actually more complicated than this because you can inherit from multiple classes). Thus, __getattribute__ allows one to deal with every attribute lookup, while __getattr__ is a hook that lets you deal with lookup that fail using the "normal" procedures. Prototypal inheritance can be implemented in python (see http://solo.gardentheory.com/2010/10/crockfords-javascript-prototypal.html).

ECMAScript objects can be created using an object literal syntax that looks very much like a python dictionary (the difference being that keys need not be quoted).

var x = {b : 4};
document.write(x.b); // writes 4
document.write(x['b']); // writes 4

In Python, you can (but generally do not) update an object's __dict__ using a similar syntax:

class A:
    pass
x = A()
x.__dict__.update({'b':4})

Syntax

Both languages are basically, C-like. Python uses indentation to denote blocks of code. ,
Concept JavaScript Python Notes
Variable declaration var x; not needed.
constants const x = 4; not supported (though some types are immutable) Assigning a new value to a const variable in ECMAScript can be ignored or can generate an exception
Common Types String,Number,ObjectArray,Booleanfunctionstr,int,long,float,list,tuple,dict,bool, function, and user-defined classes Python 2 distinguishes between function and instancemethod, and has the concept of bound and unbound instancemethods, and there are implications for monkey-patching (see below).
Branch
if (var i = < x) {
    ...
}
else if {
    ...
}
if i <= x:
    ...
Loops
for (var i = 0; i < x; ++i) {
    ...
}
for i in xrange(x):
    ...
while (var i =< x) {
    ...
}
while i <= x:
    ...
code blocks { } indentation
statements ; (can be implied!) newline or ; Implied ; at newline can cause problems in ECMAScript (see ECMAScriptGotchas)
Assignment var  x = y; x = y In both languages assignment is binding a name to an object (not stuffing new content into the object, as can be in C/C++).
Arguments In both languages, a reference to the object is passed. Calling a setter on the object passed in will affect the caller, but rebinding the argument name in a function will not change the object sent by the caller
Operators 1 + '2' results in '12' 1 + '2' results in a TypeError Both uses + for addition and string concatenation, but python will not coerce to a string.
Ternary operators x > 5 ? x : 3 x if x > 5 else 3
and logical operator return x && x.f() return x and x.f() Same behavior in both languages - shortcircuiting and return of the last value needed to assure correct boolean interpretation
or logical operator return x || y return x or y Same behavior in both languages - shortcircuiting and return of the last value needed to assure correct boolean interpretation
Scoping var in function leads to function level scoping, if omitted the variable is global Variables set in a function are local, unless they are introduced using the global keyword
Namespaces Not supported. Scripts executed are all executed in the same namespace. import statement executes a package or module in its own namespace. Use . to access thes variables:
import sys
print sys.argv
x = null; x = None
undefined no equivalent (None) var x; statement anywhere in a function implicitly acts as if the first statement of the function was var x = undefined;
boolean Boolean, true, false bool, True, False
!!x bool(x)
numeric Number int, long, or float Note that Python 3 does not distinguish between int and long. Python's integers grow to be as big as needed, but JavaScripts numbers cannot be greater than Number.MAX_VALUE and start to experience rounding error for at under 10E15.
NaN float('NaN')
Infinity float('inf')
Number.NEGATIVE_INFINITY -float('inf')
num = Number(s); num = float(s)
Mathematical functions Math.log(x)
import math
math.log(x)
Coerce to number num = +s; num = float(s)
read next number num = parseInt(s, 10); or parseFloat(s); Inappropriate strings return NaN, in python float(x) will raise a ValueError if x.strip() is not a string that interpretted as a float. Both languages support scientific notation such as '3.2E-5' for float parsing.
strings String str
Coerce to string String(x) str(x)
Implicit conversion to string Equality testing with x == y, + operator, probably elsewhere(?) Only in print statements and % string formating.
Equality (x == y) can implicitly coerce to string or bool. Use of === is safer. x == y test for equivalence. Can be overloaded.
=== and !== is and is not === is preferred because it will not result in coercion
//blah #blah
/* blah */ no multiline comment
multiline strings Not supported. Use Triple quote
'''x
'''
or
"""x
"""
Some JavaScript interpreters support the \-newline continuation convention, but the implicit addition of ; means that this is particularly dangerous in ECMAScript
function x() {
    ...
    }
def  x():
	...
 
var x = new Array(); x = list() or x = []
Immutable Arrays Not supported x = tuple() or x = ()
Array x.length len(x) Accessing to a position beyond the length of a python list or tuple will result in an IndexError. In ECMAScript, reading out of bounds returns undefined, while writing automatically expands the array to length index + 1.
Array-indexing x[i]
x = {}
x[str(i)]
ECMAScript array indexing is like using a dictionary with implicit conversion of all keys to strings.
String and Array-slicing x.slice(1,5) x[1:5] to return a list with elements 1,2,3, and 4 of x Python supports a third "step" argument to specify the stride of a slice.
String and Array-slicing x.slice(1) x[1:] to return a list with elements 1 to the end of x
String indexing x.charAt(1) x[1]
Hash tables use x = Object(); x = {}
keys converted to strings keys are hashed. lists and dicts cannot be keys (although tuples can).
class definition No classes but, constructors can be defined.
function A() {
    ...
class A:
	def __init__(self):
Object nheritance introspection Object.getPrototypeOf(o) o.__class__
creation var x = new A(); x = A()
preferred var x = Object.create(p); x = copy.copy(p);
Members x.y or x['y'] x.y or x.__dict__['y']
in operator x in y is true if object y has property x x in y if object y contains x. If y is a dict, then its keys are searched. If y is a list, then its elements are checked. If y is an object then its __contains__ special method is called
for (var i in x) { for i in dir(x):
o.hasOwnProperty(name) name in o.__dict__
o.keys() o.__dict__.keys()
delete x.y; del x.y
Object.getPrototypeOf(x); type(x)
o instanceof String; isinstance(o, str)
typeof x; (will be primitive or `object') type(x)
Reg.Ex.
var x = new RegExp('\s+')
var matchArray = x.exec(stringToSearchThrough);
or
x = /\s+/;
var matchArray = x.exec(stringToSearchThrough);
import re; 
x = re.compile(r'\s+')
m = x.search(stringToSearchThrough)
if m:
    matchList = [m.group()] + list( m.groups())
else:
    matchList = None
String.search converts arg to Reg.Exp
Current object this implicitly declared:
function (x) {
	this.i = x;
}
Called self by convention, but is explicitly declared:
class A(object):
	def f(self, x):
	    self.i = x
Throwing exceptions throw {name : someName, message: someMessage}; raise MyException('constructor arg here')
Catching exceptions
try {
	someOp();
}
catch (e) {
    switch (e.name) {
        case 'Error':
        case 'EvalError':
        case 'RangeError':
        case 'SyntaxError':
        case 'URIError':
        case 'TypeError':
        default:
            throw e;
    }
try:
    someOp()
except ValueError as e:
    raise
except Exception:
    print "Some non ValueError Except"
finally:
    # optional code block.
    # always executed
Python also supports try/except/else constructs
The new keyword means that the next function is called in the constructor context See http://trephine.org/t/index.php?title=Understanding_the_JavaScript_new_keyword and http://trephine.org/t/index.php?title=JavaScript_classes for details.

Functions

Both languages treat functions as first class objects. In python, invoking an instancemethod using o.x(y) will implicitly send in o as the current object. In ECMAScript this passing of the current object is performed whenever the o.x(y) syntax is used. In ECMAScript, the current object is called this and it is not listed in the argument list. In Python the object is traditionally called self, but is actually defined to be the name that first appears in the argument list. In python, when you assign a function to a class's attribute (or define the function within a class), then the function becomes an instance method. To invoke the method, you must to supply an instance of that class as the first argument. For example, the following calls work:
class B:
    def f(self, i):
        return 4 + i
b = B()
b.f(1)
B.f(b, 2)
x = b.f
x(1)
y = B.f
y(b, 2)
while B.f(1) or y(1) will raise a TypeError because two arguments are required. You can use B.z = staticmethod(y) to assign a function to a class without converting it to an instancemethod. In ECMAScript, there are no classes, so assigning a function as a property of an object is simply storing the function under a new name. This is the way that python behaves when you assign an attribute of an instance to be a function (when you assign an attribute of a class to be a function, the class wraps it as an instancemethod). In ECMAScript 5 and later you will be able to useuse bind to return a function object with as the first arguments bound.
var z = {x : 1, 
        f : function(y) {
            return this.x + y;
        }
}
z.f(2); // returns 3 because this.x is 1
var w = {x : 3};
w.y = z.f;
w.y(2); // returns 5 because this.x is 3
var a = z.f;
a(1); // returns globalobject.x + 1. Probably NaN.

ECMAScript function invocation styles

Bare functions

When called as bare-functions this is set to the global object in ES3 and undefined in ES5.

Method form

When called in method for, the this object is implicitly sent (and does not appear in the argument list).

Constructor invocation

Preceded with the new keyword. The newly created object is passed in as this. This object will be returned if no other object is returned.

apply and call

First argument given will be treated as this. call takes an array of arguments, apply takes as many arguments as you want to send.

Arguments

You can basically think of javascript:
var foo = function (a, b) {
	...
}
as the python:
def foo(a=None, b=None, **arguments) {
	...
}
with the exception that instead of None, ECMAScript uses undefined, and the arguments variable in ECMAScript is an array-like object that includes all of the arguments - named and un-named, and including those bound to variables in the function declaration. Thus a and b would be in arguments

Gotchas

ECMAScriptGotchas

  • Integers can overflow at 10E15.
  • Array.sort uses String(x) < String(y) as default comparison
  • delete [0,1,2][1] results in [0, undefined, 2]. Use [0,1,2].splice(1, 1);
  • Implicit ; added to the end of lines. So
    return
    	2;
    will return undefined rather than 2.
  • In a bare function invocation this refers to the global object!
  • In ECMAScript, String.search, String.split, and String.replace all can take a regular expression as their first argument. But only String.search automatically converts any string to a regular expression!
  • constructor is a property of all objects.
  • ECMAScript is not what anyone calls it

PythonGotchas

  • The whitespace thing -- never mix tabs and spaces for indentation!
  • Integer division is the defaut in Python 2 if both operands are integers. So 2/3 results in 0 while 2/3.0 results in 0.66666666666666667. In Python 3 conversion to float is becoming the default for all division with the / operator. In both Python 2 and 3 the // produces "floor division." If both operands are ints then the result will be an int. The remainder is discarded (in both float and int floor division).
  • Grouping at tuple definition are both done with parentheses. So x = (3+1) sets x to 4, but x = (3+1,1) sets x to the tuple (4,1)

Tools

Obscure notes: Property markup (not what ECMAScript programmers call it).

One rarely needs, this but it is interesting to note that a property of an ECMAScript object can be flagged as constant, or not to be included when looping over all of the properties

From a python programmer's perspective, an ECMAScript's object acts as if it defined __setattribute__ so that every the value is boxed into a richer object that describes it. Thus, ECMAScript-style attribute access in Python would (roughly) be mimicked by:

class JavaScriptProperty:
    def __init__(self, value):
        self.value = value
        self.writeable = True
        self.enumerable = True
        self.configurable = True
 
class JavaScriptObject:
    def __init__(self):
        self.__dict__['prototype'] = None
    def __getattribute__(x, name):
        v = self.__dict__.get(name)
        if v is None:
            if self.__dict__['prototype'] is None:
                raise AttributeError(name)
            return getattr(self.__dict__['prototype'], name)
        return v.value
    def __setattribute__(x, name, value):
        self.__dict__[name] = JavaScriptProperty(value)
Setting o.y with  enumerable = false; means that the property y will not show up when you use a for (x in o) { loop construct. Setting o.y with  writeable = false; means that the value of o.y cannot change. Setting o.y with  configurable = true; means that the value of o.y can change and the property can be deleted. In ECMAScript you can create objects with: Object.create(prototype, properties, ...)