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.
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:
o.__class__.
__getattribute__
is defined call
return
o.
__getattribute__
(
'x'
)
'x'
in
o.
__dict__
return
o.
__dict__
[
'x'
]
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})
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
,
Object
Array
,
Boolean
function
|
,
str
,
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 ''' """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); 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 |
new
keyword means that the next function is called in the
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)
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.
this
is set to the global object in ES3 and
undefined
in ES5.
this
object is implicitly sent (and does not appear in the argument list).
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
this
.
call
takes an array of arguments,
apply
takes as many arguments as you want to send.
var foo = function (a, b) { ... }
def foo(a=None, b=None, **arguments) { ... }
None
, ECMAScript uses
undefined
, and the
arguments
variable in ECMAScript is an array-like object that includes
a
and
b
would be in
arguments
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
)
;
return 2;
undefined
rather than 2.
this
refers to the global object!
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.
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).
x =
(
3
+
1
)
sets x to 4, but
x =
(
3
+
1
,
1
)
sets x to the tuple
(
4
,
1
)
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)
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
,
...
)