views:

1328

answers:

10

Please, share your tips and tricks related to JavaScript coding. The ones which make code more elegant and faster.

See also:

+5  A: 

Use of a library such as jQuery

Richard Ev
+19  A: 

Check if an object is not empty

// JavaScript 1.5 
function isNotEmpty(obj) {
    for (var tmp in obj)
        return true
}

// JavaScript 1.8 
function isNotEmpty(obj) obj.__count__;

Transform the arguments object into an array

function foo() {
    Array.slice(arguments); // is ['aa',11]
}
foo('aa', 11);

JavaScript Minifier / comment remover

var script = new Script('var a; /* this is a variable */ var b; ' +
             '// another variable');
Print( script.toString() );
// prints:
// var a;
// var b;

Singleton pattern

function MySingletonClass() {
    if ( arguments.callee._singletonInstance )
        return arguments.callee._singletonInstance;
    arguments.callee._singletonInstance = this;
    this.Foo = function() {
        // ...
    }
}

var a = new MySingletonClass();
var b = MySingletonClass();
Print( a === b ); // prints: true

Factory method pattern

Complex = new function() {
    function Complex(a, b) {
        // ...
    }
    this.fromCartesian = function(real, mag) {
        return new Complex(real, imag);
    }
    this.fromPolar = function(rho, theta) {
        return new Complex(rho * Math.cos(theta), rho * Math.sin(theta));
}}
var c = Complex.fromPolar(1, Math.pi); // Same as fromCartesian(-1, 0);

Use Functions as an Object

function Counter() {
    if ( !arguments.callee.count ) {
        arguments.callee.count = 0;
    }
    return arguments.callee.count++;
}

Print( Counter() ); // prints: 0
Print( Counter() ); // prints: 1
Print( Counter() ); // prints: 2

Listen a property for changes

function onFooChange( id, oldval, newval ) {
    Print( id + " property changed from " + oldval + " to " + newval );
    return newval;
}

var o = { foo:5 };
o.watch( 'foo', onFooChange );
o.foo = 6;
delete o.foo;
o.foo = 7;
o.unwatch('foo');
o.foo = 8;

// prints: 
// foo property changed from 5 to 6
// foo property changed from undefined to 7
Tarkus
Accessing ‘slice’ directly through the Array object instead of Array.prototype is non-standard and won't work everywhere. Instead use “Array.prototype.slice.call(arraylike, begin[, end])”.
bobince
(However technically even this usage is non-standard as Array methods are not defined to work on non-Array objects, and it would depend on the internal implementation whether it was OK or not. However, since this trick is quite widely used it will probably continue to be OK in practice.)
bobince
Array.prototype.slice.call(arraylike, begin[, end]) seldom works with IE collections (like stylesheet rules)
KooiInc
What's that Singleton pattern in there? What's wrong with object literals?
Ionuț G. Stan
Although not all of them are clear to me why to use them in javascript, some look like really good examples and stuff to think about! thanks ;-)
Sander Versluys
+8  A: 

Trick

Use the "javascript:" protocol

Youc can store js code in a bookmark so in can be used in any Web site you visit. It's called "bookmarklet", and it's a hell of a fun :-)

Some bookmarklets for web dev.

Javascript to enhance IE

If you are like most of the Web designers, you want IE6 (en 5, 4, etc) to die. But there is a way to make him interpret the CSS just like IE7, which is much better.

Use conditional comments to load a JS librairy that will tweak his behavior :

<!--[if lt IE 7]>
<script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE7.js"
        type="text/javascript"></script>
<![endif]-->

Now, you can deal more easily with IE, and not care about the version. IE6 users will have to wait some seconds for JS to load the first visit, that's all. And for those who have IE6 and JS disable, well, they will see a crappy Website but they are masochist anyway ;-)

Iterate over Arrays using "for in"

I think everybody know the usefullness of the "for in" loop :

// creating an object (the short way, to use it like a hashmap)
var diner = {
"fruit":"apple"
"veggetable"="bean"
}

// looping over its properties
for (meal_name in diner ) {
    document.write(meal_name+"<br \n>");
}

Result :

fruit
veggetable

But there is more. Since you can use an object like an associative array, you can process keys and values, just like a foreach loop :

// looping over its properties and values
for (meal_name in diner ) {
    document.write(meal_name+" : "+diner[meal_name]+"<br \n>");
}

Result :

fruit : apple
veggetable : bean

And since Array are objects too, you can iterate other array the exact same way :

var my_array = ['a', 'b', 'c'];
for (index in my_array ) {
    document.write(index+" : "+my_array[index]+"<br \n>");
}

Result :

0 : a
1 : b
3 : c

Remove easily an known element from an array

var arr = ['a', 'b', 'c', 'd'];
var pos = arr.indexOf('c');
pos > -1 && arr.splice( pos, 1 );

Best practice

Always use the second argument parseInt()

parseInt(string, radix)

Indeed, parseInt() converts a string to int, but will try to guess the numeral system if you omit radix, following the rules :

  • If the string begins with "0x", the radix is 16 (hexadecimal)
  • If the string begins with "0", the radix is 8 (octal). This feature is deprecated
  • If the string begins with any other value, the radix is 10 (decimal)

Most of the time, you will use :

var my_int = parseInt(my_string, 10);

Check the property ownership while using "for in"

"for in" iterate other the object properties, but the properties can be ineritated from the object prototype. Since anyone can dynamically alter prototypes, on sensible objects you'd better check if the property is its own before using it :

 for (var name in object) {
      if (object.hasOwnProperty(name)) { 

     }
  }
e-satis
Yes, I learnt that "parseInt" best practice the hard way!
Steve Harrison
Again, great suggestions, this is an awesome question! txn
Sander Versluys
+5  A: 

prototype extension of native objects for utility methods

A couple of my favourites:

String.prototype.reverse = function () 
{
    return this.split('').reverse().join('');
};

Date.prototype.copy = function ()
{
    return new Date(this);
};
annakata
+5  A: 

Remedial work. Use prototype-based monkey-patching to add new standardised features to old and crappy browsers.

// Add JavaScript-1.6 array features if not supported natively
//
if (![].indexOf) {
    Array.prototype.indexOf= function(find) {
        for (var i= 0; i<this.length; i++)
            if (this[i]==find)
                return i;
        return -1;
    };
}
if (![].map) {
    Array.prototype.map= function(fn) {
        var out= [];
        for (var i= 0; i<this.length; i++)
            out.push(fn(this[i]));
        return out;
    };
}
if (![].filter) {
    Array.prototype.filter= function(fn) {
        var out= [];
        for (var i= 0; i<this.length; i++)
            if (fn(this[i]))
                out.push(this[i]);
        return out;
    };
}

// Add ECMAScript-3.1 method binding if not supported natively.
// Note, uses a direct reference, which if assigned to an event
// handler will cause reference loops/memory leaking in IE6.
// (Could add a lookup-map cycle-breaker to improve this in the
// future.)
//
if (!Function.prototype.bind) {
    Function.prototype.bind= function(owner/* {, args} */) {
        var method= this;
        var args= Array_fromSequence(arguments).slice(1);
        return function() {
            var allargs= args.length==0? arguments : arguments.length==0? args : Array_fromSequence(args, arguments);
            return method.apply(owner, allargs);
        };
    };
}

// Compile an Array from one or more Array-like sequences (arguments, NodeList...)
// Used by Function.bind.
//
Array_fromSequence= function(/* args */) {
    var arr= [];
    for (var argi= 0; argi<arguments.length; argi++)
        for (var itemi= 0; itemi<arguments[argi].length; itemi++)
            arr.push(arguments[argi][itemi]);
    return arr;
};

Generally, it's worth being conservative with the prototype patching, because changes you make will affect all scripts on the page, potentially causing bad interactions. Adding arbitrary methods can go wrong when another script tries to use the same member name for another purpose. But fixing up browsers to comply with new standards is generally harmless.

bobince
If you're going to go that far, using a framework and sharing work seeems like a better way to go.
Tchalvak
+3  A: 

Logical operators tricks

var a = 5;
a == 5 && Print( 'a is 5 \n' );
a == 7 || Print( 'a is not 7 \n' );

// prints: 
// a is 5
// a is not 7

Remove an item by value in an Array object

var arr = ['a', 'b', 'c', 'd'];
var pos = arr.indexOf( 'c' );
pos > -1 && arr.splice( pos, 1 );
Print( arr ); // prints: a,b,d

shuffle the Array

var list = [1,2,3,4,5,6,7,8,9];
list = list.sort(function() Math.random() > 0.5 ? 1 : -1);
Print( list );
// prints something like: 4,3,1,2,9,5,6,7,8

Optional named function arguments

function foo({ name:name, project:project}) {
    Print( project );
    Print( name );
}
foo({ name:'web', project:'so' })
foo({ project:'so', name:'web'})

Might not work in FF. See Comments.

Floating to integer

(123.345456).toFixed(); // is: 123
typeof (1.5).toFixed(); // is: string

Change the primitive value an object with valueOf

function foo() {
    this.valueOf = function() {
        return 'this is my value';
}}
var bar = new foo();
Print( bar ); // prints: this is my value

New object override

function foo() {
    return new Array(5,6,7);
}
var bar = new foo();
bar.length // is 3

Nested functions and closures

function CreateAdder( add ) {
    return function( value ) {
        return value + add;
}}
// usage: 
var myAdder5 = CreateAdder( 5 );
var myAdder6 = CreateAdder( 6 );
Print( myAdder5( 2 ) ); // prints 7
Print( myAdder6( 4 ) ); // prints 10

SyntaxError

Raised when a syntax error occurs while parsing code in eval()

try {
    eval('1 + * 5'); // will rise a SyntaxError exception
} catch( ex ) {
    Print( ex.constructor == SyntaxError ); // Prints true
}

ReferenceError

Raised when de-referencing an invalid reference.

try {
    fooBar(); // will rise a ReferenceError exception
} catch( ex ) {
    Print( ex.constructor == ReferenceError ); // Prints true
}
Koistya Navin
the latest trick work only in FF.a similar cross-browser trick can be:function foo(x){ with(x){ alert( project ); alert( naame ); }}foo({ naame:'web', project:'so' })foo({ project:'so', naame:'web'})but with statement must be used carefully
kentaromiura
@kentaromiura, good point. Thanks.
Koistya Navin
+2  A: 

Solid type-checking function:

function type(v) {
    if (v === null) { return 'null'; }
    if (typeof v === 'undefined') { return 'undefined'; }
    return Object.prototype.toString.call(v).match(/\s(.+?)\]/)[1].
           toLowerCase();
}
// Usage:
type({}); // 'object'
type([]); // 'array'
type(333); // 'number'

Find the maximum or minimum value in an array of numbers:

var arrNumbers = [1,4,7,3,5,9,3,2];
Math.max.apply(null, arrNumbers); // 9
Math.min.apply(null, arrNumbers); // 1
J-P
+3  A: 

I often see people that doesn't have any clue of how javascript really works. So I wish to point out this document: javascript prototypal inheritance

Sorry, too many time I see people call new without actually fully understanding what they are really doing in javascript.

Update: Douglas Crockford on prototypal inheritance

kentaromiura
+3  A: 

Test if an image has loaded:

function hasLoaded ( img ) {
  return img.complete && img.naturalWidth !== 0;
}

Dec to hex / hex to dec:

Amazingly people still try to do this computationally:

// number to hexadecimal
'0x' + Number( 15 ).toString( 16 );

// hexadecimal to number
parseInt( String( '8f3a5' ), 16 );

String repeat:

Perhaps not be the fastest way of doing this, but a nifty approach I picked up somewhere:

String.prototype.repeat = function ( n ) {
  return new Array( (n || 1) + 1 ).join( this );
}

Get number signature with boolean math operations:

Not very useful, but an interesting demonstration of booleans vs. the +/- operators. I've used it in GPS coordinates formating:

function signature ( n ) {
  return ['-','','+'][ -(0 > n) +(0 < n) +1 ];
}

Quick & easy namespaces:

String.prototype.namespace = function ( obj ) {
  var c, b = this.split('.'), 
      p = window;
  while (c = b.shift()) {
    p = (p[c] = (p[c] || {}));
  }
  if (typeof obj === 'object') {
    for (var k in obj) {
      p[k] = obj[k];
    }
  }
  return p;
};

Doesn't necessarily have to sit on the string prototype, I just think it's neat for the purposes of this example:

"com.stackoverflow.somemodule".namespace({
  init: function () {
    // some code
  }
})
Borgar
+2  A: 

String format like function:

String.prototype.format = function(){
    var pattern = /\{\d+\}/g;
    var args = arguments;
    return this.replace(pattern, function(capture){ return args[capture.match(/\d+/)]; });
}

//Example

var a = 'hello, {0}';
var b = a.format('world!');
AlexanderN