Yet another blog, by yet another person.
Random stuff about Python, Java, Scala, and other random stuff.
I was looking for a book on JavaScript that was not one of those "xyz bible" book or "dummies" book, and not about DOM. I wanted something that was about the language, and best practices. Along came “JavaScript: The Good Parts” by Douglas Crockford. Exactly what I was looking for.
JavaScript always looked too ugly and consequently never got the extra attention that I pays to other languages. This book made JavaScript look a lot more beautiful. I highly recommend this book.
Every time, I learn a new*** language, I compare it with Python. It is interesting to see where they are they the same, and where they differ; and the similarities and differences in idioms and attitudes. This helps me gain some more insight into both languages, and reinforce what I learned.
This is not a JavaScript vs. Python post. It does not compare the advantages and disadvantages of either.
For the examples in this post, I used the Rhino JavaScript engine that comes with Sun's Java 1.6, and Python 2.6 (with all future features enabled) and 3.0.
Both JavaScript and Python use binding names to references to objects as the model for defining variables. Reading a non-existant names causes and exception to be thrown in both JavaScript and Python; ReferenceError and NameError, respectively. In both JavaScript and Python names can be bound using assignments and by defining a function. In Python there are additional methods to define names (importing something, defining classes).
In both JavaScript and Python, names exist within specific scopes---global and function scopes (in Python there is also the class scope). This means that the main module (or each module or package in the case of Python) is one scope, and each function (and class in the case of Python) has its own scope.
Python and JavaScript diverges in the way they define variables.
In Python the first assignment is definition, but always within the current scope. A consequence of this is that you cannot define a variable without giving it a value. To create a variable that does not have anything, you would simply assign None. There is no way to define a new name in an outer scope from within another scope using assignment statements**.
# Python
name_one = 7 # defines new in global
def f():
name_two = 8 # defines new in f
name_four = 4 # defines new in f
def g():
name_three = 9 # defines new in g
name_four = 8 # defines new in g
name_one = 1 # defines new in g
In JavaScript, first assignment can be definition, but it always defines a new global variable. However, if there are any variables by that name in the current scope hierarchy, it will just be an assignment. To define a new variable to the current scope var statement must be used. When using var statements, the initial value can be left out, in which case undefined is used.
Consider the following example:
// JavaScript
name_one = 7; // defines new in global
var name_two = 8; // defines new in global
function f() {
name_three = 9; // defines new in global
var name_four = 10; // defines new in f
function g() {
var name_five = 11; // defines new in g
name_six = 6; // defines new in global
name_four = 9; // assigns 9 to 'name_four' in f
}
g();
}
f();
println(name_three); // 9
println(name_six); // 6
// println(name_four); // throws ReferenceError
Note that both name_four and name_six have been defined in the global scope after running function f, but not name_four, even though all three are first assignment statement to the respective names with each scope.
Both JavaScript and Python do not use block or control-structure level scoping. Consider the following examples:
# Python (common Python idiom) x = True if x: y = 7 else: y = 8 print(y) # 7
// JavaScript (bad coding style)
var x = true;
if (x){
var y = 7;
} else {
var y = 8;
}
println(y); // 7
y is defined within the code block of the if control structure, but they are not bound that block, but to the current scope (in the above its the global scope, but it could also be a function).
This is unlike most languages, especially ones that follow the C-syntax, e.g. Java, C++, Perl (strict mode). In the case of JavaScript, it can be more of a problem, because its block syntax is similar to that of Java or Perl, which creates a natural expectation that it has block level scope.
Personally, I had the same expectation from Python code blocks at one time, and it was one of those Aha!-moments when I found out otherwise; and until quite recently I thought JavaScript did too.
This is something that really annoys Crockford, at least according to his book.
What happens if we access a variable before it is defined? Python and JavaScript behaves differently.
Python always raises an exception (NameError or Unbound(whatever)Error). Consider the example below:
# Python # print(k) # raises NameError # print(x) # raises NameError x = True # this is common Python idiom if x: y = 7 else: y = 8 print(y) # 7 # bad coding style follows if x: m = 42 print(m) # 42 if not x: n = 99 # print(n) # raises NameError
It is ok to not define a variable if it is not used by the current execution path. This can be used for conditionally defining variables. See definition and access of m and n in the code above. We can avoid the exception if the code that access the variable is guaranteed to execute if and only if the code that defines it executes. However, this type of coding should be avoided or carefully documented.
JavaScript, on the other hand returns undefined if it is defined somewhere in the scope, but throws a ReferenceError otherwise. Consider the following code:
// JavaScript (bad coding style used)
// println(k); // throws ReferenceError
println(y); // undefined
var x = true;
if (x){
var y = 7;
} else {
var y = 8;
}
println(y); // 7
if (x) {
var m = 42;
}
println(m); // 42
if (!x) {
var n = 99;
}
println(n); // undefined
Unlike Python, if we use var to define a variable in any path (even if that is not executed), the variable is defined with the default value (i.e., undefined). Therefore, if such a variable is accessed undefined is returned. In some ways, using var in JavaScript is like declaring variables in C++/Java like language, except that JavaScript doesn't complain if it is used multiple times in the same scope.
In both JavaScript and Python, functions can be nested and each will have its own scope.
In both JavaScript and Python variables in outer scopes can be seen in inner scopes, unless, of course, hidden by a local definition. See examples below (note how local definitions hide names from outer scopes):
# Python
a_global = 42
def f():
a_local = 72
a_local2 = 88
print(a_global) # 42
def g():
a_nested = 99
a_local2 = 11 # hides outer scope's a_local2
print(a_global) # 42
print(a_local) # 72
print(a_local2) # 11
g()
f()
// JavaScript
var a_global = 42;
function f() {
var a_local = 72;
var a_local2 = 99;
println(a_global); // 42
function g() {
var a_nested = 99;
var a_local2 = 11; // hides out scope's a_local2
println(a_global); // 42
println(a_local); // 72
println(a_local2); // 11
}
g();
}
f();
Note that in both Python and JavaScript, being able to access a reference to an object means being able to modify it, if it is a mutable object.
This is another area where Python and JavaScript diverges a bit.
In Python, we cannot assign to a variable from outer scope unless we explicitly declare it as global or nonlocal (nonlocal is only available in Python 3.x). Also, this declaration has to be made before its use, otherwise a warning is generated (SytaxWarning).
# Python (nonlocal only on 3.x)
a_global = 42
def f():
global a_global
a_global = 72
a_local = 11
def g():
nonlocal a_local
a_local = 99
g()
print(a_local) # 99
f()
print(a_global) # 72
In Python 2.x, we have to employ a mutable value holder object to modify non-global instead of re-assignment.
In JavaScript, just assign to a variable name to assign to variables in closest outer scope (don't use the explicit definition syntax; i.e. var).
// JavaScript
var a_global = 42;
function f() {
a_global = 72;
var a_local = 11;
function g() {
a_local = 99;
}
g();
println(a_local); // 99
}
f();
println(a_global); // 72
In both Python and JavaScript, variables in an outer scope that is hidden by an inner scope can only be accessed by creating a new name in that outer scope. See an example below:
# Python
def f():
a = 4
ar = a
def g():
a = 7
def h():
print(a) # 7
print(ar) # 4
h()
g()
f()
// JavaScript
function f() {
var a = 4;
var ar = a;
function g() {
var a = 7;
function h() {
println(a); // 7
println(ar); // 4
}
h();
}
g();
}
f();
JavaScript has a single top-level (global) namespace with additional namespaces provided by objects. This makes sense if we consider that JavaScript was supposed to be a simple embedded scripting language. However, at least in Web development, we have started building libraries of stuff in JavaScript.
Python has additional methods for code organization, including modules and packages. When we talk about 'global' in Python, it is actually global to a module or package. Different modules have their own namespace and scope. Packages provide a method to organize modules. Considering that Python was not specifically designed to be an embedded language, it would be surprising if it did not have such features.
Another difference is that JavaScript does not have methods to import specific pieces of code. Code can be included, but is like copying and pasting code. Importing can be simulated by assigning objects to a variable; this assumes that we are using libraries built using nested objects.
There is a lot more fun to be had with JavaScript; a lot more to explore.
Labels: javascript, python
Posted by FiniteState42i @21:00 | Permanent Link
Subscribe to
Posts [Atom]