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?'); -});