Adventures in JavaScript Development

In Search of JavaScript Developers: A Gist

| Comments

I posted a gist the other day in an attempt to locate some JavaScript help. I’m finding, lately, that I’m being asked to do more work than I can comfortably take on without having some overflow capacity, but I’ve been a little lax at actually identifying people who can provide that capacity up until now. That meant I was turning down work, and that’s not a good thing.

I had a great time the last couple of days watching people come up with solutions, and was especially gratified that so many people who weren’t looking for work thought that answering JavaScript questions sounded like fun.

A lot of people have asked if I’d be providing “answers” to the questions I posed in the gist, so I wanted to try to do that, mixed in with a bit of commentary about what I was looking for.

Question 1: Writing Readable but Terse Code

1
2
3
4
5
6
// 1: how could you rewrite the following to make it shorter?
if (foo) {
  bar.doSomething(el);
} else {
  bar.doSomethingElse(el);
}

Answers to this question told me whether to keep reading; I was looking for something like:

1
2
3
4
5
bar[foo ? 'doSomething' : 'doSomethingElse'](el);

// OR

bar['doSomething' + (foo ? '' : 'Else')](el);

To be honest, the second one is maybe a little too clever. But generally, I wanted to see submissions that understood using the ternary operator instead of an if statement to create shorter but still-readable code when all that is different is a method name.

Some people submitted an answer that used a ternary operator, but didn’t take advantage of it to just switch the method name:

1
foo ? bar.doSomething(el) : bar.doSomethingElse(el);

This is an improvement, definitely, but there’s room for more.

Question 2: Understanding Variable Scope

1
2
3
4
5
6
var foo = 'hello';

(function() {
  var foo = foo || 'world';
  console.log(foo);
})();

This was a question where, in hindsight, what I was looking for may not have been super-clear. I got a lot of right-enough answers, but really what I wanted to hear was that the || statement was absurd, because foo would always evaluate to 'world' due to variable hoisting.

This can be sort of a sort of crazy thing to wrap your head around, but basically JavaScript scans the contents of a function for var statements before it runs the function. Any variable initialized with a var statement inside the function will become local to the function, even if the variable is “used” before it is initialized. Changing the order of the two lines inside the function shows this readily:

1
2
3
4
5
6
var foo = 'hello';

(function() {
  console.log(foo); // undefined!
  var foo = foo || 'world';
})();

Some submissions thought I wanted access to the external foo inside the closure — not an unreasonable interpretation of the question.

1
2
3
4
5
6
var foo = 'hello';

(function(f) {
  var foo = f || 'world';
  console.log(foo);
})(foo);

Anyway, there were lots of right-enough answers, but anyone who talked about hoisting definitely caught my eye.

Question 3: Working with Objects and Prototypes

This question was looking for really basic understanding of prototypes. It also was written to be a tad hard to follow, lumping all the questions into a single paragraph, to simulate a not-atypical client request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 3: given the following code, how would you override the value of the
// bar property for the variable foo without affecting the value of the
// bar property for the variable bim? how would you affect the value of
// the bar property for both foo and bim? how would you add a method to
// foo and bim to console.log the value of each object's bar property? how
// would you tell if the object's bar property had been overridden for the
// particular object?
var Thinger = function() {
  return this;
};

Thinger.prototype = {
  bar : 'baz'
};

var foo = new Thinger(),
    bim = new Thinger();

The good submissions broke the question down into separate comments, and then showed the answers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// override the bar prop for foo w/o affecting bim
foo.bar = 'new value';

// change the bar prop for both foo and bim
// (*if* it hasn't been overridden locally!)
Thinger.prototype.bar = 'another new value';

// we could delete foo.bar now and it would get
// the prototype value instead
// delete foo.bar;

// add a method to foo and bim to log bar
Thinger.prototype.logger = function() {
  console.log(this.bar);
};

// check if bar has been overridden
foo.hasOwnProperty('bar'); // true
bim.hasOwnProperty('bar'); // false

Question 4: Iterating over Objects

This one was pretty basic:

1
2
3
4
5
6
7
8
// 4: given the following code, and assuming that each defined object has
// a 'destroy' method, how would you destroy all of the objects contained
// in the myObjects object?
var myObjects = {
  thinger : new myApp.Thinger(),
  gizmo : new myApp.Gizmo(),
  widget : new myApp.Widget()
};

Really I just wanted to see people iterate over an object without the use of a helper like jQuery.each. The hasOwnProperty check may seem like overkill, but I was glad when people didn’t leave it out. Adding in the delete statement was another nice touch, though not strictly required by the question.

1
2
3
4
5
6
for (var obj in myObjects) {
  if (myObjects.hasOwnProperty(obj)) {
    myObjects[obj].destroy();
    delete myObjects[obj];
  }
}

Question 5: Solving Deceptively Simple Problems

This question was probably the most fun, because even though it was a dead-simple task, the answers were all over the map. This was the question:

1
2
3
4
5
6
// 5: given the following array, create an array that contains the
// contents of each array item repeated three times, with a space between
// each item. so, for example, if an array item is 'foo' then the new
// array should contain an array item 'foo foo foo'. (you can assume the
// library of your choice is available)
var myArray = [ 'foo', 'bar', 'baz' ];

Rather than going through the different answers one at a time, I’m just going to tell you to visit this JSPerf test page to see some of the variations, and their relative performance.

This brings up a good question tweeted by Ryan Florence:

T | F – In #JavaScript, 90% of the time we do stuff only a few times, maybe hundreds. Therefore, 90% of the time Readability > Performance.

I tend to come down on the side of readability and compression over straight-up perf, precisely because we’re rarely doing anything that’s actually that intensive. On the other hand, we should avoid doing things that are outright stupid; where the line gets drawn depends a lot, I think, on experience.

Here’s the thing, though: Something that seems like a gratuitous and obscure optimization to a less experienced developer might seem completely readable and obvious to a more experienced developer. How to balance this? Can comments bridge the gap? Should the gap be bridged? I dunno.

Question 6: Basic jQuery Best Practices and DRY

I see way too much code in real life that looks like this question.

1
2
3
4
5
6
7
8
9
10
11
12
// 6: how could you improve the following code?
$(document).ready(function() {
  $('.foo #bar').css('color', 'red');
  $('.foo #bar').css('border', '1px solid blue');
  $('.foo #bar').text('new text!');
  $('.foo #bar').click(function() {
    $(this).attr('title', 'new title');
    $(this).width('100px');
  });

  $('.foo #bar').click();
});

There are a slew of things wrong in this tiny snippet. First and foremost, making the same selection repeatedly suggests that the author fundamentally doesn’t understand what their code is doing, or the expense they’re incurring in doing it; the selection should be made once, and then the selection should be cached and/or the methods should be chained.

While it wasn’t imperative, submitters did well to point out that CSS changes should be made via class names instead of hard-coded CSS in JavaScript; they also did well to put the click handler in a named function. Finally, while there may be cases where an ID selector needs to be prefixed by a class, i.e. .foo #bar, I appreciated it if people questioned this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$(document).ready(function() {
  var handleClick = function(el) {
        el.attr('title', 'new title')
          .width('100px');
      },

      bar = $('#bar')
        // ideally: use a class for this
        .css({
          color : 'red',
          border : '1px solid blue'
        })
        .text('new text!')
        .click(function(e) {
          handleClick($(e.target));
        });

  handleClick(bar);
});

Question 7: Asynchronicity

This is a pretty newby thing, but I wanted to make sure people understood the basic concept of async requests — that is, you can’t set the value of a variable inside an XHR’s callback and expect that value to be available immediately.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(function() {
  var foo;

  dojo.xhrGet({
    url : 'foo.php',
    load : function(resp) {
      foo = resp.foo;
    }
  });

  if (foo) {
    // run this important code
  }
})();

Fixing this just involves waiting for the XHR to complete before running the code that expects foo to be set. (Alternately, you could make the request run synchronously by setting sync : true in the XHR config object.)

There was one other issue with this code as well: Dojo needs to know the response should be handled as JSON, else it will handle it as text. If a submitter missed this, I didn’t hold it against them — Pete Higgins actually had to point it out to me :) That said, it would become pretty obvious pretty quickly in real code.

1
2
3
4
5
6
7
8
9
10
11
(function() {
  dojo.xhrGet({
    url : 'foo.php',
    handleAs : 'json'
  })
  .addCallback(function(resp) {
    if (resp && resp.foo) {
      // do stuff
    }
  });
})();

Note that the callback function could also be specified in the XHR config object using the load property; Dojo’s XHRs are great in that while you can specify everything in a config object, you can also attach callbacks to the return value of the XHR methods. You should read more about this because it is very pleasant.

Question 8: DRY

Repetitive code is dumb.

1
2
3
4
5
6
7
// 8: how could you rewrite the following code to make it shorter?
(function(d, $){
  $('li.foo a').attr('title', 'i am foo');
  $('li.bar a').attr('title', 'i am bar');
  $('li.baz a').attr('title', 'i am baz');
  $('li.bop a').attr('title', 'i am bop');
})(dojo, dojo.query);

How far you want to go with DRYing this out is debatable, but to me this cries out for improvement. Here’s what I’d do:

1
2
3
4
5
(function(d, $){
  d.forEach(['foo', 'bar', 'baz', 'bop'], function(c) {
    $('li.' + c + ' a').attr('title', 'i am ' + c);
  });
})(dojo, dojo.query);

I’d be lying if I didn’t mention that I also wanted to show here how easy it is to make Dojo look like jQuery. After all, it’s just JavaScript, right?

Question 9: DOM Manipulation Best Practices & DRY

I thought it was well-known that we don’t append 202 things to the DOM one at a time; the good news is, most people did know this. The bad news is, some people did not.

In addition to doing 202 appends, this code also does 202 selections. To top it off, the iterator i is global because we didn’t prefix it with var.

1
2
3
4
5
6
7
8
9
// 9: how would you improve the following code?
for (i = 0; i <= 100; i++) {
  $('#thinger').append(
    '<p><span class="thinger">i am thinger ' + i + '</span></p>'
  );
  $('#gizmo').append(
    '<p><span class="gizmo">i am gizmo ' + i + '</span></p>'
  );
}

Here’s a fix:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var thingerDom = [], gizmoDom = [],
    tpl = '<p><span class="%s">i am %s %i</span></p>',
    tplFn = function(str, i) {
      return tpl.replace(/%s/g, str).replace(/%i/g, i);
    },
    i;

for (i = 0; i <= 100; i++) {
  thingerDom.push(tplFn('thinger', i));
  gizmoDom.push(tplFn('gizmo', i));
}

$('#thinger').append(thingerDom.join(''));
$('#gizmo').append(gizmoDom.join(''));

There’s more that could be done here to DRY this out a bit more, but the fix addresses the main problem of excessive DOM manipulation.

Question 10: Loose Typing

Numbers in JavaScript suck, especially when the user enters them.

1
2
3
4
5
6
// 10: a user enters their desired tip into a text box; the baseTotal,
// tax, and fee values are provided by the application. what are some
// potential issues with the following function for calculating the total?
function calculateTotal(baseTotal, tip, tax, fee) {
  return baseTotal + tip + tax + fee;
}

How you’d actually deal with this problem would probably depend on the business logic of your application; you may be well-advised to convert everything to integers instead of trying to deal with decimals, because math with floats in JavaScript can have issues.

What I wanted to see in submissions, though, was an awareness that the tip would come to us as a string, and we couldn’t just add it to the other arguments and expect a useful result. I was more interested in the discussion of this problem, and other problems that could arise, but here’s at least the beginning of a solution:

1
2
3
4
5
6
7
8
9
10
function calculateTotal(baseTotal, tip, tax, fee) {
  // convert the tip to a number using base 10;
  // allow for a NaN result from parseFloat
  tip = parseFloat(tip) || 0;

  // don't allow a negative tip
  if (tip < 0) { tip = 0; }

  return baseTotal + tip + tax + fee;
}

Question 11: Array Mapping

For this question, I was looking for code that used a map method to return an array by running a function on each item in the array. Some people used a forEach method to iterate over the array instead, and then push the results to a new array they created. I guess this is OK, but it’s not my preference.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 11: given the following data, write code that returns an array
// containing the name of each item, followed by a comma-separated list of
// the item's extras, if it has any. e.g.
//
//    [ "Salad (Chicken, Steak, Shrimp)", ... ]
//
// (you can assume the library of your choice is available)
var menuItems = [
  {
    id : 1,
    name : 'Salad',
    extras : [
      'Chicken', 'Steak', 'Shrimp'
    ]
  },
  {
    id : 2,
    name : 'Potato',
    extras : [
      'Bacon', 'Sour Cream', 'Shrimp'
    ]
  },
  {
    id : 3,
    name : 'Sandwich',
    extras : [
      'Turkey', 'Bacon'
    ]
  },
  {
    id : 4,
    name : 'Bread'
  }
];

Here’s an answer:

1
2
3
4
5
6
7
var newArray = dojo.map(menuItems, function(item) {
  var ret = item.name;
  if (item.extras && item.extras.length) {
    ret += '(' + item.extras.join(', ') + ')';
  }
  return ret;
});

Bonus 1: Functional Programming 101

This was a late addition, courtesy of Andrew Hedges, so not everyone saw it:

1
2
// BONUS: write code such that the following alerts "Hello World"
say('Hello')('World');

I wanted to see people understand that functions could return other functions, and that the returned function has access to the scope of the wrapper function:

1
2
3
4
5
var say = function(first) {
  return function(second) {
    alert(first + ' ' + second);
  }
};

Some people got pleasantly carried away with this question; check out this JSFiddle from Colin Snover.

Bonus 2: Attention to Detail

This last bonus was riddled with errors, including some that I made accidentally when I wrote it at 2 a.m.

1
2
3
4
5
6
7
8
9
10
11
12
// BONUS: what is the faulty logic in the following code?
// how would you fix it?
var date = new Date(),
    day = date.getDate(),
    month = date.getMonth(),
    dates = [];

for (var i = 0; i <= 5; i++) {
  dates.push(month + '/' + (day + i));
}

console.log('The next five days are ', dates.join(', '));

Here’s what you should see:

  • The for loop will return 6 dates, not 5.
  • The method for calculating the date for each successive date could end up with nonexistent dates (32, 33, etc.), and it doesn’t change the month when it should.
  • The getMonth method on the date object returns a zero-indexed month.

Here’s a fix:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function() {
  var date = new Date(),
      otherDate = new Date(),
      day = date.getDate(),
      future = 5,
      dates = [],
      i;

  for (i = 1; i <= future; i++) {
    otherDate.setDate(day + i);
    newMonth = otherDate.getMonth() + 1;
    newDay = otherDate.getDate();
    dates.push(newMonth + '/' + newDay);
  }
})();

Postscript

I want to be really clear that I’m not some super-awesome and infallible JavaScript developer, and more to the point, there was a time in the not-too-distant past where I would have failed my own quiz miserably. While my main goal in putting this together was to find some skilled developers to help me out, I also wanted to provide a tool for exposing up-and-coming developers to some slightly more advanced concepts of JavaScript. I hope that, whatever your skill level, you found it to be at least entertaining, and at best, useful. I also hope you’ll forgive any gross errors I’ve made in the answers above, though I tried really hard to test them all.

Post-Postscript

  • To the person who complained that I included code from a library other than jQuery, and who helpfully illustrated their point with the graph that shows jQuery is obviously better than anything that ever was: I’m not sure whether to laugh or cry or just be really snarky. I’m looking for JavaScript developers. If you don’t see the value in knowing more than just jQuery, and if you can’t find your way through relatively trivial non-jQuery code, you need not apply.
  • If you found the questions vague and requiring a lot of assumptions or guesswork, well, welcome to consulting — if such things make you uncomfortable, we’re not going to be a good fit. I was looking for people to tell me what they know, to impress me, to point out where they saw holes in the questions, to take initiative. This was not a multiple choice test; it was an interview.

License

If you think the quiz would be useful to you as you’re looking for a JavaScript developer to call your own, it’s licensed under the WTFPL, which you should read just because it’s funny.

Comments