Archive
JS 101 – Week 3
In Javascript, functions are first class objects. What does it mean to be a first class object?
A function can be used anywhere an object can be used; in particular, you can pass functions as method arguments. (We did this in last week’s homework by passing the print
function into the iterateAndOperate
method). This is in contrast to a language like Java where functions are not first class objects, and the programmer must use interfaces and anonymous classes to get around this problem. For instance, in Java, to delay the invocation of a function, you might do the following:
SwingUtilities.invokeLater(new Runnable() { @Override public void run() { System.out.println("I was run!"); } })
whereas in Javascript, you could do something like
invokeLater(function() { print("I was run") });
assuming there was an invokeLater
function.
Functions and variables share the same namespace. What does this mean and what important implication does this have?
This means that you must take care in not defining variables in the global namespace that have the same name as a function. For instance,
// global variable x = 5; function x(args) { alert(args); } // The global variable is shadowing the function name; // the function is not called x(5);
Douglas Crockford equates Javascript functions with Lambdas, and also mentions that they are a secure construct. Can you do some research and reflect on what he means by ‘secure construct’?
A lambda basically means that you can pass a function as an argument to another function; this is due to the aforementioned first class nature of functions. They are a secure construct because the scope of the functions is such that private variables in function A cannot be accessed by function B when A is passed into B. In other words,
function outer(func) { // "undefined", since the ‘privateVariable’ // field is scoped only to the inner function. return func.privateVariable; } function inner(a,b,c) { var privateVariable = 25; return a + ", " + b + ", " + c; } alert(outer(inner("cat","dog","bear")));
f
Can you explain the concept of a closure.
A closure means that an inner function continues to have access to variables defined inside of an outer function, even after the outer function has finished. I wrote an example you can view on jsfiddle.
What is the difference between a function and a method?
A method is a function that is bound to an object, and thus it has an implicit “self” reference.
In Javascript there is no implicit type checking of arguments passed to functions. This could lead to bugs if programmers are not careful about what they pass to functions. If you create a function, how would you protect it from well meaning but careless programmers?
You can do defensive checks within the function. For instance, if a method is supposed to take in a number, you could do the following:
function numericalFunction(x) { if (typeof(x) != "number") { throw("Expected numerical value for numericalFunction; got " + x); } // Proceed as normal }
You can also use the arguments
implicit variable in the function to check that the number of arguments the user passed in is equal to the number of arguments that the method expects.
Javascript functions have implicit access to something called this
. this
points to different things depending on how the function was created. Can you explain why we need this
, and what it represents in different type of functions.
If a function is created globally, its this
pointer will be to the DOMObject. If it’s created and bound to an object, the this
pointer points to that object. The following illustrates this:
function f() { return "f's this: " + this; } function nested() { function inner() { return "inner's this:" + this; } return inner(); } // o is an object; add a method do it var o = {}; o.f = f; o.nested = nested; o.newFunc = function() { return "newFunc's this: " + this; }; o.nestedFunc = function() { var inner = function() { return "nestedFunc's inner this: " + this; } return inner(); } print(f()); print(nested()); print(o.f()); print(o.nested()); print(o.newFunc()); print(o.nestedFunc()); // prints // f's this: [object DOMWindow] // inner's this:[object DOMWindow] // f's this: [object Object] // inner's this:[object DOMWindow] // newFunc's this: [object Object] // nestedFunc's inner this: [object DOMWindow] //
Note that only the non nested functions that have been bound to object o
point to that object as opposed to the DOMWindow.
The reason we want the this
pointer is to be able to introspect on the type and contents of the object that is calling the function. For instance, in my last assignment, I used the this
variable to produce a nice representation of an object:
nick = {name:"Nick",age:23}; // This function returns a string representation of whatever // is invoking it function selfStringRep() { var str = ""; for (var v in this) { // This is not an inherited property if (this.hasOwnProperty(v)) { str += "’" + v + "’: " + this[v] +"\n"; } } return str; } Object.prototype.toString = selfStringRep; print(nick.toString()); // prints //’name’: Nick //’age’: 23
4.1
The solution for the cat problem talks about a ‘set’ of names. A set is a collection of values in which no value may occur more than once. If names are strings, can you think of a way to use an object to represent a set of names?
Show how a name can be added to this set, how one can be removed, and how you can check whether a name occurs in it.
var cat_set = {}; // Add a cat cat_set["Tigger"] = true; // remove a cat delete cat_set["Tigger"]; // Check if cat exists cat_set["Frog"] != undefined // Wrap it all up nicely: in an object with functions: var cat_set = {}; cat_set.add_cat = function(cat) { this[cat] = true; } cat_set.remove_cat = function(cat) { delete this[cat]; } cat_set.cat_exists = function(cat) { return this[cat] != undefined; } cat_set.add_cat("Frisky"); // prints "true" show(cat_set.cat_exists("Frisky")) cat_set.remove_cat("Frisky"); // "false" show(cat_set.cat_exists("Frisky"))
4.2
Write a function range that takes one argument, a positive number, and returns an array containing all numbers from 0 up to and including the given number.
An empty array can be created by simply typing []. Also remember that adding properties to an object, and thus also to an array, can be done by assigning them a value with the = operator. The length property is automatically updated when elements are added.
// Assumes n >= 0 function range(n) { var array = []; for (var i = 0; i <= n; i++) { array[i] = i; } return array; }
4.3
split
andjoin
are not precisely each other’s inverse.string.split(x).join(x)
always produces the original value, butarray.join(x).split(x)
does not. Can you give an example of an array where.join(" ").split(" ")
produces a different value?
[" ", " ", " "].join(" ").split(" "); // equals ["", "", "", "", "", ""]
4.4
Write a function called startsWith that takes two arguments, both strings. It returns true when the first argument starts with the characters in the second argument, and false otherwise.
function startsWith(string, pattern) { return string.slice(0,pattern.length) === pattern; }
4.5
Can you write a function
catNames
that takes a paragraph as an argument and returns an array of names?Strings have an
indexOf
method that can be used to find the (first) position of a character or sub-string within that string. Also, whenslice
is given only one argument, it will return the part of the string from the given position all the way to the end.It can be helpful to use the console to ‘explore’ functions. For example, type
"foo: bar".indexOf(":")
and see what you get.
// The sentence is in the form "Blah blah: cat1, cat2, …, catN." function catNames(paragraph) { var colonIndex = paragraph.indexOf(":"); // Skip the colon and the following space var catListString = paragraph.slice(colonIndex + 2); return catString.split(", "); }
/*#p2pu-Jan2011-javascript101*/