tutorial Hidden Features of JavaScript?




stackoverflow javascript (24)

What "Hidden Features" of JavaScript do you think every programmer should know?

After having seen the excellent quality of the answers to the following questions I thought it was time to ask it for JavaScript.

Even though JavaScript is arguably the most important Client Side language right now (just ask Google) it's surprising how little most web developers appreciate how powerful it really is.


You can access object properties with [] instead of .

This allows you look up a property matching a variable.

obj = {a:"test"};
var propname = "a";
var b = obj[propname];  // "test"

You can also use this to get/set object properties whose name is not a legal identifier.

obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.

Some people don't know this and end up using eval() like this, which is a really bad idea:

var propname = "a";
var a = eval("obj." + propname);

This is harder to read, harder to find errors in (can't use jslint), slower to execute, and can lead to XSS exploits.


You can use objects instead of switches most of the time.

function getInnerText(o){
    return o === null? null : {
        string: o,
        array: o.map(getInnerText).join(""),
        object:getInnerText(o["childNodes"])
    }[typeis(o)];
}

Update: if you're concerned about the cases evaluating in advance being inefficient (why are you worried about efficiency this early on in the design of the program??) then you can do something like this:

function getInnerText(o){
    return o === null? null : {
        string: function() { return o;},
        array: function() { return o.map(getInnerText).join(""); },
        object: function () { return getInnerText(o["childNodes"]; ) }
    }[typeis(o)]();
}

This is more onerous to type (or read) than either a switch or an object, but it preserves the benefits of using an object instead of a switch, detailed in the comments section below. This style also makes it more straightforward to spin this out into a proper "class" once it grows up enough.

update2: with proposed syntax extensions for ES.next, this becomes

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);

How about closures in JavaScript (similar to anonymous methods in C# v2.0+). You can create a function that creates a function or "expression".

Example of closures:

//Takes a function that filters numbers and calls the function on 
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
  var filteredNumbers = [];

  for (var index = 0; index < numbers.length; index++)
  {
    if (filterFunction(numbers[index]) == true)
    {
      filteredNumbers.push(numbers[index]);
    }
  }
  return filteredNumbers;
}

//Creates a function (closure) that will remember the value "lowerBound" 
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
  return function (numberToCheck) {
    return (numberToCheck > lowerBound) ? true : false;
  };
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);

You don't need to define any parameters for a function. You can just use the function's arguments array-like object.

function sum() {
    var retval = 0;
    for (var i = 0, len = arguments.length; i < len; ++i) {
        retval += arguments[i];
    }
    return retval;
}

sum(1, 2, 3) // returns 6

You may catch exceptions depending on their type. Quoted from MDC:

try {
   myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
   // statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
   // statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
   // statements to handle EvalError exceptions
} catch (e) {
   // statements to handle any unspecified exceptions
   logMyErrors(e); // pass exception object to error handler
}

NOTE: Conditional catch clauses are a Netscape (and hence Mozilla/Firefox) extension that is not part of the ECMAScript specification and hence cannot be relied upon except on particular browsers.


You can use the in operator to check if a key exists in an object:

var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true

If you find the object literals too ugly you can combine it with the parameterless function tip:

function list()
 { var x = {};
   for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
   return x
 }

 5 in list(1,2,3,4,5) //true

You can also extend (inherit) classes and override properties/methods using the prototype chain spoon16 alluded to.

In the following example we create a class Pet and define some properties. We also override the .toString() method inherited from Object.

After this we create a Dog class which extends Pet and overrides the .toString() method again changing it's behavior (polymorphism). In addition we add some other properties to the child class.

After this we check the inheritance chain to show off that Dog is still of type Dog, of type Pet, and of type Object.

// Defines a Pet class constructor 
function Pet(name) 
{
    this.getName = function() { return name; };
    this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function() 
{
    return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet) 
function Dog(name, breed) 
{
    // think Dog : base(name) 
    Pet.call(this, name);
    this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function() 
{
    return 'This dogs name is: ' + this.getName() + 
        ', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true

Both answers to this question were codes modified from a great MSDN article by Ray Djajadinata.


Know how many parameters are expected by a function

function add_nums(num1, num2, num3 ){
    return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.

Know how many parameters are received by the function

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6

I could quote most of Douglas Crockford's excellent book JavaScript: The Good Parts.

But I'll take just one for you, always use === and !== instead of == and !=

alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true

== is not transitive. If you use === it would give false for all of these statements as expected.


Private Methods

An object can have private methods.

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    // A private method only visible from within this constructor
    function calcFullName() {
       return firstName + " " + lastName;    
    }

    // A public method available to everyone
    this.sayHello = function () {
        alert(calcFullName());
    }
}

//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();

// This fails since the method is not visible from this scope
alert(person1.calcFullName());

Here are some interesting things:

  • Comparing NaN with anything (even NaN) is always false, that includes ==, < and >.
  • NaN Stands for Not a Number but if you ask for the type it actually returns a number.
  • Array.sort can take a comparator function and is called by a quicksort-like driver (depends on implementation).
  • Regular expression "constants" can maintain state, like the last thing they matched.
  • Some versions of JavaScript allow you to access $0, $1, $2 members on a regex.
  • null is unlike anything else. It is neither an object, a boolean, a number, a string, nor undefined. It's a bit like an "alternate" undefined. (Note: typeof null == "object")
  • In the outermost context, this yields the otherwise unnameable [Global] object.
  • Declaring a variable with var, instead of just relying on automatic declaration of the variable gives the runtime a real chance of optimizing access to that variable
  • The with construct will destroy such optimzations
  • Variable names can contain Unicode characters.
  • JavaScript regular expressions are not actually regular. They are based on Perl's regexs, and it is possible to construct expressions with lookaheads that take a very, very long time to evaluate.
  • Blocks can be labeled and used as the targets of break. Loops can be labeled and used as the target of continue.
  • Arrays are not sparse. Setting the 1000th element of an otherwise empty array should fill it with undefined. (depends on implementation)
  • if (new Boolean(false)) {...} will execute the {...} block
  • Javascript's regular expression engine's are implementation specific: e.g. it is possible to write "non-portable" regular expressions.

[updated a little in response to good comments; please see comments]


Numbers are also objects. So you can do cool stuff like:

// convert to base 2
(5).toString(2) // returns "101"

// provide built in iteration
Number.prototype.times = function(funct){
  if(typeof funct === 'function') {
    for(var i = 0;i < Math.floor(this);i++) {
      funct(i);
    }
  }
  return this;
}


(5).times(function(i){
  string += i+" ";
});
// string now equals "0 1 2 3 4 "

var x = 1000;

x.times(function(i){
  document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document

Be sure to use the hasOwnProperty method when iterating through an object's properties:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

This is done so that you will only access the direct properties of anObject, and not use the properties that are down the prototype chain.


To properly remove a property from an object, you should delete the property instead of just setting it to undefined:

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

The property prop2 will still be part of the iteration. If you want to completely get rid of prop2, you should instead do:

delete obj.prop2;

The property prop2 will no longer will make an appearance when you're iterating through the properties.


"Extension methods in JavaScript" via the prototype property.

Array.prototype.contains = function(value) {  
    for (var i = 0; i < this.length; i++) {  
        if (this[i] == value) return true;  
    }  
    return false;  
}

This will add a contains method to all Array objects. You can call this method using this syntax

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");

Private variables with a Public Interface

It uses a neat little trick with a self-calling function definition. Everything inside the object which is returned is available in the public interface, while everything else is private.

var test = function () {
    //private members
    var x = 1;
    var y = function () {
        return x * 2;
    };
    //public interface
    return {
        setx : function (newx) {
            x = newx;
        },
        gety : function () {
            return y();
        }
    }
}();

assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());

If you're Googling for a decent JavaScript reference on a given topic, include the "mdc" keyword in your query and your first results will be from the Mozilla Developer Center. I don't carry any offline references or books with me. I always use the "mdc" keyword trick to directly get to what I'm looking for. For example:

Google: javascript array sort mdc
(in most cases you may omit "javascript")

Update: Mozilla Developer Center has been renamed to Mozilla Developer Network. The "mdc" keyword trick still works, but soon enough we may have to start using "mdn" instead.


Prototypal inheritance (popularized by Douglas Crockford) completely revolutionizes the way you think about loads of things in Javascript.

Object.beget = (function(Function){
    return function(Object){
        Function.prototype = Object;
        return new Function;
    }
})(function(){});

It's a killer! Pity how almost no one uses it.

It allows you to "beget" new instances of any object, extend them, while maintaining a (live) prototypical inheritance link to their other properties. Example:

var A = {
  foo : 'greetings'
};  
var B = Object.beget(A);

alert(B.foo);     // 'greetings'

// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo);     // 'hello'

A.bar = 'world';
alert(B.bar);     // 'world'


// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo);     // 'hello'

B.bar = 'universe';
alert(A.bar);     // 'world'

Also mentioned in Crockford's "Javascript: The Good Parts":

parseInt() is dangerous. If you pass it a string without informing it of the proper base it may return unexpected numbers. For example parseInt('010') returns 8, not 10. Passing a base to parseInt makes it work correctly:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.

Off the top of my head...

Functions

arguments.callee refers to the function that hosts the "arguments" variable, so it can be used to recurse anonymous functions:

var recurse = function() {
  if (condition) arguments.callee(); //calls recurse() again
}

That's useful if you want to do something like this:

//do something to all array items within an array recursively
myArray.forEach(function(item) {
  if (item instanceof Array) item.forEach(arguments.callee)
  else {/*...*/}
})

Objects

An interesting thing about object members: they can have any string as their names:

//these are normal object members
var obj = {
  a : function() {},
  b : function() {}
}
//but we can do this too
var rules = {
  ".layout .widget" : function(element) {},
  "a[href]" : function(element) {}
}
/* 
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
  var elements = document.querySelectorAll(rules[item]);
  for (var e, i = 0; e = elements[i++];) rules[item](e);
}

Strings

String.split can take regular expressions as parameters:

"hello world   with  spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]

String.replace can take a regular expression as a search parameter and a function as a replacement parameter:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"

Functions are first class citizens in JavaScript:

var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };

var sum = function(x,y,z) {
  return x+y+z;
};

alert( passFunAndApply(sum,3,4,5) ); // 12

Functional programming techniques can be used to write elegant javascript.

Particularly, functions can be passed as parameters, e.g. Array.filter() accepts a callback:

[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]

You can also declare a "private" function that only exists within the scope of a specific function:

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}

Assigning default values to variables

You can use the logical or operator || in an assignment expression to provide a default value:

var a = b || c;

The a variable will get the value of c only if b is falsy (if is null, false, undefined, 0, empty string, or NaN), otherwise a will get the value of b.

This is often useful in functions, when you want to give a default value to an argument in case isn't supplied:

function example(arg1) {
  arg1 || (arg1 = 'default value');
}

Example IE fallback in event handlers:

function onClick(e) {
    e || (e = window.event);
}

The following language features have been with us for a long time, all JavaScript implementations support them, but they weren't part of the specification until ECMAScript 5th Edition:

The debugger statement

Described in: § 12.15 The debugger statement

This statement allows you to put breakpoints programmatically in your code just by:

// ...
debugger;
// ...

If a debugger is present or active, it will cause it to break immediately, right on that line.

Otherwise, if the debugger is not present or active this statement has no observable effect.

Multiline String literals

Described in: § 7.8.4 String Literals

var str = "This is a \
really, really \
long line!";

You have to be careful because the character next to the \ must be a line terminator, if you have a space after the \ for example, the code will look exactly the same, but it will raise a SyntaxError.


I'd have to say self-executing functions.

(function() { alert("hi there");})();

Because Javascript doesn't have block scope, you can use a self-executing function if you want to define local variables:

(function() {
  var myvar = 2;
  alert(myvar);
})();

Here, myvar is does not interfere with or pollute the global scope, and disappears when the function terminates.


Functions are objects and therefore can have properties.

fn = function(x) {
   // ...
}

fn.foo = 1;

fn.next = function(y) {
  //
}






hidden-features