###Comprehensive Technical Interview Question
Consider the following javascript module:
/**
* A module for tracking when a user visits the page.
*/
window.VisitingDateTracker = {
/**
* Initialize the tracker.
*/
init: function() {
this._visitTimestamp = new Date();
},
/**
* We don't want clients modifying the original _visitTimestamp, so they can
* use this method to retrieve it.
*/
getVisitTimestamp: function() {
return this._visitTimestamp;
},
/**
* @private
*/
_visitTimestamp: null
};
// IE8 Madness
if (typeof window.addEventListener === "function") {
window.addEventListener('load', VisitingDateTracker.init, false);
} else { // IE8
window.attachEvent('load', VisitingDateTracker.init);
}
We wish to capture the date/time when a user immediately entered our page so we can use it for analytics purposes (or come up with some other user story).
Q1-1: Will this code work as expected?
A1-1: No. If s/he answers "yes", ask why they think it will work, and try and point out the place where they go wrong. If they answer correctly, ask...
Q1-2: Why won't it work as expected?
A1-2: VisitingDateTracker.init
's receiver will be bound to the Global object (aka the window
object for browsers), which means that when init
tries to set this._visitTimestamp
it will set that property on window
instead of VisitingDateTracker
.
Q1-3: How could you modify the code to make it behave as expected?
A1-3: Two ways:
- The naive way: modify the callback for
window.addEventListener
window.addEventListener('load', function() { VisitingDateTracker.init() }, false);
- The robust way: Bind
init
's receiver toVisitingDateTracker
. Inside theVisitingDateTracker
Code:
init: (function() {/* ... */}).bind(this),
// ....
The ideal candidate would know (and tell you about) both ways. If s/he doesn't do it both ways ask if there's another way to do it without modifying either VisitingDateTracker
or addEventListener
, depending on how they did it. Also if they happen to use bind
, ask them if their code would work in IE8, or some other older browser (that doesn't support ES5). The correct answer to this is "no", and then ask him/her to write their own (basic) version of it. Something like:
var _bind = function(fn, ctx) {
return function() {
return fn.apply(ctx, Array.prototype.slice.call(arguments));
};
};
Points off for trying to extend the native function object and/or not passing through the arguments to the bound function. Bonus points for implementing partial composition.
As you can see you can go pretty crazy with this question.
Q2: Let's say we want to modify init
so that it can be called no more than one time, this way we can ensure that clients don't try and stupidly call init multiple times and skew the actual visiting timestamp for the user. Could you modify this code so that it will only execute the first time it is invoked?
A2: Something like
// inside VisitingDateTracker
init: (function() {
var hasBeenCalled = false;
return function {
if (hasBeenCalled) {
return;
}
hasBeenCalled = true;
// original init code goes here
};
})(),
// ....
If the candidate does this, as a follow up you can ask him/her to refactor that logic into some function (i.e. once
) that will execute an arbitrary function just one time.
var once = function(fn) {
var hasBeenCalled = false;
return function() {
if (hasBeenCalled) {
return;
}
hasBeenCalled = true;
return fn.apply(this, Array.prototype.slice.call(arguments));
};
};
Points off for not returning the result of fn.apply
and/or not passing in arguments
to fn
.
Q2-BONUS: Let's say you were writing this in a language other than Javascript, i.e. Python, Java, or C++. What other consideration for once
would you have to take into affect? (Good question for full-stack candidates/techncial leads, or just to test the candidate's knowledge of systems design)
A2-BONUS: The one that comes to my mind is concurrency. Two simultaneously-executing pieces of code could possibly invoke the one-time function at the "same time", and the second piece of code could invoke the function before the first piece of code sets hasBeenCalled
to true
. Thankfully in JS-land where everything is single-threaded we don't have to worry about this :) Solution to this problem: synchronize the critical region of this function using a mutex/semaphore.
Other problems the candidate may mention could be return types (in statically-typed languages) and variadic arguments.
Q3 (open-ended): Can you think of any ways to make this code more robust?
A3: Any of the following, including but not limited to:
- Wrap the code in an immediately-invoked function expression (
(function() { /* code goes here.. */ })()
) - Pass in the
window
object as a closure to the IIFE ((function(w) { /* ... */ })(window)
) - Make sure
window
is defined before binding to it - Etc.