Core Javascript Documentation: Release 0.0
Core Javascript Documentation: Release 0.0
Core Javascript Documentation: Release 0.0
Release 0.0
Jonathan Fine
2 Objects 8
2.1 Immutables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4 The object tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5 Simple classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3 Functions 18
3.1 The global object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3 What is this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.4 Bind is transient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.5 What is new? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.6 Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.7 Mark Miller’s device . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
i
Core JavaScript Documentation, Release 0.0
Contents 1
CHAPTER 1
For Windows the easiest thing to do is to download the JSDB interpreter, and use notepad (or some other editor).
For Linux you and install Rhino and use your favourite editor.
$ sudo apt-get install rhino
You’ll want to be able to run the JavaScript interpreter from the command line when in the work folder. This will be
automatic (via the PATH) with Linux and Rhino. For Windows the easiest thing to do is to place the jsdb.exe file in
the work folder.
When you’re ready type js at a command prompt. This starts the interpreter and gives you a js> prompt. You’ll get
something like this.
core-javascript-work$ js
Rhino 1.7 release 2 2010 01 20
js>
2
Core JavaScript Documentation, Release 0.0
Now type the command as below at the JavaScript prompt (with Return at the end of each line) and you’ll get responses
as below.
js> a = '0'
0
js> b = 0
0
js> c = ''
js> a == b
true
js> b == c
true
js> a == c
false
You might be surprised by the last response from the interpreter, but every JavaScript interpreter does this.
1.2.1 Goal
The goal is to create a web page which contains several independent counters. Each time a counter is clicked, it is
incremented. Here’s you can try out a working example of what’s wanted.
Below is the complete code of this example. To simplify the matter, it is completely self-contained. It uses no library
code, other than the definition of SimpleClass.
In general library code is a good idea. This example is designed to teach you the basics of JavaScript, and not the use
of a library. We hope that what you learn here will help you choose a library, and build libraries of your own.
1.2.2 counters.html
<html>
<head>
<script src="library.js"></script>
<script src="counters.js"></script>
<link rel="stylesheet" type="text/css" href="counters.css" />
<title>JS for Python: example: counters</title>
</head>
<body>
<h1>Counters</h1>
<div id="example">
<p>This will disappear if JavaScript is working properly.</p>
</div>
</body>
1.2.3 counters.css
body {
background: #DDD;
font-family: sans-serif;
}
#example {
padding: 20px;
}
#example span {
padding: 10px;
margin: 10px;
border: 10px solid blue;
background: #DDF;
foreground: blue;
font-weight: bold;
}
1.2.4 counters.js
(function()
{
// Define a Counter class.
var counter = {}; // Prototype object for Counter.
counter.__init__ = function(name){
this.name = name;
this.count = 0;
};
counter.onclick = function(event){
this.count ++;
};
counter.html = function(){
Counter = SimpleClass(counter);
// Interaction.
var onclick_factory = function(models){
var models = [
Counter('apple'),
Counter('banana'),
Counter('cherry'),
Counter('date')
];
element.innerHTML = (
'<span id="a0">apple 0</span>'
+ '<span id="a1">banana 0</span>'
+ '<span id="a2">cherry 0</span>'
+ '<span id="a3">date 0</span>'
);
element.onclick = onclick_factory(models);
element = undefined; // Avoid IE memory leak.
};
})();
To make it easier to test code, and to ensure that example code is correct, there is a small testtools.js file in the work
folder.
To prepare a test create a file like this. It’s also in the work folder. (For real examples the file will give assert a more
interesting argument, whose truth or falsity is perhaps not obvious.)
TEST('all-pass', function()
{
assert( 2 + 2 === 4 );
assert( 'the ' + 'cat' === 'the cat');
});
TEST('2-4-fail', function()
{
assert( true );
assert( false );
assert( 1 );
assert( 0 );
});
Here’s how to run the tests at the command line, finishing with a command prompt that allows you inspect the state
and also run the test again.
Rhino
JSDB
1.3.3 testools.js
(Optional on a first reading.) Here’s the file testools.js. It’s short and simple (and has a not-nice dependency on global
variables). It also has a branch based on whether it’s being run on JSDB or Rhino.
log = function(s){
print(s + '\n');
};
} else {
log = print;
}
//
var TEST = function(title, code){
var x;
try {
code();
} catch (x) {
log('! Exception at ' + x.fileName +':' + x.lineNumber);
log('! [' + x.name + '] ' + x.message );
}
};
Objects
2.1 Immutables
First, a quick word about variables. A value is an indentifier that holds a value. In JavaScript every value has a type,
but variables are not typed. In other words, any variable can hold any value. Most dynamic language (such as Perl,
Python and Ruby) are like this.
Always declare variables with a var statement, because sometimes a missing var can cause a hard-to-find bug. Exper-
iments at the command line are the only exception to this rule.
We can change an array or a ‘dictionary’. Immutable values are values that can’t be changed. We can’t change, for
example, the third character in a string.
2.1.1 Strings
Strings literals are delimited by single or double quote marks, with special characters escaped. There’s no difference
between the two forms (except in single quote you don’t have to escape double quote, and vice versa.) Generally, I
prefer the single quote form as it’s less busy on the eye and slightly easier to type.
js> s = 'Hello world.'
Hello world.
2.1.2 typeof
There’s a built-in operator called ‘typeof’ that returns a string that, sort-of, gives the type of a value.
8
Core JavaScript Documentation, Release 0.0
js> typeof(s)
string
Because typeof is an operator (just as ‘+’ is an operator) the parentheses are not needed, and many JavaScript pro-
grammers omit them.
js> typeof s
string
2.1.3 Numbers
JavaScript numbers are platform and processor independent. It uses IEE 754 to represent both integers and floats.
js> i = 42
42
js> typeof i
number
2.1.4 Booleans
JavaScript has keywords ‘true’ and ‘false’ whose values are always true and false respectively.
js> t = true
true
js> typeof b
boolean
js> f = (1 > 2)
false
js> typeof f
boolean
JavaScript has two values that represent None. Later, we’ll see why, and which to use when. For now we’ll simply
note that they are different, because their types are different.
2.1. Immutables 9
Core JavaScript Documentation, Release 0.0
2.1.6 Gotchas
2.2 Equality
JavScript has two operations for equality (and inequality). One of them is ‘==’ (and ‘!=’ for inequality). It’s the oddest.
Not transitive
If a is equal to b, and if b is equal to c , then we expect a to be equal to c. This is called the transitive property.
Not reflexive
Is symmetric
If a is equal to b then we expect b to be equal to a. This is called the symmetric property. In JavaScript equality is
symmetric.
10 Chapter 2. Objects
Core JavaScript Documentation, Release 0.0
Is transitive
Not reflexive
By the way, there’s a thread on Facebook with subject Time and Date on my wall shows NaNNaNNaN at NaN:NaN.
I wonder how that happened.
Is symmetric
Double equality does implicit conversions and besides has some odd rules. My advice is don’t use double equality.
Triple equality does not do conversions. If you want to do conversions in your comparision my advice is to make them
explicit.
The easiest conversion is to string. Here are two immutables, a number and a string.
js> a = 0
0
js> b = '0'
0
js> a == b
true
js> a === b
false
Here’s how to do an explicit conversion to string before comparison, which gives an equality.
2.2. Equality 11
Core JavaScript Documentation, Release 0.0
Note: It’s always easier to read a triple equal comparison, because you’re not distracted by the complex double equal
rules.
2.3 Objects
JavaScript has objects and is object-oriented, but in an unusual way (more on that later).
Simple objects are like dictionaries or hashes in other languages. They support key-value storage and access to
attributes. Here’s how to create a simple object.
js> obj = {}
[object Object]
We can store attribute values in a simple object (or any object in the doc:object-tree).
It’s not an error to ask for something that’s not there. We get undefined.
12 Chapter 2. Objects
Core JavaScript Documentation, Release 0.0
You can use null to signal that there is a value, but that it is None.
js> obj.none = null
null
js> obj.none === null
true
2.3.3 hasOwnProperty
You can use hasOwnProperty to help figure out why an object has an undefined attribute. It’s also useful when
inspecting the doc::object-tree. Often, however, it’s better to write your code so you don’t need to do this (for example
by using null).
js> obj.hasOwnProperty('dne')
false
js> obj.hasOwnProperty('undef')
true
js> obj.hasOwnProperty('none')
true
You can create a simple object by placing key-value pairs in the curly braces.
js> someone = {
> 'name': 'Joe Doe',
> 'age': 43
> }
[object Object]
js> someone.age
43
Take care not to put a trailing semicolon in the object literal. It will work in Firefox but not in Internet Explorer.
2.3.5 Arrays
An array is a list of items. You can put anything is as a value for the list. Use square brackets to create an array.
js> array = []
Arrays expand to accomodate the data you store in them. You can even leave gaps.
js> array[0] = 'zero'
zero
js> array[3] = 'three'
three
An array turned into a string consists of the string on its entries joined by commas.
js> array
zero,,,three
js> array[1] === undefined
true
2.3. Objects 13
Core JavaScript Documentation, Release 0.0
As with simple objects, simply place the values between the square brackets, separated by commas.
As with simple objects, beware of trailing commas and missing entries. This will work in some browsers and not
others.
2.3.7 JSON
JSON,stands for JavaScript Simple Object Notation. It is very much one of the best parts of JavaScript. It is the
fat-free alternative to XML, and is widely used in AJAX (instead of XML). JSON objects are object and array literals
constructed using only simple objects, arrays, strings, true, false and null.
If your code accepts JSON objects then it will be a lot easier to use it with AJAX or otherwise integrate it with other
systems.
Many programming languages have JSON libraries. You don’t have to use JavaScript to use JSON.
To provide standards, there are rules on how to write a JSON objects. Many applications and programming languages
will generate valid JSON for you. I find YAML a convenient way of authoring JSON data.
respectively set and get the attr attribute of the object obj. Inheritance is where an object gets some its attributes from
one or other more objects. JavaScript uses an object tree for inheritance.
2.4.1 Tree
All JavaScript objects are part of an inheritance tree. Each object in the tree has a parent object, which is also called
the prototype object (of the child). There is a single exception to this rule, which is the root of the tree. The root of
the tree does not have a parent object.
Note: You can’t get far in JavaScript without understanding the object tree.
Get
When JavaScript needs to get an attribute value of an object, it first looks up the attr of the attribute in the object’s
dictionary. If the attr is a key in the dictionary, the associated value is returned.
If the attr is not a key, then the process is repeated using the object’s parent, grandparent, and so on until the key is
found. If the key is not found in this way then undefined is returned.
14 Chapter 2. Objects
Core JavaScript Documentation, Release 0.0
Set
When JavaScript needs to set an attribute value of an object it ignores the inheritance tree. It simply sets that value in
the object’s dictionary.
2.4.2 Root
When the interpreter starts up, the root of the tree is placed at Object.prototype. (We’ll find out later why that location
is used.)
Every object inherits from the root, although perhaps not directly. Here’s a simple example:
If we give root a attr attribute then every other object, including those already created and those not yet created, also
has a attr attribute with the same value. (In practice it’s better not to change Object.prototpye.)
This applies arrays:
js> f = function(){}
function () {
}
js> f.attr
gotcha
Note: A page might have many scripts, all of which would like to modify the Object.prototype root object. This can
cause bugs and incompatibilities. So try not to do this.
However, this up-the-tree lookup does not apply if attr is found earlier in the tree. We continue the previous example
to show this, and the behaviour of set.
2.4.3 Create
Any tree can be constructed from its root, together with a command create(parent) that returns a new child of the
given parent node.
In all but the most recent version of JavaScript the create function is not built in. However, it’s easy to write one, once
you know enough JavaScript. Here’s how its done in the work folder’s library.js file.
return child;
};
js> a = {}
js> b = create(a)
js> a.attr = 'apple'
apple
js> b.attr
apple
js> c = create(b)
js> c.attr
apple
js> b.attr = 'banana'
banana
js> c.attr
banana
Note: JavaScript uses an inheritance tree. By using create, we can create any inheritance tree. All JavaScript objects
are in this tree.
JavaScript has objects, which through the object tree support inheritance. Almost all object-oriented languages have
classes and instances, but JavaScript has neither. This will be explained later, along with a discussion of the conse-
quences.
For now our focus is on getting going with object-oriented programming. The easiest way to do this is via create.
16 Chapter 2. Objects
Core JavaScript Documentation, Release 0.0
2.5.1 Definition
Here’s a factory function that creates classes. It returns a function called cls, which in turn when called returns
instances. At it’s heart is create, and also apply and arguments, which we’ve not seen before).
return cls;
};
We call the returned function cls because in JavaScript class, like function is a reserved word in JavaScript. However,
in JavaScript the identifier class has no meaning, and cannot be used in any valid JavaScript program. It seems that
they intended to provide a built-in class capability, but never got round to it.
Here’s an example of the use of SimpleClass. There’s a bug in it, and I’ve changed the test so it passes. Can you find
the bug and fix the example?
TEST('SimpleClass', function()
{
var fruit = {};
this.name = name;
this.colour = colour;
};
fruit.greet = function(){
});
Functions
JavaScript has a global object. It is, so to speak, the object of last resort. If there’s no other suitable object then
JavaScript will use the global object (rather than reporting an error).
Note: Douglas Crockford writes JavaScript’s global object ... is far and away the worst part of JavaScript’s
many bad parts.
function () {
return this;
}
The programmer who wants to can always obtain access to the global object.
Global variables are nothing more than attributes of the global object.
18
Core JavaScript Documentation, Release 0.0
It’s very easy to inadvertently pollute (change) the global object. All it takes is an assignment to an undeclared variable
in a function.
There’s a more subtle way to pollute the global object, which involves JavaScript’s this object.
To begin with, note that we can push values onto an array.
js> array = []
js> array.push(1, 2, 3)
3
js> array
1,2,3
js> p = array.push
function push() { [native code for Array.push, arity=1] }
js> p(4, 5, 6)
3
The push function returns the length of the object it has just pushed to. So the value 3 above is a signal that all is not
well. And indeed it is not. The original array is unchanged, and the global object has an entry at 0.
js> array
1,2,3
js>
js> global[0]
4
js> global.length
3
3.1.5 Explanation
This behaviour is a consequence of What is this? and Bind is transient, together with JavaScript’s no-exceptions
design.
3.2 Closures
You’ll see closures a lot in JavaScript. The reasons for this are:
1. In JavaScript, Bind is transient.
2. It is callback functions attached to DOM elements that respond to events.
3. Most code attaches callbacks to individual DOM elements (rather than delegating to a parent element).
TEST('closures', function()
{
// A factory for creating get-set pairs.
var get_set_factory = function(){
var value;
var get = function(){
return value;
};
var set = function(new_value){
value = new_value;
};
return {
get: get,
set: set
};
};
tmp= get_set_factory();
var get_b = tmp.get;
var set_b = tmp.set;
20 Chapter 3. Functions
Core JavaScript Documentation, Release 0.0
set_a(12);
assert( get_a() === 12 );
set_b(13);
assert( get_b() === 13 );
});
3.2.2 Discussion
The factory function creates two functions, get and set, which refer to the variable value of the factory function. The
get function returns the value of this variable, while the set function changes it.
To test the factory function we create two get-set pairs. We then test that each set operation changes the value return
by its partner get operation.
Finally, we test that the two pairs don’t interfere with each other.
3.2.3 Explanation
Objects continue to exist so long as a reference remains to them. When the last reference to an object is removed (or
when the object is part of cyclic garbage) the object can be destroyed. When the return value of a function is stored
(say as the value of a variable) then this return value continues to exist. Similarly, anything referred to by the return
value continues to exist.
Closures come about when the return value of a function is itself a function defined within the outer function (or
contains references to such functions).
Suppose the outer function returns an inner function (or in other words a function that is defined within the outer
function). Suppose also that the inner function uses a variable (or parameter) of the outer function. In this situation
the inner function holds a reference to the value of this variable. The code in the inner function can read (or get) this
value. It can also write to this variable.
One final point. Each call of the outer function creates a new instantiation of the function variable.
3.2.4 Exercises
A programmer, perhaps in a hurry, misses out a var in the example above. So now it reads:
3.2. Closures 21
Core JavaScript Documentation, Release 0.0
JavaScript has a keyword this, whose value depends on the execution context. It is always an object (and never an
immutable, such as a number).
TEST('return_this', function()
{
var return_this = function(){
return this;
};
});
3.3.2 Discussion
The function return_this is a convenient way of getting of getting hold of the object that is the value of this. The code
that follows shows:
1. The default value of this is the global object.
2. When return_this is called as either a method (i.e. an attribute that is a function) or item of obj then this is obj
itself.
3. That the call and apply methods of a function allow us to set the value of this within a particular execution of
the function.
4. If we pass undefined to function call or apply then we get the global object as this.
3.3.3 Explanation
Throughout the execution of JavaScript code, the identifier this has a value, which is an object (and not an immutable
value). The value of this depends on how the JavaScript interpreter got to be executing the code.
1. At the command line this is the global object.
2. In a function this is the object from which the interpreter got the function.
22 Chapter 3. Functions
Core JavaScript Documentation, Release 0.0
3. Because global variables are attributes of the global object, the rule for calling global variables as functions is a
special case of the previous rule.
4.
Here’s a helpful way to look at the situation. It is as if the interpreter maintains a this stack, which starts containing a
single item, the global object. Each time an item access of an object is immediately followed by a function call, the
object is added to the this stack, and removed at the end of the function call. For all other function calls the global
object is added to the this stack.
During execution of a function the value of this is the element at the top of the this stack.
Finally, the function call and apply methods allow for a particular object to be placed at the top of the this stack before
the function is called.
3.3.4 Exercises
TEST('transient-bind', function()
{
// Set up the test object.
var return_this = function(){
return this;
};
var obj = {
return_this : return_this
};
assert( obj.return_this === return_this );
});
3.4.2 Discussion
We create an object that has a return_this method. The outcome of calling return_this depends on how it is called.
There’s a logic in the test code that produces contradictory, or at least surprising, outcomes.
From
we would expect
But this means that in obj.return_this() the obj is not relevant. But JavaScript (or its designers) wanted objects to have
methods, so they introduced a special rule.
3.4.3 Explanation
JavaScript does not distinguish classes and instances. Python, and perhaps other dynamic languages, does. This allows
Python to supply a bound method, which retains a reference to the object, when a function belonging to the class is
called on an instance.
Here’s a Python (version 3) command line dialogue that illustrates this.
>>> def method(): pass # The function.
...
>>> class A: method = method # The class.
>>> A.method # Function from class.
<function method at 0x15be518>
>>> A.method is method # Same as the original function.
True
>>> a = A() # Instance.
>>> a.method # Bound method, not original function.
<bound method A.method of <__main__.A object at 0x16b9d10>>
The odd behaviour shown in the JavaScript test code is a consequence of JavaScript not distinguishing between classes
and instances.
This can be omitted at a first reading, and omitted altogether if you never have to deal with code that uses the new
operator. If you do, then you must read about the missing new problem.
JavaScript has an object tree. This is a fundamental language feature. The new operator is a way of adding objects to
the tree. The create function, which is built-in to the latest version of JavaScript, can be defined in terms of new, and
vice versa.
TEST('new-operator', function()
{
// A 'traditional class'. It records 'this' as a side effect.
var this_in_Fn;
24 Chapter 3. Functions
Core JavaScript Documentation, Release 0.0
var Fn = function(){
this_in_Fn = this;
};
});
3.5.2 Discussion
We saw that when Fn is called without the new then during its execution the value of this is the global object. If the
constructor Fn mutates this (and almost every constructor does), then it is the global object that is mutated.
This is very bad:
1. You may clobber someone elses data.
2. Someone else may clobber your data.
3. If you create two instances, you will clobber your own data.
4. Testing for this error involves extra work.
5. Bugs that arise from this error can appear random and can be hard to find.
Note: Crockford writes A much better alternative is to not use new at all.”
3.6 Arguments
This can be omitted at a first reading. The main point here is that in JavaScript
1. There is no checking for number and type of arguments.
2. The arguments actually supplied are stored in a special variable, called arguments.
3. The value of arguments is almost, but not exactly, an array.
TEST('return_arguments', function()
{
var return_arguments = function(){
return arguments;
};
});
This can be omitted at a first reading. Mark Miller is one of the leading JavaScript experts at Google. He’s been
credited an ingenious application of call that gives a simple and reliable test for whether or not an object is an array.
This may not sound very much, but it is something that had been puzzling the other experts for some years. For
example, Crockford in his Good Parts (published 2008, page 61) gives a much more complex and less reliable solution
to this problem.
The moral of this story is that even the experts in JavaScript can have difficulty finding the best way to solve a simple
problem.
26 Chapter 3. Functions
Core JavaScript Documentation, Release 0.0
TEST('millers-device', function()
{
var array = [];
var obj = {};
var fn = function(){};
});
Here’s some JavaScript code I found somewhere. I’ve changed the names of everything to hide the origin of the code.
But it’s not something I’ve made up.
The exercise is
1. Understand and describe what it does.
2. Provide clearer code that does the same thing.
3. Suggest a better way of going about things.
}(AAA || {}));
4.1.1 Hints
1. The code creates and calls an anonymous function, a bit like this.
28
Core JavaScript Documentation, Release 0.0
}(AAA || {}));
We are making sure that there’s an object AAA and giving it an attribute AAA.fff, which is a function. We won’t say
more about the function in this summary.
First, we’ll hoist the argument to the anonymous function into its body. We can always do this, without changing the
meaning, provided the function is called only once.
}();
There, that’s better. We no longer have to read to the end of the function to find out what BBB is (and in particular its
relationship with AAA).
Next, we’ll hide (or ignore) the assigment to BBB.fff. This assignment sets an attribute on the object pointed to by the
variable BBB. In other words, it mutates the object pointed to by BBB, but the variable BBB still points to the same
object as it did before.
AAA || {}
evaluates to AAA, while if AAA is undefined then it evaluates to the newly created object literal (the left-and-right
curly braces). In either case, this value is assigned to the local variable BBB, which is then returned by the anonymous
function and bound to AAA.
Put another way, if AAA starts off as undefined then it is bound to a new object, while if it starts off as an object then
it finishes as the same object.
Therefore, our truncated version of the original exercise is equivalent to
This idiom is very common in JavaScript, and is (as here) a consequence of using objects as namespaces (which is
better than using the global object) and defensiveness about the order in which files are loaded.
Here’s a command line session that shows this equivalence. To begin with AAA is undefined and the assignment binds
AAA to a new object. For the second assignment AAA is defined and the assignment binds AAA to the object is
already bound to.
Reading the code, it looks as if BBB is local to the anonymous function. As a variable it is, but its value is not. The
value of BBB is bound to the global object AAA at the end of the function call.
The following is almost equivalent to our original JavaScript code. Note that BBB.hhh has been changed to AAA.hhh.
AAA.fff = function(ccc)
{
return {
ggg: function(ddd)
{
return AAA.hhh(ddd, ccc.iii());
}
};
};
4.2.5 Caveat
Why did I say almost equivalent? Because we might subsequently do something like
For the original code the closure variable BBB still exists and points to the same object as CCC, even though AAA is
undefined. Thus, the reference to BBB.hhh in the original code will continue to work.
For the revised code, when the value of AAA is set to undefined the reference to AAA.hhh will fail.
Here’s some more JavaScript code I found. I’ve changed the names of everything to hide the origin of the code. But
it’s not something I’ve made up.
tgt.aaa = src.get_aaa();
tgt.bbb = src.get_bbb();
tgt.ccc = src.get_ccc();
tgt.ddd = src.get_ddd();
tgt.eee = src.get_eee();
tgt.fff = src.get_fff();
tgt.ggg = src.get_ggg();
tgt.hhh = src.get_hhh();
};
The exercise is
1. Rewrite the function copy_attributes so that it makes a loop over
var keys = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh'];
3. Write a Fields class such that something like this will work:
4.3.1 Hints
1. In JavaScript attribute access and item access are the same. First we use item access to set:
js> a = {}
[object Object]
js> key = 'attrname'
attrname
js> a[key] = 42
42
js> a.attrname
42
Here’s the code we are studying. The first task is make a loop that does the assignments.
tgt.aaa = src.get_aaa();
tgt.bbb = src.get_bbb();
tgt.ccc = src.get_ccc();
tgt.ddd = src.get_ddd();
tgt.eee = src.get_eee();
tgt.fff = src.get_fff();
tgt.ggg = src.get_ggg();
tgt.hhh = src.get_hhh();
};
As previously hinted, we use item access rather than attribute access. The problem with attribute access is that the
name of the key is hard-coded and we want it to vary during the loop.
Here’s the solution. There’s a bit of overhead in setting up the loop, but the body of the loop is straightforward.
var keys = ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff', 'ggg', 'hhh'];
var i;
var key;
The second task is to write a factory function that takes the list of keys as a parameter.
This is a common refactoring pattern, and so is worth learning well. First we give the code, and then the explanation.
Here’s what we’ve done. We created a wrapper function, with keys as its only parameter. This will be our factory
function. We’ve placed the original function into the body of the wrapper, and return it (as the value of the wrapper
function). Finally, we clean up by removing the keys constant from the inner function.
There, it’s done! When the factory function executes it returns an instance of the inner function for which keys is the
argument we supplied to the factory.
The third task is to write a Fields class that has a copy_attributes method.
Here’s a function prototype based solution. It relies on the user of the class supplying the new operator to create the
new instance (and chaos results if the user forgets).
Fields.prototype = {
copy_attributes : function(src, tgt){
A minor point is the trailing sequence of right curly braces. Two are followed by a semicolon, but the middle one is
not. Why? What happens if we put a semicolon there?
Here’s another solution to the third task, which does not require the user to supply a new operator when creating
instances. Instead it uses a factory function, which we call Class , that creates an instance constructor function from a
prototype object.
fields.__init__ = function(keys){
this.keys = keys;
};
Recall that this-based instance methods are somewhat fragile. In other words, code like this will fail (and make
unwanted changes to the global object):
var fn = instance.method;
element.onclick(fn);
Let’s say that a class has bound methods if we can safely pass around instance methods. In other words, we want
instance.method to have no dependence on the value of this.
We can achieve this by using a different class factory function.