Today I Learned

A Hashrocket project

83 posts about #javascript

Splat Arguments To A Function

Often times you have a function that takes a certain set of arguments. Like the following adder function:

var adder = function(a,b,c) {
  return a + b + c;
};

But you are left trying to pass in arguments as an array (e.g. [1,2,3]). You want to be able to splat the array of arguments so that it matches the function declaration. This can be done by using apply.

> adder.apply(undefined, [1,2,3])
6

Chrome DevTools navigation and editing tools

If you ever worked with editors such as Atom/Sublime Text you will appreciate the following: Chrome DevTools now sports fuzzy finders for your script/stylesheet files and for methods within them.

To get started open dev tools and hit Cmd+p start typing a filename or part of a filename: cmd-p

Then to jump to a specific method hit Cmd+Shift+P and start typing a method name: cmd-shift-p

If you are a big fan of multiple cursors, you’ll be happy to learn that those are now available within chrome developer tools:

cmd-d [while the cursor is on a word hit Cmd+d to duplicate the cursor to the next match]

holding alt [holding alt and dragging down will allow you to edit a vertical line]

To search all files (scripts/html markup) hit Cmd+Option+f: cmd-alt-f

Change the source in dev tools and hit Cmd+s the updated code will be run the next time it is called. DevTools will indicate it has a modified version by changing the background color to bright orange: change in memory

Avoid mutating array when sorting

I just ran into a hard to track down bug with code that amounted to this.

> a = [6,5,4,3,2,1]
[ 6, 5, 4, 3, 2, 1 ]
> a.map(function(x) { console.log(x); a.sort()})
6
2
3
4
5
6

Why is the second number 2 and not 5? Oh, because I was sorting the array. In my program I needed to gather some information about the collection as a whole and the sort statement was buried 2 function calls down, and didn’t realize the collection was being mutated while it was being iterated.

To avoid this, a best practice might be to copy the array before sorting it, two examples of that would be:

a.concat().sort()

And

a.slice(0).sort()

Same number of characters; choose your favorite.

Examine all jQuery events for namespace

In a big system you can lose track of all the events that might be registered with jQuery. An easy way to see them all is:

$._data(window, 'events')

jQuery allows namespacing events as well. This can look like:

$(myElement).on('click.nsMyElement', function() { console.log('click'})

And check it out you can see that through data

$._data(myElement, 'events')['click'][0]['namespace']
> nsMyElement

Best practice for event binding in JavaScript

When binding an event to a DOM element you often need a way to update your event in runtime. This presents a challenge because setting the new event will not override the existing one but instead create an additional event. In that case you need to namespace (jQuery terminology) your event which will allow you to replace it.

Problem:

$(window).on('scroll', function () { console.log('A') });

// Scroll output: 
// => A
// => A

$(window).on('scroll', function () { console.log('B') });

// Scroll output: 
// => A
// => B
// => A
// => B

Solution:

$(window).off('scroll.myPlugin').on('scroll.myPlugin', function () { console.log('A') });
$(window).off('scroll.myPlugin').on('scroll.myPlugin', function () { console.log('B') });

// Scroll output: 
// => B
// => B

Vanilla JS - when using the addEventListener use named functions (instead of anonymous..). It may be a good idea to keep an array of your event handlers in your object tree.

Solution:

var scrollHandler = function() { console.log('A') };
var scrollHandler2 = function() { console.log('B') };
window.addEventListener('scroll', scrollHandler);
window.removeEventListener('scroll', scrollHandler);
window.addEventListener('scroll', scrollHandler2);

// Scroll output: 
// => B
// => B

This is especially important when writing your own libraries to avoid collisions with user events.

Synchronous XHR is deprecated

Sometimes before I’ve figured out the right pattern for my app I use the synchronous xhr to get some naive green for my tests.

  var isAsync = false
  var xhr = new XMLHttpRequest()
  xhr.open('post', '/things.json', isAsync)
  xhr.send(JSON.stringify({things: things}))

But this time I got a nice deprecation notice at the console.

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/

And low and behold whatwg.org has a deprecation notice

An ES6 javascript array of numbers

If you want an array of numbers in ruby, you might do something like (0..127).to_a. Concise! I don’t really know how to do this in javascript but es6 has a couple of methods to help with a clever solution.

Array(100) gives you an array with 100 elements, all undefined.

array.keys() gives you an es6 iterator that will iterate through all the keys of the array, which happen to be 0 - 99.

Array.from() will turn an iterator into an Array!

So in just 3 weird steps you can get an array full of numbers.

Array.from(Array(127).keys())

Hopefully someone knows an easier way :).

Build URLs with Window Location

Want to build URLs with JavaScript? Try thewindow.location object.

This came from a React.js project we’re working on. It uses ES6 template string interpolation, which isn’t necessary but definitely is nice:

`${window.location.protocol}//${window.location.host}/posts/${this.props.postID}/edit`

Which becomes:

"https://secret-project.com/posts/42/edit"

Render 64-bit integers as strings in JSON

When rendering a server-side object containing 64-bit integers, precision can be lost if it is rendered as a number. For example, if a JSON document contains the number 9219976635399687875, JavaScript will parse it as 9219976635399688000. This is caused by JavaScript only having floating point numbers. To ensure very large numbers are not rounded, render the value out as a string, e.g. “9219976635399687875”.

V8 Optimizations

V8 will not define variables that would normally be within JS function scope until they’re actually called.

test('visiting /foo/bar', function(assert){
  var foo = server.create('foo');
  var bar = server.create('bar');

  andThen(function(){
      var someProp = foo.callProperty;
      debugger
  });
});

When the console opens, foo will be in scope, while bar will be undefined

Get first value from javascript Map

Map is a new key/value data structure in es6 (and its available in your chrome browser RIGHT NOW). But getting values out of a Map without using a key is complicated a little by having to go through a javascript iterator.

> m = new Map([[1,"a"],[2,"b"]])
Map {1 => "a", 2 => "b"}
> m.values()[0]
undefined
> m.values()
MapIterator {"a", "b"}

Its all good though, the next() function of a fresh iterator will always return the first value. Well, not really the first value but a value wrapped in an object with two values, the value you are looking for represented by “value” and “done” which tells you whether the iterator has run out of elements or not.

> m = new Map([[1,"a"],[2,"b"]])
Map {1 => "a", 2 => "b"}
> m.values().next().value
"a"

Javascript closures bind to the variable

If you’re a clever kind of javascript dev you create functions with variables from outside the function definition.

var i = 5;
function printSomething() {console.log(i);}
printSomething();
> 5

But you should watch yourself when using a closure within a loop.

> fns = []
> for(var i = 0; i < 3; i++) {fns.push(function () {console.log(i); })};
> fns[0]();
3
> fns[1]();
3
> fns[2]();
3

Yikes! The closure references the variable as it is now, not the variable as it was. You should only reference the variable if you don’t expect it to change (or if you want it to change).

> fns = []
> function createFn(i) { return function() { console.log(i); };}
> for (var i = 0; i < 3; i++) {fns.push(createFn(i))};
> fns[0]();
0
> fns[1]();
1
> fns[2]();
2

Pause Ember acceptance tests

While your acceptance tests are running you can use pauseTest() to pause the test suite and interact with your app. NOTE: this does not halt execution like debugger, it only pauses the test suite.

test("my acceptance test", function(assert) {
  visit('/');
  pauseTest();  // returns a promise that will never resolve
  andThen(function(){
    // make assertions
  });
});

Javascript keys are only strings!

You can use squares to access or set a value in an object, but that key is just getting converted to a string. And you can’t loop through the keys and expect the keys to be objects… they’re strings!

> a = {1: "x"}
{ '1': 'x' }
> a[/x/] = "y"
'y'
> a[1]
'x'
> a[/x/]
'y'
> a['1']
'x'
> a['/x/']
'y'
> for (var i in a) { console.log(i === 1); console.log(i === /x/) }
false
false
false
false

The microseconds since navigation started

Date.now will give you milliseconds since the epoch, but maybe what you really want is the time since the page started loading.

> performance.now()
162374.73599999974

Essentially that is the microseconds since the page started loading, but really its the microseconds since performance.timing.navigationStart which itself is defined as the:

time immediately after the user agent finishes prompting to unload 

which is defined here.

CoffeeScript doesn't have a ternary operator

CoffeeScript has no ternary operator! This blew me away, since CoffeeScript is so similar to Ruby.

In Ruby, a ? b : c translates to: Is a true? Ok, then return b, otherwise I’ll return c.

In CoffeeScript, a ? b : c translates to pure madness. If a is not null or "undefined", return a. Otherwise, return an object literal ({ b: c })…!?!

That’s crazy…..so instead of using a ternary (which doesn’t exist in CoffeeScript), call it a day with:

if a then b else c

jQuery Empty

Today I learned about the jQuery .empty() method.

.empty() removes all child nodes of the set of matched elements from the DOM. Consider it a delete button for whatever element you call it on.

.empty() is equivalent to .html('') but much faster.

h/t Cameron Daigle

JavaScript Casing

The ES5 spec defined two methods for changing the case of a string, .toUpperCase() and .toLowerCase(). They work just like Ruby’s upcase and downcase.

'test'.toUpperCase()
=> "TEST"
'TEST'.toLowerCase()
=> "test"

There are two similar methods called .toLocaleUpperCase() and .toLocaleLowerCase() which are intended to yield the correct result based on the host environment’s locale. I have not seen these methods in the wild but I’m curious if they are used.

Cmd-click a link open in a new tab with JavaScript

We have a little jQuery plugin that makes entire table rows clickable by catching a click event on a table row and assigning window.location to the href of the anchor in that row – here’s a simplified example:

$('table').on 'click', 'tbody tr', (e) ->
  $a = $(e.target).closest('tr').find('a')
  location.href = $a.attr('href')

… but this doesn’t account for users who might want to hold down command and click on a row to open it in a new tab. In order to do that, we check the e.metaKey property that jQuery has so helpfully provided:

$('table').on 'click', 'tbody tr', (e) ->
  $a = $(e.target).closest('tr').find('a')
  href = $a.attr('href')
  if e.metaKey
    open(href, '_blank')
  else
    location.href = href

Voila! (Disclaimer: If you use something like this in the wild, make sure to do some other checks, like for multiple links in rows, the existence of checkboxes, that sort of thing – but this is the core to get you started.)

thanks to Pavel for correcting my initial implementation, in which I failed to realize metaKey is available on any jQuery event type)

Throttling A Function Call

Imagine you have a JavaScript function that makes a request to your server. Perhaps it is sending user input from a textarea to be processed by the server. You may want to wrap this function in a keyboard event listener so that you are sure to react immediately to any user input. However, as the user starts typing away into this text area you may find that way to many requests are being fired off to the server. The request needs to be throttled.

You can roll your own approach to sufficiently intermittent server calls. Though, it turns out that underscore.js comes with two functions out of the box for this kind of behavior.

  • throttle will give you a function that wraps your function in a way that essentially rate-limits it to being called at most once every N milliseconds.

  • debounce, on the other hand, will give you a function that only calls your function once N milliseconds has passed since it was last called.

These are two subtly different approaches to making sure a function gets called, just not too often.

h/t Jake Worth