orig.Link
(Part 1: Language)
Values, Types, and Operators
Program Structure
Looping a triangle
var total = 8 , counter = 1 ;
var str = '' ;
for ( var a = 1 ; a <= total ; a ++ ) {
for ( var i = 1 ; i <= counter ; i ++ ) {
str += '#' ;
}
str += '\n' ;
counter ++ ;
}
console . log ( str );
> answer
for ( var line = "#" ; line . length < 8 ; line += "#" )
console . log ( line );
FizzBuzz
for ( var i = 1 ; i <= 100 ; i ++ ) {
if ( i % 3 === 0 && i % 5 === 0 ) {
console . log ( 'FizzBuzz' );
} else if ( i % 3 === 0 ) {
console . log ( 'Fizz' );
} else if ( i % 5 === 0 ) {
console . log ( 'Buzz' );
} else {
console . log ( i );
}
}
> answer
for ( var n = 1 ; n <= 100 ; n ++ ) {
var output = "" ;
if ( n % 3 == 0 )
output += "Fizz" ;
if ( n % 5 == 0 )
output += "Buzz" ;
console . log ( output || n );
}
Chess board
var size = 8 ;
var str = '' ;
for ( var i = 0 ; i < size * size ; i ++ ) {
if ( i % size === 0 ) str += '\n' ;
if (( i + Math . floor ( i / size )) % 2 === 1 ) str += '#' ;
else str += 'O' ;
};
console . log ( str );
> answer
var size = 8 ;
var board = "" ;
for ( var y = 0 ; y < size ; y ++ ) {
for ( var x = 0 ; x < size ; x ++ ) {
if (( x + y ) % 2 == 0 )
board += " " ;
else
board += "#" ;
}
board += "\n" ;
}
console . log ( board );
Functions
Closure
function multiplier ( factor ) {
return function ( number ) {
return number * factor ;
};
}
var twice = multiplier ( 2 );
console . log ( twice ( 5 ));
// 10
Recursion
function power ( base , exponent ) {
if ( exponent == 0 )
return 1 ;
else
return base * power ( base , exponent - 1 );
}
console . log ( power ( 2 , 3 ));
// 8
! Running through a simple loop is a lot cheaper than calling a function multiple times.
Consider this puzzle: by starting from the number 1 and repeatedly either adding 5 or multiplying by 3, an infinite amount of new numbers can be produced. How would you write a function that, given a number, tries to find a sequence of such additions and multiplications that produce that number? For example, the number 13 could be reached by first multiplying by 3 and then adding 5 twice, whereas the number 15 cannot be reached at all.
function findSolution ( target ) {
function find ( start , history ) {
if ( start == target )
return history ;
else if ( start > target )
return null ;
else
return find ( start + 5 , "(" + history + " + 5)" ) ||
find ( start * 3 , "(" + history + " * 3)" );
}
return find ( 1 , "1" );
}
console . log ( findSolution ( 24 ));
// (((1 * 3) + 5) * 3)
console . log ( findSolution ( 13 ));
console . log ( findSolution ( 15 ));
Note that this program doesn’t necessarily find the shortest sequence of operations. It is satisfied when it finds any sequence at all.
Minimum
function min ( a , b ) {
return a > b ? b : a ;
}
Recursion
var isEven = function ( num ) {
if ( num < 0 ) num = - num ;
if ( num == 0 ) return true ;
if ( num == 1 ) return false ;
return isEven ( num - 2 );
};
Bean counting
function countBs ( str ) {
var count = 0 ;
for ( var i = 0 ; i < str . length ; i ++ ) {
if ( str . charAt ( i ) == 'B' ) count ++ ;
};
return count ;
}
function countChar ( str , chara ) {
var count = 0 ;
for ( var i = 0 ; i < str . length ; i ++ ) {
if ( str . charAt ( i ) == chara ) count ++ ;
};
return count ;
}
Data Structures: Objects and Arrays
Properties
The two most common ways to access properties in JavaScript are with a dot and with square brackets. Both value.x and value[x] access a property on value—but not necessarily the same property. The difference is in how x is interpreted. When using a dot, the part after the dot must be a valid variable name, and it directly names the property . When using square brackets, the expression between the brackets is evaluated to get the property name . Whereas value.x fetches the property of value named “x”, value[x] tries to evaluate the expression x and uses the result as the property name.
Methods
Some methods that array objects have:
var mack = [];
mack . push ( 'Mack' );
mack . push ( 'the' , 'Knife' );
console . log ( mack );
// ['Mack', 'the', 'Knife']
console . log ( mack . join ( ' ' ));
// Mack the Knife
console . log ( mack . pop ());
// Knife
console . log ( mack );
// ['Mack', 'the']
var todoList = [];
function rememberTo ( task ) {
todoList . push ( task );
}
function whatIsNext () {
return todoList . shift ();
}
function urgentlyRememberTo ( task ) {
todoList . unshift ( task );
}
function remove ( array , index ) {
return array . slice ( 0 , index )
. concat ( array . slice ( index + 1 ));
}
console . log ( remove ([ "a" , "b" , "c" , "d" , "e" ], 2 ));
// ["a", "b", "d", "e"]
name: expression
property: value
key: value
var anObject = { left : 1 , right : 2 };
console . log ( anObject . left );
delete anObject . left ;
console . log ( anObject . left );
// undefined
console . log ( 'left' in anObject );
// false
console . log ( 'right' in anObject );
// true
var object1 = { value : 10 };
var object2 = object1 ;
var object3 = { value : 10 };
console . log ( object1 == object2 );
// true
console . log ( object1 == object3 );
// false
object1 . value = 15 ;
console . log ( object2 . value );
// 15
The binary in
operator, when applied to a string and an object, returns a Boolean value that indicates whether that object has that property.
function addEntry ( squirrel ) {
var entry = { events : [], squirrel : squirrel };
for ( var i = 1 ; i < arguments . length ; i ++ )
entry . events . push ( arguments [ i ]);
journal . push ( entry );
}
addEntry ( true , "work" , "touched tree" , "pizza" ,
"running" , "television" );
function hasEvent ( event , entry ) {
return entry . events . indexOf ( event ) != - 1 ;
}
function tableFor ( event , journal ) {
var table = [ 0 , 0 , 0 , 0 ];
for ( var i = 0 ; i < journal . length ; i ++ ) {
var entry = journal [ i ], index = 0 ;
if ( hasEvent ( event , entry )) index += 1 ;
if ( entry . squirrel ) index += 2 ;
table [ index ] += 1 ;
}
return table ;
}
console . log ( tableFor ( 'pizza' , JOURNAL ));
// [76, 9, 4, 1]
// ϕ = (n11n00 - n10n01)/√(n1•n0•n•1n•0)
function phi ( table ) {
return ( table [ 3 ] * table [ 0 ] - table [ 2 ] * table [ 1 ]) /
Math . sqrt (( table [ 2 ] + table [ 3 ]) *
( table [ 0 ] + table [ 1 ]) *
( table [ 1 ] + table [ 3 ]) *
( table [ 0 ] + table [ 2 ]));
}
console . log ( phi ([ 76 , 9 , 4 , 1 ]));
// 0.068599434
// var map = {};
// function storePhi(event, phi) {
// map[event] = phi;
// }
// storePhi('pizza', 0.069);
// storePhi('touched tree', -0.081);
// console.log('pizza' in map);
// // true
// console.log(map['touched tree']);
// // -0.081
// for (var event in map) {
// console.log('The correlation for ' + event + ' is ' + map[event]);
// }
// // The correlation for 'pizza' is 0.069
// // The correlation for 'touched tree' is -0.081
function gatherCorrelations ( journal ) {
var phis = {};
for ( var entry = 0 ; entry < journal . length ; entry ++ ) {
var events = journal [ entry ]. events ;
for ( var i = 0 ; i < events . length ; i ++ ) {
var event = events [ i ];
if ( ! ( event in phis ))
phis [ event ] = phi ( tableFor ( event , journal ));
}
}
return phis ;
}
for ( var event in correlations ) {
var correlation = correlations [ event ];
if ( correlation > 0.1 || correlation < - 0.1 )
console . log ( event + ": " + correlation );
}
// weekend: 0.1371988681
// brushed teeth: -0.3805211953
// peanuts: 0.5902679812
for ( var i = 0 ; i < JOURNAL . length ; i ++ ) {
var entry = JOURNAL [ i ];
if ( hasEvent ( "peanuts" , entry ) &&
! hasEvent ( "brushed teeth" , entry ))
entry . events . push ( "peanut teeth" );
}
console . log ( phi ( tableFor ( "peanut teeth" , JOURNAL )));
// 1
function randomPointOnCircle ( radius ) {
var angle = Math . random () * 2 * Math . PI ;
return {
x : radius * Math . cos ( angle ),
y : radius * Math . sin ( angle )
};
}
console . log ( randomPointOnCircle ( 2 ));
The sum of a range
function range ( start , end , step ) {
if ( step == undefined ) {
if ( start < end ) step = 1 ;
else step = - 1 ;
}
var arr = [];
for ( var i = start ; ( i - start ) * ( i - end ) <= 0 ; i += step ) {
arr . push ( i );
}
return arr ;
}
function sum ( numbers ) {
var sum = 0 ;
for ( var i in numbers ) {
sum += numbers [ i ];
}
return sum ;
}
Reversing an array
function reverseArray ( arr ) {
var a = [];
for ( var i = 0 ; i < arr . length ; i ++ ) {
a . unshift ( arr [ i ]);
}
return a ;
}
// alternative
function reverseArray ( arr ) {
var a = [];
for ( var i = arr . length - 1 ; i >= 0 ; i -- ) {
a . push ( arr [ i ]);
}
return a ;
}
function reverseArrayInPlace ( arr ) {
for ( var i = 0 , j = arr . length - 1 ; i < j ; i ++ , j -- ) {
var t = arr [ i ];
arr [ i ] = arr [ j ];
arr [ j ] = t ;
}
}
A list
function arrayToList ( arr ) {
if ( arr . length == 0 ) return null ;
var a = arr . slice ( 0 );
return {
value : a . shift (),
rest : arrayToList ( a )
};
}
// alternative
function arrayToList ( arr ) {
var l = null ;
for ( var i = arr . length - 1 ; i >= 0 ; i -- ) {
l = {
value : arr [ i ],
rest : l
};
}
return l ;
}
function listToArray ( list ) {
var a = [];
for ( var node = list ; node ; node = node . rest ) {
a . push ( node . value );
}
return a ;
}
function prepend ( ele , list ) {
return {
value : ele ,
rest : list
};
}
function nth ( list , num ) {
for ( var node = list , i = 0 ; node ; node = node . rest , i ++ ) {
if ( i == num ) return node . value ;
}
}
Deep comparison
function deepEqual ( one , two ) {
if ( one != null && two != null
&& typeof one == "object"
&& typeof two == "object" ) {
if ( Object . keys ( one ). length != Object . keys ( two ). length )
return false ;
for ( var i in one ) {
if ( ! deepEqual ( one [ i ], two [ i ])) return false ;
}
return true ;
} else {
return one === two ;
}
}
Higher-order Functions
Higher-order functions that somehow apply a function to the elements of an array are widely used in JavaScript. The forEach
method is the most primitive such function.
function forEach ( array , action ) {
for ( var i = 0 ; i < array . length ; i ++ )
action ( array [ i ]);
}
var numbers = [ 1 , 2 , 3 , 4 , 5 ], sum = 0 ;
forEach ( numbers , function ( number ) {
sum += number ;
});
console . log ( sum );
// 15
To illustrate how helpful this is, let’s look back at a function from the previous chapter. It contains two array-traversing loops. Working with forEach makes it slightly shorter and quite a bit cleaner.
In fact, we don’t need to write forEach
ourselves. It is available as a standard method on arrays. Since the array is already provided as the thing the method acts on, forEach
takes only one required argument: the function to be executed for each element.
function gatherCorrelations ( journal ) {
var phis = {};
journal . forEach ( function ( entry ) {
entry . events . forEach ( function ( event ) {
if ( ! ( event in phis ))
phis [ event ] = phi ( tableFor ( event , journal ));
});
});
return phis ;
}
function unless ( test , then ) {
if ( ! test ) then ();
}
function repeat ( times , body ) {
for ( var i = 0 ; i < times ; i ++ ) body ( i );
}
repeat ( 3 , function ( n ) {
unless ( n % 2 , function () {
console . log ( n , "is even" );
});
});
// 0 is even
// 2 is even
JSON
JavaScript gives us functions, JSON.stringify
and JSON.parse
, that convert data from and to this format. The first takes a JavaScript value and returns a JSON-encoded string. The second takes such a string and converts it to the value it encodes.
Filtering an array
function filter ( array , test ) {
var passed = [];
for ( var i = 0 ; i < array . length ; i ++ ) {
if ( test ( array [ i ]))
passed . push ( array [ i ]);
}
return passed ;
}
console . log ( filter ( ancestry , function ( person ) {
return person . born > 1900 && person . born < 1925 ;
}));
// [{name: "Philibert Haverbeke", …}, …]
Flattening
arrays . reduce ( function ( a , b ) {
return a . concat ( b );
}, []);
Mother-child age difference
function average ( array ) {
function plus ( a , b ) { return a + b ; }
return array . reduce ( plus ) / array . length ;
}
var byName = {};
ancestry . forEach ( function ( person ) {
byName [ person . name ] = person ;
});
function hasKnownMother ( p ) {
if ( byName [ p . mother ]) return true ;
}
function ageDifference ( p ) {
return ( p . born - byName [ p . mother ]. born );
}
console . log ( average ( ancestry . filter ( hasKnownMother ). map ( ageDifference )));
// alternative
function ageDifference ( p ) {
if ( byName [ p . mother ])
return ( p . born - byName [ p . mother ]. born );
else
return null ;
}
function filterNull ( n ) {
if ( n != null ) return true ;
}
console . log ( average ( ancestry . map ( ageDifference ). filter ( filterNull )));
Historical life expectancy
function average ( array ) {
function plus ( a , b ) { return a + b ; }
return array . reduce ( plus ) / array . length ;
}
var le = {};
ancestry . forEach ( function ( person ) {
var age = person . died - person . born ;
var century = Math . ceil ( person . died / 100 );
if ( ! le [ century ]) le [ century ] = [];
le [ century ]. push ( age );
});
for ( var h in le ) {
console . log ( h + ':' , average ( le [ h ]));
}
//alternative (X)
function groupBy ( arra , func ) {
var obj = {};
arra . forEach ( func . bind ( null , obj ));
return obj ;
}
function groupByCentury ( g , p ) {
var century = Math . ceil ( p . died / 100 );
var age = p . died - p . born ;
if ( g [ century ]) {
g [ century ]. push ( age );
} else {
g [ century ] = [];
g [ century ]. push ( age );
}
}
// alternative
function groupBy ( arra , func ) {
var obj = {};
arra . forEach ( function ( a ) {
var key = func ( a );
if ( ! obj [ key ]) obj [ key ] = [];
obj [ key ]. push ( a );
});
return obj ;
}
function century ( p ) {
return Math . ceil ( p . died / 100 );
}
var le = groupBy ( ancestry , century );
for ( var h in le ) {
console . log ( h + ':' , average ( le [ h ]. map ( function ( p ) {
return p . died - p . born ;
})));
}
Every and then some
function every ( arra , test ) {
for ( var i = 0 ; i < arra . length ; i ++ ) {
if ( ! test ( arra [ i ]))
return false ;
}
return true ;
}
function some ( arra , test ) {
for ( var i = 0 ; i < arra . length ; i ++ ) {
if ( test ( arra [ i ]))
return true ;
}
return false ;
}
// alternative (X)
function every ( arra , test ) {
return arra . reduce ( function ( a , b ) {
return a && test ( b );
}, true );
}
function some ( arra , test ) {
return arra . reduce ( function ( a , b ) {
return a || test ( b );
}, false );
}
The Secret Life of Objects
There is a method similar to apply, called call.
var fatRabbit = { type : "fat" , speak : speak };
speak . apply ( fatRabbit , [ "Burp!" ]);
// The fat rabbit says 'Burp!'
speak . call ({ type : "old" }, "Oh my." );
// The old rabbit says 'Oh my.'
Project: Electronic Life
Bugs and Error Handling
Regular Expressions
Modules
Project: A Programming Language
(Part 2: Browser)
JavaScript and the Browser
The Document Object Model
Handling Events
Project: A Platform Game
Drawing on Canvas
HTTP
Forms and Form Fields
Project: A Paint Program
(Part 3: Node)
Node.js
Project: Skill-Sharing Website
Other pages