The Web Security Topics Series
Objects
[...]
Superficially, JavaScript’s implementation of objects seems simple. An object is defined as a collection of named properties. In other words, it is a mapping from names to values, very much like a hash, lookup table or associative array in other programming languages. Since a function in JavaScript is a value, methods are just properties whose names map to functions. If obj
is an object, and prop
is the name of one of its properties, either of the expressions obj.prop
or obj['prop']
can be used to obtain the property’s value. If this value is a function – so prop
is a method – it can be called like any other function. In this case, the dot notation is usually preferred: obj.prop(a, b, c)
, for example. The square brackets are needed if the name is not a constant but has been computed dynamically. [...]
[...]
[Defining objects] is often done by using an object literal, which is just a way of listing a collection of properties. For example, suppose you have a sequence, perhaps a sequence of pages comprising a long article, and you need to be able to move forwards and backwards in the sequence. You could create an object to perform this job, as shown in Listing 10.
Listing 10
var counter = {
_min: 1,
_max: 10,
_current: 1,
next: function() {
if (++this._current > this._max )
throw new Error('value is too large')
return this
},
previous: function() {
if (--this._current < this._min )
throw new Error('value is too small')
return this
},
value: function() {
return this._current
}
}
As you can see, the object literal is enclosed in curly brackets. Within these, there is a list of properties, each consisting of the property’s name (which must be a legal JavaScript name), followed by a colon and the property’s value. The properties in the list are separated by commas.
This object has six properties, of which three are data values. The other three are functions, so they are the object’s methods. Within each method’s body, a special variable called this
refers to the object itself. Thus, the next
and previous
methods change the value of the _current
property, and the value
method returns its value. The values _min
, _max
and _current
should never be accessed directly from outside the object. The use of an underscore at the beginning of their names is a convention that indicates this fact, but there is nothing to prevent a program referring to counter._current
. In other words, JavaScript’s objects do not provide any data hiding. This is a significant deficiency which can only be rectified by using functions in a special way, as we will describe later.
[...]
You can assign objects to variables, as we did in Listing 10, pass them as arguments to functions, and return them as results. You may use an object literal in any of these contexts, and it is common to create an object as a literal while passing it to a function.
Objects are assigned and passed to functions by reference, which means that they are not copied. This means that an assignment creates an alias for an object, which is often a dangerous thing to do, but as we will see shortly, it is sometimes convenient. In the case of function calls, any changes made inside the called function will affect the original object.
[...]
The behaviour of the variable this
, although consistent, can sometimes be surprising. We stated above that within each method’s body a special variable called this
refers to the object itself, but although that seems straightforward it fails to take account of one of the worst features of JavaScript, the Global
object. All the built-in constructors and several utility functions are properties of the Global
object, and so are any functions that are defined without being methods of some other object. This includes functions defined at the top level of a program, as you might expect, but also functions defined locally inside methods, and all anonymous functions. References to the Global
object are transparent: f()
is interpreted as Global.f()
, for example.
In the implementations of JavaScript in Web browsers, the window
object is a property of the Global
object, but the window
object’s value is also the Global
object, leading to the familiar phenomenon that unqualified names refer to properties of the window
object. In Node.js, the variable global
behaves in the same way.
The existence of the Global
object means that it is possible to use this
in the body of any function: if the function was not defined as a method of some other object, the value of this
will be the Global
object. Confusion may ensue.
A common mistake is to assume that this
behaves like a free variable if it is used inside the body of an anonymous function called from a method. It doesn’t do that. Instead, it behaves as we have just described. You may find this clearer in Listing 12. The same thing happens if the anonymous function is assigned to a variable or defined as a named function inside g
.
Listing 12
var obj = {
f: function(fun) {
// this is obj
fun()
},
g: function() {
// this is obj
this.f(function() {
// this is Global
})
}
}
Often, it is necessary to access the value of a method’s parent object inside an anonymous function used within the method. As Listing 12 shows, using this
to refer to the parent object doesn’t work. There are two ways that do work, though.
The “high-tech” way makes use of a method called bind
, which is a property of all functions. (We will explain how this comes about shortly.) If f
is a function, taking n arguments a1, a2, ..., an
, f.bind
is a function taking n+1 arguments, a, a1, a2, ..., an
. Within this new function, this
is initialized to a
and all the other arguments are passed as normal. In other words, calling bind
through f
with a
as the first argument makes f
behave as though it was a method of the object a
. Hence, in the example shown in Listing 12, instead of passing an anonymous function to the method f
, using this.f(function() { ... })
, you would bind the function to obj
and call this.f(function() { ... }.bind(this))
.
The “low-tech” way is simply to save the value of this
in a local variable, often called self
, on entry to a method, and to refer to self
instead of this
inside any anonymous or local functions in the method, as in Listing 13.
Listing 13
var obj = {
f: function(fun) {
fun()
},
g: function() {
var self = this
// this is obj
this.f(function() {
// this is Global, but self is obj
})
}
}
(Some programmers prefer to call the local variable that
, so they can write var that = this
.) This low-tech solution is simple, immediately comprehensible, and less ugly than the high-tech solution, so it is often to be preferred.
[...]
End of Extract
[Extracted from Javascript on the Server Using Node.js and Express by Nigel Chapman and Jenny Chapman]