wiki.chadlindstrom.ca

Web Development Best Practices and Design Patterns




Javascript Tutorials : Language Overview

Contents

Reserved Words

  • breaklooping
  • elselogic
  • newobjects
  • varvariables/attributes
  • caselogic
  • finallyexception handling
  • returnfunctions
  • voidfunctions
  • catchexception handling
  • forlooping
  • switchlogic
  • whilelooping
  • continuelooping
  • functionfunctions
  • thisobjects
  • with???
  • defaultlogic
  • iflogic
  • throwexception handling
  • deletecan't recall the context here
  • inseen in iterations/loops
  • tryexception handling
  • dolooping
  • instanceofobjects
  • typeofobjects

Types

  • Number
  • String
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Null
  • Undefined

Logic

Loops

For

for var i = 0; i < a.length; i++ ){
  // do something with a[i]
}

This can be improved by caching a.length at start:

for var i = 0; j = a.length; i < j; i++ ){
  // do something with a[i]
}

Objects

Basic Object Creation

var obj = new Object();

Is equivalent to:

var obj = {}; //object literal syntax

Property Access

obj.name = "Chad";
var name = obj.name;

Is equivalent to:

obj["name"] = "Chad";
var name = obj["name"];

Object literal syntax (AKA JSON)

var obj = {
   name: "Carrot",
   "for": "Max",
   details: {
      color: "orange",
      size: 12
   }
}

with reference looking something like:

obj.details.color

or as we've seen above, the equivalent:

obj["details"]["size"]

Iterating over keys and attributes

var obj = { 'name': "Chad", 'age': 26 };
for( var attr in obj){
   print( attr + ' = ' + obj[attr]);
}

Arrays

The safest way to append to an array is demonstrated below:

var animals = ["dog", "cat"];
animals[animals.length] = "hen";

Array Methods

  • animals.toString()
  • animals.toLocaleString()
  • animals.concat(item,..)
  • animals.join(sep)
  • animals.pop()
  • animals.push(item,..)
  • animals.reverse()
  • animals.shift()
  • animals.splice(start,end)
  • animals.soft(compFunction)
  • animals.splice(start,delcount,[item]..)
  • animals.unshift([item]..)


Functions

function add(x,y){
  var total = x + y;
  return total;
}

However, the better implementation av the above function would use the arguments object.

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

What if our numbers we're summing are already contained in an array? Do we have to rewrite our function? No.

add.apply(null, [2,3,4,5,6]);

Functions are objects with methods too! The apply method takes an array of arguments as the second argument.

The following is a semantically equivalent to our earlier add() function:

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

The block scope trick

Block scope is a feature of C where every set of braces defines a new scope. It can be simulated in JavaScript.

var a = 1;
var b = 2;
(function(){
   var b = 3;
   a += b;
})();

The results:

> a
4
> b
2

Recursive Functions

function countChars(elm) {
  if(elm.nodeType == 3){ //TEXT_NODE
     return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++){
     count += countChars(child);
  }
  return count;
}

Using arguments.callee now:

var charsInBody = (function(elm) {
  if(elm.nodeType == 3){ //TEXT_NODE
     return elm.nodeValue.length;
  }
  var count = 0;
  for (var i = 0, child; child = elm.childNodes[i]; i++){
     count += argumente.callee(child);
  }
  return count;
})(document.body);

This function remembers how many times is has been called:

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


Sampe usage:

> counter();
0
> counter();
1

Constructors

function Person(first,last){
  return {
     first: first,
     last: last,
     fullName: function(){
        return this.first + ' ' + this.last;
     }
  }
}

Now let's use it:

var p1 = new Person("Chad","Lindstrom");
p1.fullName();

As you can expect, this should output Chad Lindstrom.

JavaScript is an ambidextrous language, thus, allowing you to do things more than one way. The above constructor can also be done using the following:

function Person(first,last){
  this.first = first;
  this.last = last;
  this.fullName = function(){
     return this.first + " " + this.last;
  }
}

Sharing Methods

function Person(first,last){
  this.first = first;
  this.last = last;
  this.fullName = personFullName;
}
function personFullName(){
  return this.first + " " + this.last;
}

Or of course, the infamous 'prototype'

function Person(first,last){
  this.first = first;
  this.last = last;
}
Person.prototype.fullName = function(){
  return this.first + " " + this.last;
}

instanceof

> var a = [1,2,3];
> a instanceof Array
true
> a instanceof Object
true 
> a instanceof String
false

Inheritance

> s = new Person("Chad","Lindstrom");
> s.firstNameCaps();
TypeError on line 1: s.firstNameCaps() is not a function
> Person.prototype.firstNameCaps = function():
    return this.first.toUpperCase();
 }
> s.firstNameCaps();
CHAD

Now for the inheritance:

> var s = "Chad";
> s.reversed();
TypeError on line 1: s.reveresed() is not a function
> String.prototype.reversed = function():
    var r = ;
    for(var i = this.length - 1; i >= 0; i--){
       r += this[i];
    }
    return r;
 }
> s.reversed();
dahC
> "This can now be reversed".reversed()
desrever eb won nac sihT

Example

function Geek(){
  Person.apply(this,arguments);
  this.geekLevel = 5;
} 
Geek.prototype = new Person();
Geek.prototype.setLevel = function(lvl){
  this.geekLevel = lvl;
}
Geek.prototype.getLevel = function(){
  return this.geekLevel;
}


new Person()?

  • We're using an instance of the Person object as out prototype
  • We have todo this, because we need to be able to modify out prototype to add new methods that are only available to Geek instances
  • This is counter-intuitive and, well, a bit dumb

Inheritance Solutions

  • design classes with inheritance in mind - don't do anything important in the constructur (that might break if you create an empty instance for use as a prototype)
  • use one of the many workarounds
  • Prototype's Class.create()
  • The stuff you get by searching for "javascript inheritance" on the Web

Advanced Functions

  • a function is just another object
  • you can store it in a variable
  • you can pass it to another function
  • you can return if from a function

Some Examples

ArrayMap

function arrayMap(array,func){
  var result = [];
  for(var i = 0; i < array.length; i++){
     result[i] = func(array[i]);
  }
  return result;
}
function calcVat(price){
  return price * 1.175;
}
var prices = [10,8,9.50];
pricesVat = arrayMap(prices,calcVat);

Sales Tax Factory

function salesTaxFactory(percent){
  function func(price) {
     return price + (percent/100) * price;
  }
  return func;
}
calcVat = salesTaxFactory(17.5);
calc4 = salesTaxFactory(4);

pricesVat = arrayMap(prices,calcVat);
prices4 = arrayMap(prices,calc4);


Operation Factory

function makeOp(op,y){
   switch(op){
       case '+':
           return function(x){ return x + y };
       case '-':
           return function(x){ return x - y };
       case '/':
           return function(x){ return x / y };
       case '*':
           return function(x){ return x * y };
       default:
           return function(x){ return x };
   }
}
var third = makeOp('/',3);
var dbl = makeOp('*',2);
print(third(24));
print(dbl(5));

Closures

  • The code above is an example of closures in action.
  • A closure is a function that has captures the scope in which it was defined
  • Actually, functions in JavaScript have a scope chain (similar to the prototype chain)

Singleton

  • JavaScript shares a single global namespace
  • It's easy to clobber other people's functions and variables and easy for others to clobber yours
  • The less code affecting the global namespace the better
var chad = (function() {
  var myVar = 5; //private
  function init(x){
     //can access myVar and doPrivate
  }
  function doPrivate(x){
     // ... invisible to the outside world
  }
  function doSomething(x,y){
     // can access myVar and doPrivate
  }
  return {
     'init':init,
     'doSomething':doSomething
  }
})();

chad.init(x);

Singleton Benefits

  • lets you wrap up a complex application with doznds of functions up in a single, private namespace - a closure.
  • this lets you expose only the functions that make up your applications external interface

Memory Leaks

  • to understand memory leaks, you need to understand a bit about garbage collection
  • Stuff gets freed up automatically when it's no londer in use (both JavaScript objects and jost objects, such as DOM nodes)
  • IE uses different garbage collectors for JS and for the DOM, and can't handle circular references between them

This leaks:

function leak() {
  var div = document.getElementById('d');
  div.obj = {
     'leak': div
  }
}

This also leaks:

function sneakyLeak() {
  var div = document.getElementById('d');
  div.onclick = function(){
     alert("hi!");
  }
}

The solution:

function sneakyLeak() {
  var div = document.getElementById('d');
  div.onclick = function(){
     alert("hi!");
  }
  div = null;
}

Performance

De-reference complex lookups

var s = document.getElementById('d').style;
s.width = '100%';
s.color = 'red';
// ...
s.display = 'block';

... especially inside loops

var lookup = foo.bar.bav;
for(var i = 0; i < 1000; i++){
   lookup.counter += someCalc();
}

Suggested Libraries

References

Retrieved from "http://wiki.chadlindstrom.ca/index.php/Javascript_Tutorials_:_Language_Overview"

This page has been accessed 2,309 times. This page was last modified 18:47, 9 March 2007.


This page has been accessed 2,309 times. This page was last modified 18:47, 9 March 2007.