diff --git a/README.md b/README.md
index f71fcfcc..6d32de03 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,7 @@
-Update
+JS Workshop Koans
======
-> If you like JavaScript Koans you might enjoy my newer, more user friendly, project [Didacto: JavaScript](http://javascript.didacto.net/)
-
-JavaScript Koans is an interactive learning environment that uses failing tests to introduce students to aspects of JavaScript in a logical sequence.
-
-The inspiration for this project comes from the Edgecase Ruby Koans and the book 'Javascript: The Good Parts'.
+This is a fork of [JavaScript Koans](https://github.com/liammclennan/JavaScript-Koans) with some modified examples.
Open the file jskoans.htm in your favourite browser and make the tests pass.
-The koans use the [Qunit](http://qunitjs.com/) test syntax and test runner.
-
-Get started with Ryan Anklam's [Learn JavaScript completely On the Cloud With the JavaScript Koans and Cloud9 IDE](http://blog.bittersweetryan.com/2011/08/learn-some-javascript-completely-on.html)
diff --git a/jskoans.htm b/jskoans.htm
index a6bc4c00..d96c0f14 100644
--- a/jskoans.htm
+++ b/jskoans.htm
@@ -5,35 +5,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+
+
+
+
JavaScript Koans
-
To begin, find the file 'topics/about_asserts.js', and complete the tests.
+
To begin, find the file 'topics/about_functions.js', and complete the tests.
test markup, will be hidden
diff --git a/support/helper.js b/support/helper.js
new file mode 100644
index 00000000..428d5ae8
--- /dev/null
+++ b/support/helper.js
@@ -0,0 +1,11 @@
+window.innerVariable = undefined;
+window.a = undefined;
+window.c = undefined;
+window.foo = undefined;
+window.bar = undefined;
+window.something = undefined;
+window.another = undefined;
+window.result = undefined;
+window.defined = function (a) {
+ return typeof(a) !== 'undefined';
+}
\ No newline at end of file
diff --git a/topics/about_arrays.js b/topics/about_arrays.js
deleted file mode 100644
index 3d4cd41f..00000000
--- a/topics/about_arrays.js
+++ /dev/null
@@ -1,43 +0,0 @@
-module("About Arrays (topics/about_arrays.js)");
-
-test("array literal syntax and indexing", function() {
- var favouriteThings = ["cellar door", 42, true]; // note that array elements do not have to be of the same type
- equal(__, favouriteThings[0], 'what is in the first position of the array?');
- equal(__, favouriteThings[1], 'what is in the second position of the array?');
- equal(__, favouriteThings[2], 'what is in the third position of the array?');
-});
-
-test("array type", function() {
- equal(__, typeof([]), 'what is the type of an array?');
-});
-
-test("length", function() {
- var collection = ['a','b','c'];
- equal(__, collection.length, 'what is the length of the collection array?');
-});
-
-test("splice", function() {
- var daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
- var workingWeek = daysOfWeek.splice(__, __);
- ok(workingWeek.equalTo([__]), 'what is the value of workingWeek?');
- ok(daysOfWeek.equalTo([__]), 'what is the value of daysOfWeek?');
-});
-
-test("stack methods", function() {
- var stack = [];
- stack.push("first");
- stack.push("second");
-
- equal(__, stack.pop(), 'what will be the first value popped off the stack?');
- equal(__, stack.pop(), 'what will be the second value popped off the stack?');
-});
-
-test("queue methods", function() {
- var queue = [];
- queue.push("first");
- queue.push("second");
- queue.unshift("third");
-
- equal(__, queue.shift(), 'what will be shifted out first?');
- equal(__, queue.shift(), 'what will be shifted out second?');
-});
diff --git a/topics/about_asserts.js b/topics/about_asserts.js
deleted file mode 100644
index baf7fc75..00000000
--- a/topics/about_asserts.js
+++ /dev/null
@@ -1,14 +0,0 @@
-
-module("About Asserts (topics/about_asserts.js)");
-
-test("ok", function() {
- ok(__ === true, 'what will satisfy the ok assertion?');
-});
-
-test("not ok", function() {
- ok(__ === false, 'what is a false value?');
-});
-
-test("equal", function() {
- equal(__, 1 + 1, 'what will satisfy the equal assertion?');
-});
diff --git a/topics/about_assignment.js b/topics/about_assignment.js
deleted file mode 100644
index 4532861e..00000000
--- a/topics/about_assignment.js
+++ /dev/null
@@ -1,12 +0,0 @@
-
-module("About Assignment (topics/about_assignment.js)");
-
-test("local variables", function() {
- var temp = __;
- equal(temp, 1, "Assign a value to the variable temp");
-});
-
-test("global variables", function() {
- temp = 1; // Not using var is an example. Always use var in practise.
- equal(window.__, temp, 'global variables are assigned to the window object');
-});
diff --git a/topics/about_closure.js b/topics/about_closure.js
new file mode 100644
index 00000000..73c4eb75
--- /dev/null
+++ b/topics/about_closure.js
@@ -0,0 +1,112 @@
+/* jshint eqeqeq: false, unused: false, eqnull: true, undef: false, latedef: false */
+/* jshint -W083 */
+module("About Closure (topics/about_closure.js)");
+
+test("simple closure", function() {
+ function foo () {
+ var result = "a";
+ function changeResult() {
+ return result;
+ }
+
+ ok(__ === result, 'result is available here');
+
+ return changeResult;
+ }
+
+ var changeResult = foo();
+
+ ok(__ === defined(result), 'its not in this scope');
+
+ var closuredResult = changeResult();
+
+ ok(__ === closuredResult, 'but its not gone because of the closure');
+});
+
+test("module pattern", function() {
+ var foo = (function CoolModule() {
+ var something = "cool";
+ var another = [1, 2, 3];
+
+ function doSomething() {
+ return something;
+ }
+
+ function doAnother() {
+ return another.join( " ! " );
+ }
+
+ return {
+ doSomething: doSomething,
+ doAnother: doAnother
+ };
+ })();
+
+ ok(__ === defined(something), 'is something available in this scope?');
+ ok(__ === defined(another), 'is something available in this scope?');
+ ok(__ === foo.doSomething(), 'what is the value returned by doSomething?');
+ ok(__ === foo.doAnother(), 'what is the value returned by doAnother?');
+});
+
+asyncTest("closure with async functions", function(assert) {
+ var sum = 0;
+ for (var i=1; i<=5; i++) {
+ setTimeout( function timer1(){
+ sum += i;
+ }, i*5 );
+ }
+
+ setTimeout( function timer2(){
+ ok(__ === sum, 'what is the value of sum?');
+ start();
+ }, 26 );
+});
+
+asyncTest("closure at for loop with explicit assignment", function(assert) {
+ var sum = 0;
+ for (var i=1; i<=5; i++) {
+ var j = i;
+ setTimeout( function timer1(){
+ sum += j;
+ }, i*5 );
+ }
+
+ setTimeout( function timer2(){
+ ok(__ === sum, 'what is the value of sum?');
+ start();
+ }, 26 );
+});
+
+asyncTest("IIFE",function(){
+ var sum = 0;
+ for (var i=1; i<=5; i++) {
+ (function(){
+ var j = i;
+ setTimeout( function timer1(){
+ sum += j;
+ }, j*5 );
+ })();
+ }
+
+ setTimeout( function timer2(){
+ ok(__ === sum, 'what is the value of sum?');
+ start();
+ }, 26 );
+});
+
+asyncTest("passing values on IIFE",function(){
+ var sum = 0;
+ for (var i=1; i<=5; i++) {
+ (function(j){
+ setTimeout( function timer1(){
+ sum += j;
+ }, j*5 );
+ })( i );
+ }
+
+ setTimeout( function timer2(){
+ ok(__ === sum, 'what is the value of sum?');
+ start();
+ }, 26 );
+});
+
diff --git a/topics/about_control_structures.js b/topics/about_control_structures.js
deleted file mode 100644
index 767d266c..00000000
--- a/topics/about_control_structures.js
+++ /dev/null
@@ -1,73 +0,0 @@
-module("About Control Structures (topics/about_control_structures.js)");
-
-test("if", function() {
- var isPositive = false;
- if (2 > 0) {
- isPositive = true;
- }
- equal(__, isPositive, 'what is the value of isPositive?');
-});
-
-test("for", function() {
- var counter = 10;
- for (var i = 1; i <= 3; i++) {
- counter = counter + i;
- }
- equal(__, counter, 'what is the value of counter?');
-});
-
-test("for in", function() {
- // this syntax will be explained in about objects
- var person = {
- name: "Amory Blaine",
- age: 102
- };
- var result = "";
- // for in enumerates the property names of an object
- for (var property_name in person) {
- result = result + property_name;
- };
- equal(__, result, 'what is the value of result?');
-});
-
-test("ternary operator", function() {
- var fruit = true ? "apple" : "orange";
- equal(__, fruit, 'what is the value of fruit?');
-
- fruit = false ? "apple" : "orange";
- equal(__, fruit, 'now what is the value of fruit?');
-});
-
-test("switch", function() {
- var result = 0;
- switch (2) {
- case 1:
- result = 1;
- break;
- case 1+1:
- result = 2;
- break;
- }
- equal(__, result, 'what is the value of result?');
-});
-
-test("switch default case", function() {
- var result = "Pippin";
- switch ("m") {
- case "f":
- result = "Frodo";
- break;
- case "s":
- result = "Samwise";
- break;
- default:
- result = "Merry";
- break;
- }
- equal(__, result, 'what is the value of result?');
-});
-
-test("null coalescing", function() {
- var result = null || "a value";
- equal(__, result, 'what is the value of result?');
-});
diff --git a/topics/about_equality.js b/topics/about_equality.js
index fe3e3d21..07ce80a7 100644
--- a/topics/about_equality.js
+++ b/topics/about_equality.js
@@ -1,14 +1,7 @@
-
+/* jshint eqeqeq: false, unused: true, eqnull: true, undef: false */
+/* jshint -W019 */
module("About Equality (topics/about_equality.js)");
-test("numeric equality", function() {
- equal(3 + __, 7, "");
-});
-
-test("string equality", function() {
- equal("3" + __, "37", "concatenate the strings");
-});
-
test("equality without type coercion", function() {
ok(3 === __, 'what is exactly equal to 3?');
});
@@ -17,7 +10,42 @@ test("equality with type coercion", function() {
ok(3 == "__", 'what string is equal to 3, with type coercion?');
});
-test("string literals", function() {
- equal(__, "frankenstein", "quote types are interchangable, but must match.");
- equal(__, 'frankenstein', "quote types can use both single and double quotes.");
+test("equality with type coercion", function() {
+ var undefIsNull; //undefined
+ if(undefIsNull == null) {
+ undefIsNull = true;
+ }
+ ok(__ === undefIsNull, 'is undefined equal to null?');
+});
+
+
+test("truthyness of positive numbers", function() {
+ var oneIsTruthy = 1 ? true : false;
+ ok(__ === oneIsTruthy, 'is one truthy?');
+});
+
+test("truthyness of negative numbers", function() {
+ var negativeOneIsTruthy = -1 ? true : false;
+ ok(__ === negativeOneIsTruthy, 'is -1 truthy?');
+});
+
+test("truthyness of zero", function() {
+ var zeroIsTruthy = 0 ? true : false;
+ ok(__ === zeroIsTruthy, 'is 0 truthy?');
+});
+
+
+test("casting to boolean", function() {
+ var castedTrue = !!"false" ? true : false;
+ ok(__ === castedTrue, 'is this string casted to boolean truthy?');
+});
+
+test("arrays of arrays truthyness", function() {
+ var isThisTrue = [[]] ? true : false;
+ ok(__ === isThisTrue, 'is an empty array within an array truthy?');
+});
+
+test("NaN equality", function() {
+ var isNaNEqualToNaN = NaN === NaN;
+ ok(__ === isNaNEqualToNaN, 'is an array within an array equal truthy?');
});
diff --git a/topics/about_functions.js b/topics/about_functions.js
new file mode 100644
index 00000000..0e95bb46
--- /dev/null
+++ b/topics/about_functions.js
@@ -0,0 +1,93 @@
+/* jshint eqeqeq: false, unused: false, eqnull: true, undef: false, latedef: false */
+/* jshint -W093 */
+module("About Functions (topics/about_functions.js)");
+
+test("defining named functions", function() {
+ function fun() {}
+ ok(fun.name === __, 'what is the name of the function?');
+});
+
+test("defining anonymous functions", function() {
+ var fun = function() {};
+ ok(fun.name === __, 'what is the name of the function?');
+});
+
+test("first class objects", function() {
+ var fun = function() {
+ return function returnFunc() {};
+ };
+ ok(typeof fun() === __, 'what is the type of the functions return value?');
+});
+
+
+test("using call to invoke function",function(){
+ var invokee = function( message ){
+ return this + message;
+ };
+ var result = invokee.call("I am this!", "Where did it come from?");
+
+ ok(__ === result, "what will the value of result?");
+});
+
+test("using apply to invoke function",function(){
+ var invokee = function( message1, message2 ){
+ return this + message1 + message2;
+ };
+ var result = invokee.apply("I am this!", ["I am arg1","I am arg2"]);
+
+ ok(__ === result, "what will the value of result?");
+});
+
+test("function arguments",function(){
+ function someFunc(a, b){
+ return a + b;
+ }
+
+ ok(someFunc(0, 1, 2, 3, 4) === __, "what happens if we use more arguments than the function expects?");
+});
+
+
+test("arguments array", function() {
+ var add = function() {
+ var total = 0;
+ for(var i = 0; i < arguments.length; i++) {
+ // complete the implementation of this method so that it returns the sum of its arguments
+ __;
+ }
+ __;
+ };
+
+ equal(15, add(1,2,3,4,5), "add 1,2,3,4,5");
+ equal(9, add(4,7,-2), "add 4,7,-2");
+});
+
+test("function properties",function(){
+ function someFunc(a){
+ if(!someFunc.cache) { someFunc.cache = {}; }
+ if(someFunc.cache[a] != null) {
+ return someFunc.cache[a];
+ }
+ var result = a * 5;
+ return someFunc.cache[a] = result;
+ }
+ var result = someFunc(2);
+ someFunc.cache[3] = 5;
+ result = someFunc(3);
+ ok(__ === result, "What is the value of result after consecutive calls?");
+});
+
+
+test("complex example with functions",function(){
+ function someFunc(a, b, c, d, e, f){
+ return a(b, c) + d(e, f);
+ }
+
+ var result = someFunc(function(a, b) {
+ return a + b;
+ }, "I", " am a ", function(a, b) {
+ return (a + b).split("").reverse().join("");
+ }, "dog tpircs", "avaj");
+
+ ok(__ === result, "what is the value of result?");
+});
+
diff --git a/topics/about_functions_and_closure.js b/topics/about_functions_and_closure.js
deleted file mode 100644
index 7eb20777..00000000
--- a/topics/about_functions_and_closure.js
+++ /dev/null
@@ -1,75 +0,0 @@
-module("About Functions And Closure (topics/about_functions_and_closure.js)");
-
-test("defining functions directly", function() {
- var result = "a";
- function changeResult() {
- // the ability to access a variables defined in the same scope as the function is known as 'closure'
- result = "b";
- };
- changeResult();
- equal(__, result, 'what is the value of result?');
-});
-
-test("assigning functions to variables", function() {
- var triple = function(input) {
- return input * 3;
- };
- equal(__, triple(4), 'what is triple 4?');
-});
-
-test("self invoking functions", function() {
- var publicValue = "shared";
-
- // self invoking functions are used to provide scoping and to alias variables
- (function(pv) {
- var secretValue = "password";
- equal(__, pv, 'what is the value of pv?');
- equal("__", typeof(secretValue), "is secretValue available in this context?");
- equal("__", typeof(publicValue), "is publicValue available in this context?");
- })(publicValue);
-
- equal("__", typeof(secretValue), "is secretValue available in this context?");
- equal("__", typeof(publicValue), "is publicValue available in this context?");
-});
-
-test("arguments array", function() {
- var add = function() {
- var total = 0;
- for(var i = 0; i < arguments.length; i++) {
- // complete the implementation of this method so that it returns the sum of its arguments
- // __
- }
- // __
- };
-
- equal(15, add(1,2,3,4,5), "add 1,2,3,4,5");
- equal(9, add(4,7,-2), "add 4,7,-2");
-});
-
-test("using call to invoke function",function(){
- var invokee = function( message ){
- return this + message;
- };
-
- //another way to invoke a function is to use the call function which allows
- //you to set the callers "this" context. Call can take any number of arguments:
- //the first one is always the context that this should be set to in the called
- //function, and the arguments to be sent to the function,multiple arguments are separated by commas.
- var result = invokee.call("I am this!", "Where did it come from?");
-
- equal(__, result, "what will the value of invokee's this be?");
-});
-
-test("using apply to invoke function",function(){
- var invokee = function( message1, message2 ){
- return this + message1 + message2;
- };
-
- //similar to the call function is the apply function. Apply only has two
- //arguments: the first is the context that this should be set to in the called
- //function and and array of arguments to be passed into the called function.
- var result = invokee.apply("I am this!", ["I am arg1","I am arg2"]);
-
- equal(__, result, "what will the value of invokee's this be?");
-});
-
diff --git a/topics/about_hoisting.js b/topics/about_hoisting.js
new file mode 100644
index 00000000..622449ae
--- /dev/null
+++ b/topics/about_hoisting.js
@@ -0,0 +1,75 @@
+/* jshint eqeqeq: false, unused: false, eqnull: true, undef: false, latedef: false */
+/* jshint -W019 */
+/* jshint -W004 */
+/* jshint -W083 */
+module("About Hoisting(topics/about_hoisting.js)");
+
+test("basic hoisting value", function() {
+ foo = 2;
+
+ ok(foo === __, 'what value does foo have?');
+
+ var foo = 5;
+});
+
+test("basic hoisting", function() {
+ ok(foo === __, 'what value does foo have?');
+
+ var foo = 5;
+});
+
+var barryglobal = 2;
+test("basic hoisting with globals", function() {
+ ok(barryglobal === __, 'what value does bar have?');
+
+ barryglobal = 5;
+});
+
+
+test("function hoisting", function() {
+ ok(typeof foo === __, 'is foo initialized as function?');
+
+ function foo() { return 5;}
+});
+
+test("function hoisting with expression", function() {
+ ok(typeof foo === __, 'is foo initialized as function?');
+
+ var foo = function() { return 5;};
+});
+
+test("function hoisting with a named variable", function() {
+ ok(typeof foo === __, 'is foo a number or a function?');
+
+ var foo = 5;
+ function foo() { return 5;}
+});
+
+
+test("advanced hoisting", function() {
+ ok(foo() === __, 'what does foo produce?');
+
+ var foo = 1;
+ foo = function() { return 2;};
+ function foo() { return 3;}
+});
+
+test("advanced hoisting", function() {
+ var foo = 1;
+ foo = function() { return 2;};
+
+ ok(foo() === __, 'what does foo produce?');
+
+ function foo() { return 3;}
+});
+
+test("advanced hoisting with loops", function() {
+ var txt = ["a","b","c"];
+ for (var i = 0; i < 3; ++i ) {
+ result = [];
+ result.push(txt[i]);
+ }
+ var result = [];
+
+ ok(result.length === __, "how many elements are in result?");
+});
diff --git a/topics/about_numbers.js b/topics/about_numbers.js
deleted file mode 100644
index 1319acd8..00000000
--- a/topics/about_numbers.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-module("About Numbers (topics/about_numbers.js)");
-
-test("types", function() {
- var typeOfIntegers = typeof(6);
- var typeOfFloats = typeof(3.14159);
- equal(__, typeOfIntegers === typeOfFloats, 'are ints and floats the same type?');
- equal(__, typeOfIntegers, 'what is the javascript numeric type?');
- equal(__, 1.0, 'what is a integer number equivalent to 1.0?');
-});
-
-test("NaN", function() {
- var resultOfFailedOperations = 7/'apple';
- equal(__, isNaN(resultOfFailedOperations), 'what will satisfy the equals assertion?');
- equal(__, resultOfFailedOperations == NaN, 'is NaN == NaN?');
-});
diff --git a/topics/about_objects.js b/topics/about_objects.js
deleted file mode 100644
index 24c03533..00000000
--- a/topics/about_objects.js
+++ /dev/null
@@ -1,43 +0,0 @@
-
-module("About Objects (topics/about_objects.js)");
-
-test("object type", function() {
- var empty_object = {};
- equal(__, typeof(empty_object), 'what is the type of an object?');
-});
-
-test("object literal notation", function() {
- var person = {
- __:__,
- __:__
- };
- equal("Amory Blaine", person.name, "what is the person's name?");
- equal(102, person.age, "what is the person's age?");
-});
-
-test("dynamically adding properties", function() {
- var person = {};
- person.__ = "Amory Blaine";
- person.__ = 102;
- equal("Amory Blaine", person.name, "what is the person's name?");
- equal(102, person.age, "what is the person's age?");
-});
-
-test("adding properties from strings", function() {
- var person = {};
- person["__"] = "Amory Blaine";
- person["__"] = 102;
- equal("Amory Blaine", person.name, "what is the person's name?");
- equal(102, person.age, "what is the person's age?");
-});
-
-test("adding functions", function() {
- var person = {
- name: "Amory Blaine",
- age: 102,
- toString: function() {
- return __; // HINT: use the 'this' keyword to refer to the person object.
- }
- };
- equal("I Amory Blaine am 102 years old.", person.toString(), "what should the toString function be?");
-});
diff --git a/topics/about_operators.js b/topics/about_operators.js
deleted file mode 100644
index 9859900b..00000000
--- a/topics/about_operators.js
+++ /dev/null
@@ -1,47 +0,0 @@
-
-module("About Operators (topics/about_operators.js)");
-
-test("addition", function() {
- var result = 0;
- //starting i at 0, add i to result and increment i by 1 until i is equal to 5
- for (var i = 0; i <= 5; i++) {
- result = result + i;
- }
- equal(__, result, "What is the value of result?");
-});
-
-test("assignment addition", function() {
- var result = 0;
- for (var i = 0; i <=5; i++) {
- //the code below is just like saying result = result + i; but is more concise
- result += i;
- }
- equal(__, result, "What is the value of result?");
-});
-
-test("subtraction", function() {
- var result = 5;
- for (var i = 0; i <= 2; i++) {
- result = result - i;
- }
- equal(__, result, "What is the value of result?");
-});
-
-test("assignment subtraction", function() {
- var result = 5;
- for (var i = 0; i <= 2; i++) {
- result -= i;
- }
- equal(__, result, "What is the value of result?");
-});
-
-//Assignment operators are available for multiplication and division as well
-//let's do one more, the modulo operator, used for showing division remainder
-
-test("modulus", function() {
- var result = 10;
- var x = 5;
- //again this is exactly the same as result = result % x
- result %= x;
- equal(__, result, "What is the value of result?");
-});
diff --git a/topics/about_prototypal_inheritance.js b/topics/about_prototypal_inheritance.js
deleted file mode 100644
index 811c040e..00000000
--- a/topics/about_prototypal_inheritance.js
+++ /dev/null
@@ -1,61 +0,0 @@
-
-// demonstrate the effect of modifying an objects prototype before and after the object is constructed
-
-module("About Prototypal Inheritance (topics/about_prototypal_inheritance.js)");
-
-// this 'class' pattern defines a class by its constructor
-var Mammal = function(name) {
- this.name = name;
-}
-// things that don't need to be set in the constructor should be added to the constructor's prototype property.
-Mammal.prototype = {
- sayHi: function() {
- return "Hello, my name is " + this.name;
- }
-}
-
-test("defining a 'class'", function() {
- var eric = new Mammal("Eric");
- equal(__, eric.sayHi(), 'what will Eric say?');
-});
-
-// add another function to the Mammal 'type' that uses the sayHi function
-Mammal.prototype.favouriteSaying = function() {
- return this.name + "'s favourite saying is " + this.sayHi();
-}
-
-test("more functions", function() {
- var bobby = new Mammal("Bobby");
- equal(__, bobby.favouriteSaying(), "what is Bobby's favourite saying?");
-});
-
-test("calling functions added to a prototype after an object was created", function() {
- var paul = new Mammal("Paul");
- Mammal.prototype.numberOfLettersInName = function() {
- return this.name.length;
- };
- // the following statement asks the paul object to call a function that was added
- // to the Mammal prototype after paul was constructed.
- equal(__, paul.numberOfLettersInName(), "how long is Paul's name?");
-});
-
-// helper function for inheritance.
-// From https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_Revisited
-function extend(child, supertype){
- child.prototype = supertype.prototype;
-}
-
-// "Subclass" Mammal
-function Bat(name, wingspan) {
- Mammal.call(this, name);
- this.wingspan = wingspan;
-}
-
-// configure inheritance
-extend(Bat, Mammal);
-
-test("Inheritance", function() {
- var lenny = new Bat("Lenny", "1.5m");
- equal(__, lenny.sayHi(), "what does Lenny say?");
- equal(__, lenny.wingspan, "what is Lenny's wingspan?");
-});
diff --git a/topics/about_prototype_chain.js b/topics/about_prototype_chain.js
deleted file mode 100644
index 46e3b4df..00000000
--- a/topics/about_prototype_chain.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// demonstrate objects prototype chain
-
-// https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_and_the_prototype_chain
-module("About Prototype Chain (topics/about_prototype_chain.js)");
-
-var father = {
- b: 3,
- c: 4
-};
-
-var child = Object.create(father);
-child.a = 1;
-child.b = 2;
-
-/*
- * ---------------------- ---- ---- ----
- * [a] [b] [c]
- * ---------------------- ---- ---- ----
- * [child] 1 2
- * ---------------------- ---- ---- ----
- * [father] 3 4
- * ---------------------- ---- ---- ----
- * [Object.prototype]
- * ---------------------- ---- ---- ----
- * [null]
- * ---------------------- ---- ---- ----
- * */
-
-test("Is there an 'a' and 'b' own property on child?", function () {
- equal(__, child.hasOwnProperty('a'), 'child.hasOwnProperty(\'a\')?');
- equal(__, child.hasOwnProperty('b'), 'child.hasOwnProperty(\'b\')?');
-});
-
-test("Is there an 'a' and 'b' property on child?", function () {
- equal(__, child.a, 'what is \'a\' value?');
- equal(__, child.b, 'what is \'b\' value?');
-});
-
-test("If 'b' was removed, whats b value?", function () {
- delete child.b;
- equal(__, child.b, 'what is \'b\' value now?');
-});
-
-
-test("Is there a 'c' own property on child?", function () {
- equal(__, child.hasOwnProperty('c'), 'child.hasOwnProperty(\'c\')?');
-});
-
-// Is there a 'c' own property on child? No, check its prototype
-// Is there a 'c' own property on child.[[Prototype]]? Yes, its value is...
-test("Is there a 'c' property on child?", function () {
- equal(__, child.c, 'what is the value of child.c?');
-});
-
-
-// Is there a 'd' own property on child? No, check its prototype
-// Is there a 'd' own property on child.[[Prototype]]? No, check it prototype
-// child.[[Prototype]].[[Prototype]] is null, stop searching, no property found, return...
-test("Is there an 'd' property on child?", function () {
- equal(__, child.d, 'what is the value of child.d?');
-});
-
-
diff --git a/topics/about_reflection.js b/topics/about_reflection.js
deleted file mode 100644
index 51e7c8ff..00000000
--- a/topics/about_reflection.js
+++ /dev/null
@@ -1,68 +0,0 @@
-module("About Reflection (topics/about_reflection.js)");
-
-var A = function() {
- this.aprop = "A";
-};
-
-var B = function() {
- this.bprop = "B";
-};
-
-B.prototype = new A();
-
-test("typeof", function() {
- equal(__, typeof({}), 'what is the type of an empty object?');
- equal(__, typeof('apple'), 'what is the type of a string?');
- equal(__, typeof(-5), 'what is the type of -5?');
- equal(__, typeof(false), 'what is the type of false?');
-});
-
-test("property enumeration", function() {
- var keys = [];
- var values = [];
- var person = {name: 'Amory Blaine', age: 102, unemployed: true};
- for(var propertyName in person) {
- keys.push(propertyName);
- values.push(person[propertyName]);
- }
- ok(keys.equalTo(['__','__','__']), 'what are the property names of the object?');
- ok(values.equalTo(['__',__,__]), 'what are the property values of the object?');
-});
-
-test("hasOwnProperty", function() {
- var b = new B();
- var propertyName;
-
- var keys = [];
- for (propertyName in b) {
- keys.push(propertyName);
- }
- equal(__, keys.length, 'how many elements are in the keys array?');
- deepEqual([__, __], keys, 'what are the properties of the array?');
-
- // hasOwnProperty returns true if the parameter is a property directly on the object,
- // but not if it is a property accessible via the prototype chain.
- var ownKeys = [];
- for(propertyName in b) {
- if (b.hasOwnProperty(propertyName)) {
- ownKeys.push(propertyName);
- }
- }
- equal(__, ownKeys.length, 'how many elements are in the ownKeys array?');
- deepEqual([__], ownKeys, 'what are the own properties of the array?');
-});
-
-test("constructor property", function () {
- var a = new A();
- var b = new B();
- equal(__, typeof(a.constructor), "what is the type of a's constructor?");
- equal(__, a.constructor.name, "what is the name of a's constructor?");
- equal(__, b.constructor.name, "what is the name of b's constructor?");
-});
-
-test("eval", function() {
- // eval executes a string
- var result = "";
- eval("result = 'apple' + ' ' + 'pie'");
- equal(__, result, 'what is the value of result?');
-});
diff --git a/topics/about_regular_expressions.js b/topics/about_regular_expressions.js
deleted file mode 100644
index b49bc723..00000000
--- a/topics/about_regular_expressions.js
+++ /dev/null
@@ -1,31 +0,0 @@
-
-module("About Regular Expressions (topics/about_regular_expressions.js)");
-
-test("exec", function() {
- var numberFinder = /(\d).*(\d)/;
- var results = numberFinder.exec("what if 6 turned out to be 9?");
- ok(results.equalTo([__, __, __]), 'what is the value of results?');
-});
-
-test("test", function() {
- var containsSelect = /select/.test(" select * from users ");
- equal(__, containsSelect, 'does the string provided contain "select"?');
-});
-
-test("match", function() {
- var matches = "what if 6 turned out to be 9?".match(/(\d)/g);
- ok(matches.equalTo([__, __]), 'what is the value of matches?');
-});
-
-test("replace", function() {
- var pie = "apple pie".replace("apple", "strawberry");
- equal(__, pie, 'what is the value of pie?');
-
- pie = "what if 6 turned out to be 9?".replace(/\d/g, function(number) { // the second parameter can be a string or a function
- var map = {'6': 'six','9': 'nine'};
- return map[number];
- });
- equal(__, pie, 'what is the value of pie?');
-});
-
-// THE END
diff --git a/topics/about_scope.js b/topics/about_scope.js
index efa802a0..350235aa 100644
--- a/topics/about_scope.js
+++ b/topics/about_scope.js
@@ -1,21 +1,160 @@
module("About Scope (topics/about_scope.js)");
-thisIsAGlobalVariable = 77;
+variable1 = 77;
test("global variables", function() {
- equal(__, thisIsAGlobalVariable, 'is thisIsAGlobalVariable defined in this scope?');
+ // defined returns true if the variable is defined in the scope, else false
+ ok(__ === defined(variable1), 'is variable1 defined in this scope?');
+ ok(__ === defined(window.variable1), 'is variable1 defined on the window object?');
});
-test("variables declared inside of a function", function() {
+test("function scope", function() {
var outerVariable = "outer";
- // this is a self-invoking function. Notice that it calls itself at the end ().
(function() {
var innerVariable = "inner";
- equal(__, outerVariable, 'is outerVariable defined in this scope?');
- equal(__, innerVariable, 'is innerVariable defined in this scope?');
+ ok(__ === defined(outerVariable), 'is outerVariable defined in this scope?');
+ ok(__ === defined(innerVariable), 'is innerVariable defined in this scope?');
})();
- equal(__, outerVariable, 'is outerVariable defined in this scope?');
- equal(__, typeof(innerVariable), 'is innerVariable defined in this scope?');
+ ok(__ === defined(outerVariable), 'is outerVariable defined in this scope?');
+ ok(__ === defined(innerVariable), 'is innerVariable defined in this scope?');
+});
+
+test("block scope", function() {
+ var outerVariable = "outer";
+
+ if (outerVariable) {
+ var innerVariable = "inner";
+ ok(__ === defined(outerVariable), 'is outerVariable defined in this scope?');
+ ok(__ === defined(innerVariable), 'is innerVariable defined in this scope?');
+ }
+
+ ok(__ === defined(outerVariable), 'is outerVariable defined in this scope?');
+ ok(__ === defined(innerVariable), 'is innerVariable defined in this scope?');
+});
+
+test("for loop scope", function() {
+ for (var i=0; i<10; i++) {
+ // doing something
+ }
+
+ ok(__ === defined(i), 'is i defined in this scope?');
+});
+
+test("functions calling functions", function() {
+ function foo() {
+ ok(__ === a, 'what is the value of a?');
+ }
+
+ function bar() {
+ var a = 3;
+ foo();
+ }
+
+ var a = 2;
+
+ bar();
+});
+
+test("functions inside functions calling functions inside for loops", function () {
+ var res = 0;
+
+ function foo() {
+ function bar(a) {
+ i = 10;
+ return a + i;
+ }
+
+ for (var i=0; i<10; i++) {
+ res += bar( i * 2 );
+ }
+ }
+ foo();
+
+ ok(__ === res, 'what is the final value of res?');
+});
+
+test("Outside scope", function () {
+ var res = 0;
+
+ function foo() {
+ function bar(a) {
+ b = 1;
+ return a + i;
+ }
+
+ for (var i=0; i<10; i++) {
+ res += bar( i * 2 );
+ }
+ }
+ foo();
+
+ ok(__ === defined(a), 'is a defined in this scope?');
+ ok(__ === defined(b), 'is b defined in this scope?');
+ ok(__ === defined(c), 'is c defined in this scope?');
+ ok(__ === defined(foo), 'is foo defined in this scope?');
+ ok(__ === defined(bar), 'is bar defined in this scope?');
+});
+
+test("For loops again", function () {
+ var res = 0;
+
+ function foo() {
+ function bar(res) {
+ res++;
+ return 1;
+ }
+
+ for (var i=0; i<10; i++) {
+ res += bar(i);
+ }
+ }
+ foo();
+ res++;
+
+ ok(__ === res, 'what is the final value of res?');
+});
+
+test("Try catch", function () {
+ try{
+ throw 2
+ } catch(a) {
+ ok(__ === a, 'whats the value of a?');
+ }
+ ok(__ === defined(a), 'is a defined in this scope?');
+});
+
+test("eval on strict", function () {
+ function foo(str) {
+ "use strict";
+ eval( str );
+ ok(__ === defined(a), 'is a defined in this scope?');
+ }
+
+ foo( "var a = 2" );
+});
+
+test("why you shouldn't use with", function () {
+ function foo(obj) {
+ with (obj) {
+ a = 2;
+ }
+ }
+
+ var o1 = {
+ a: 3
+ };
+
+ var o2 = {
+ b: 3
+ };
+
+ foo( o1 );
+ ok(__ === o1.a, 'what is o1.a?');
+ ok(__ === defined(a), 'is a defined in global scope?');
+
+ foo( o2 );
+ ok(__ === o2.a, 'what is o2.a?');
+ ok(__ === defined(a), 'is a defined in global scope?');
});
diff --git a/topics/about_strings.js b/topics/about_strings.js
deleted file mode 100644
index 18f9c68a..00000000
--- a/topics/about_strings.js
+++ /dev/null
@@ -1,34 +0,0 @@
-
-module("About Strings (topics/about_strings.js)");
-
-test("delimiters", function() {
- var singleQuotedString = 'apple';
- var doubleQuotedString = "apple";
- equal(__, singleQuotedString === doubleQuotedString, 'are the two strings equal?');
-});
-
-test("concatenation", function() {
- var fruit = "apple";
- var dish = "pie";
- equal(__, fruit + " " + dish, 'what is the value of fruit + " " + dish?');
-});
-
-test("character Type", function() {
- var characterType = typeof("Amory".charAt(1)); // typeof will be explained in about reflection
- equal(__, characterType, 'Javascript has no character type');
-});
-
-test("escape character", function() {
- var stringWithAnEscapedCharacter = "\u0041pple";
- equal(__, stringWithAnEscapedCharacter, 'what is the value of stringWithAnEscapedCharacter?');
-});
-
-test("string.length", function() {
- var fruit = "apple";
- equal(__, fruit.length, 'what is the value of fruit.length?');
-});
-
-test("slice", function() {
- var fruit = "apple pie";
- equal(__, fruit.slice(0,5), 'what is the value of fruit.slice(0,5)?');
-});
diff --git a/topics/about_this.js b/topics/about_this.js
index 85185f04..0fdd629a 100644
--- a/topics/about_this.js
+++ b/topics/about_this.js
@@ -6,39 +6,125 @@ test("'this' inside a method", function () {
intro: function () {
return "Hello, my name is " + this.__;
}
- }
- equal(person.intro(), "Hello, my name is bob", "If an object has a method can you access properties inside it?");
+ };
+
+ ok(person.intro() === "Hello, my name is bob", "If an object has a method can you access properties inside it?");
});
test("'this' on unattached function", function () {
var person = {
- globalName: 'bob',
+ unattachedName: 'bob',
intro: function () {
- return "Hello, my name is " + this.globalName;
+ return "Hello, my name is " + this.unattachedName;
}
- }
+ };
var alias = person.intro;
// if the function is not called as an object property 'this' is the global context
// (window in a browser). This is an example. Please do not do this in practise.
window.__ = 'Peter';
- equal(alias(), "Hello, my name is Peter", "What does 'this' refer to when it is not part of an object?");
+ ok(alias() === "Hello, my name is Peter", "What does 'this' refer to when it is not part of an object?");
+});
+
+asyncTest("this with setTimeout", function(assert) {
+ var name = 'frank';
+
+ var person = {
+ name: 'bob',
+ intro: function () {
+ ok(__ === this.name, "What does 'this' refer?");
+ start();
+ }
+ };
+
+ setTimeout(person.intro, 1 );
});
test("'this' set explicitly", function () {
var person = {
name: 'bob',
- intro: function () {
- return "Hello, my name is " + this.name;
- }
- }
+ intro: function () {
+ return "Hello, my name is " + this.name;
+ }
+ };
// calling a function with 'call' lets us assign 'this' explicitly
var message = person.intro.call({__: "Frank"});
equal(message, "Hello, my name is Frank", "What does 'this' refer to when you use the 'call()' method?");
});
-// extra credit: underscore.js has a 'bind' function http://documentcloud.github.com/underscore/#bind
-// read the source and see how it is implemented
+asyncTest("bound this", function(assert) {
+ var name = 'frank';
+
+ var person = {
+ name: 'bob',
+ intro: function () {
+ ok(__ === this.name, "What does 'this' refer?");
+ start();
+ }
+ };
+
+ var intro = person.intro.bind(person);
+
+ setTimeout(intro, 1 );
+});
+
+test("bound this with explicit call", function(assert) {
+ var name = 'frank';
+
+ var person = {
+ name: 'bob',
+ intro: function () {
+ ok(__ === this.name, "What does 'this' refer?");
+ }
+ };
+
+ var intro = person.intro.bind(person);
+
+ intro.call({name: 'test'});
+});
+
+test("double bound this", function(assert) {
+ var name = 'frank';
+
+ var person = {
+ name: 'bob',
+ intro: function () {
+ ok(__ === this.name, "What does 'this' refer?");
+ }
+ };
+
+ var intro = person.intro.bind(person);
+
+ var doubleBoundIntro = intro.bound({name: 'test'});
+
+ doubleBoundIntro();
+});
+
+test("new keyword", function(assert) {
+ var name = 'frank';
+ function Person (name) {
+ this.name = name;
+ }
+
+ var person = new Person('bob');
+
+ ok(__ === person.name, "What is the name of the person?");
+ ok(__ === name, "Has the name variable changed?");
+});
+
+test("overly complicated bind magic",function(){
+ function someFunc(a, b, c, d){
+ return this + a(b) + c(d);
+ }
+
+ var result = someFunc.bind("this", function(b) {
+ return this + b;
+ }.bind(" was", " too "), "hello", function(d) {
+ return (this + d).split("").reverse().join("");
+ }.bind("em rof"), " ysae")();
+
+ ok(__ === result, "what is the value of result?");
+});
diff --git a/topics/about_truthyness.js b/topics/about_truthyness.js
deleted file mode 100644
index 9b524c14..00000000
--- a/topics/about_truthyness.js
+++ /dev/null
@@ -1,22 +0,0 @@
-
-module("About Truthyness (topics/about_truthyness.js)");
-
-test("truthyness of positive numbers", function() {
- var oneIsTruthy = 1 ? true : false;
- equal(__, oneIsTruthy, 'is one truthy?');
-});
-
-test("truthyness of negative numbers", function() {
- var negativeOneIsTruthy = -1 ? true : false;
- equal(__, negativeOneIsTruthy, 'is -1 truthy?');
-});
-
-test("truthyness of zero", function() {
- var zeroIsTruthy = 0 ? true : false;
- equal(__, zeroIsTruthy, 'is 0 truthy?');
-});
-
-test("truthyness of null", function() {
- var nullIsTruthy = null ? true : false;
- equal(__, nullIsTruthy, 'is null truthy?');
-});