├── .gitignore ├── Gemfile ├── public ├── slides.pdf ├── images │ ├── minus.gif │ └── plus.gif ├── css │ ├── chosen-sprite.png │ ├── editor.css │ └── chosen.css ├── js │ ├── globals.js │ ├── ace │ │ ├── theme-vibrant_ink.js │ │ ├── theme-textmate.js │ │ └── mode-javascript.js │ ├── editor.js │ ├── stacktrace.js │ ├── chosen.jquery.min.js │ └── jquery-1.4.3.min.js └── index.html ├── tasks ├── 04. Iteration methods │ ├── 08. Summing it up │ ├── 04. Mapping data │ ├── 05. Check all those elements │ ├── 02. Traversing arrays with forEach │ ├── 01. Functional fibonaci │ ├── 03. Find what you want │ ├── 07. Reduce our answer │ ├── 06. Lets find that needle │ └── 09. Bringing it together, mapReduce... ├── 02. Functions │ ├── 08. What is this │ ├── 04. Variables by reference (intermediate) │ ├── 09. Constructing objects │ ├── 05. Binding scope │ ├── 07. Variables by reference, revisited (hard) │ ├── 02. Higher order functions │ ├── 11. Is this yours (intermediate) │ ├── 10. Context confusion (hard) │ ├── 01. How many arguments do you have │ ├── 03. JavaScript pwns your scope (hard) │ └── 06. Globally ugly, privately slow, closing sweet (intermediate) ├── 01. Object Literals │ ├── 01. Object properties │ ├── 02. Populating arrays │ ├── 04. Who are you │ └── 03. What have you got └── 03. Inheritance │ ├── 01. Pseudoclassical 1 (intermediate) │ ├── 04. Functional (intermediate) │ ├── 02. Pseudoclassical 2 (intermediate) │ └── 03. Prototypal (hard) ├── Gemfile.lock ├── README.md └── config.ru /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "sinatra" 4 | gem "json" 5 | -------------------------------------------------------------------------------- /public/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/slides.pdf -------------------------------------------------------------------------------- /public/images/minus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/images/minus.gif -------------------------------------------------------------------------------- /public/images/plus.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/images/plus.gif -------------------------------------------------------------------------------- /public/css/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnab/js-workshop/HEAD/public/css/chosen-sprite.png -------------------------------------------------------------------------------- /tasks/04. Iteration methods/08. Summing it up: -------------------------------------------------------------------------------- 1 | Implement a basic sum function using what we have learned so far. 2 | -- 3 | -------------------------------------------------------------------------------- /tasks/02. Functions/08. What is this: -------------------------------------------------------------------------------- 1 | What is this? 2 | -- 3 | var add = function(a, b) { 4 | this.result = a + b; 5 | }; 6 | 7 | add(1, 2); 8 | 9 | assert( ... === 3); -------------------------------------------------------------------------------- /tasks/02. Functions/04. Variables by reference (intermediate): -------------------------------------------------------------------------------- 1 | Log numbers 0-4 correctly in by extracting the setTimeout call to bind variable i: 2 | -- 3 | var i; 4 | for (i = 0; i < 5; i++) { 5 | setTimeout(function () { 6 | console.log(i); 7 | }, i*1000); 8 | } 9 | -------------------------------------------------------------------------------- /tasks/02. Functions/09. Constructing objects: -------------------------------------------------------------------------------- 1 | Construct two car object instances. 2 | -- 3 | function Car (type) { 4 | this.type = type; 5 | } 6 | 7 | var honda = ... 8 | var bmw = ... 9 | 10 | assert(honda.type === 'honda'); 11 | assert(bmw.type === 'bmw'); 12 | -------------------------------------------------------------------------------- /tasks/02. Functions/05. Binding scope: -------------------------------------------------------------------------------- 1 | Make the function times return a function that returns the next multiple of n for each subsequent call. 2 | -- 3 | var times = function (n) { 4 | 5 | }; 6 | 7 | var times2 = times(2); 8 | times2(); // 2 9 | times2(); // 4 10 | times2(); // 6 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | json (1.5.3) 5 | rack (1.3.2) 6 | sinatra (1.2.6) 7 | rack (~> 1.1) 8 | tilt (>= 1.2.2, < 2.0) 9 | tilt (1.3.2) 10 | 11 | PLATFORMS 12 | ruby 13 | 14 | DEPENDENCIES 15 | json 16 | sinatra 17 | -------------------------------------------------------------------------------- /tasks/02. Functions/07. Variables by reference, revisited (hard): -------------------------------------------------------------------------------- 1 | Log numbers 0-4 correctly by wrapping the anonomous function in a function that is run immediately to bind variable i: 2 | -- 3 | var i; 4 | for (i = 0; i < 5; i++) { 5 | setTimeout(function () { 6 | console.log(i); 7 | }, i*1000); 8 | } 9 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/01. Object properties: -------------------------------------------------------------------------------- 1 | Set the three properties, in three different ways. 2 | -- 3 | var person = { 4 | 5 | }; 6 | 7 | 8 | assert(person.name === 'bob', 'name should equal "bob"'); 9 | assert(person.gender === 'male', 'gender should equal "male"'); 10 | assert(person.weight === 100, 'weight should equal 100'); 11 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/02. Populating arrays: -------------------------------------------------------------------------------- 1 | Populate the array, please. 2 | -- 3 | var arr = []; 4 | var i; 5 | for (i = 0; i < 10; i += 1) { 6 | // .. 7 | } 8 | 9 | assert(arr[0] === 0, 'first element should equal 0'); 10 | assert(arr[1] === 1, 'second element should equal 1'); 11 | assert(arr.length === 10, 'array length should be 10'); 12 | -------------------------------------------------------------------------------- /tasks/02. Functions/02. Higher order functions: -------------------------------------------------------------------------------- 1 | Create a function that returns new functions that return true! 2 | -- 3 | var createFunction = function () {}; 4 | 5 | assert(typeof createFunction() === 'function', 6 | 'createFunction() should return a function'); 7 | assert(createFunction()() === true, 8 | "calling createFunction's returned function should return true"); 9 | -------------------------------------------------------------------------------- /tasks/02. Functions/11. Is this yours (intermediate): -------------------------------------------------------------------------------- 1 | Use Bob's greet method to greet Ed! Don't repeat yourself, please. 2 | -- 3 | var bob = { 4 | name: 'Bob', 5 | greet: function () { 6 | return 'O hai ' + this.name + '!'; 7 | } 8 | }; 9 | 10 | var ed = { 11 | name: 'Ed' 12 | }; 13 | 14 | assert(bob.greet() === 'O hai Bob!'); 15 | assert( ... === 'O hai Ed!'); 16 | -------------------------------------------------------------------------------- /tasks/02. Functions/10. Context confusion (hard): -------------------------------------------------------------------------------- 1 | Fix the following code by adding one line and changing one word. 2 | -- 3 | var PI = { 4 | factor : 3.14, 5 | times : function (a) { 6 | 7 | var calculate = function (num) { 8 | return num * this.factor; // wtf? 9 | }; 10 | 11 | return calculate(a); 12 | } 13 | } 14 | 15 | assert(PI.times(2) === 6.28); 16 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/04. Mapping data: -------------------------------------------------------------------------------- 1 | Find the square root of all the elements using map() 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | 5 | // Implement the map function so that it returns a new array with all the 6 | // results from the func calls. 7 | 8 | function map(array, func) { 9 | 10 | } 11 | 12 | var result = map(data, Math.sqrt); 13 | console.log(result); 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #js-workshop 2 | 3 | Workshop tasks and in-browser JavaScript editor and evaluator. 4 | 5 | ### Install 6 | git clone git://github.com/gnab/js-workshop.git 7 | cd js-workshop 8 | bundle install # You need Ruby and the bundler gem (gem install bundler) 9 | 10 | ### Run 11 | rackup 12 | 13 | The `rackup` command will launch the app at http://localhost:9292 by default. 14 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/05. Check all those elements: -------------------------------------------------------------------------------- 1 | Test all values in the data set 2 | -- 3 | function greaterThanZero(element, index, array) { 4 | return element > 0; 5 | } 6 | 7 | // Implement every so that it returns true if all calls to func where true. 8 | 9 | function every(array, func) { 10 | 11 | } 12 | 13 | assert(every([1, 2, 3], greaterThanZero) === true); 14 | assert(every([1, -2, 3], greaterThanZero) === false); 15 | -------------------------------------------------------------------------------- /tasks/02. Functions/01. How many arguments do you have: -------------------------------------------------------------------------------- 1 | Complete the sum function, making it sum all arguments, no matter how many you give it. 2 | -- 3 | 4 | function sum () { 5 | var s = 0; 6 | 7 | return s; 8 | } 9 | 10 | assert(sum() === 0, 'sum without arguments should be 0'); 11 | assert(sum(1, 2) === 3, 'sum with arguments 1 and 2 should equal 3'); 12 | assert(sum(1, 2, 3) === 6, 'sum with arguments 1, 2, and 3 should equal 6'); 13 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/04. Who are you: -------------------------------------------------------------------------------- 1 | Make the doubleMe function return the argument type followed by the value doubled for numbers and the string repeated twice for strings. 2 | -- 3 | function doubleMe (val) { 4 | 5 | } 6 | 7 | assert(doubleMe(17) === 'number 34', 8 | '17 doubled should equal "number 34"'); 9 | assert(doubleMe('painnkaak') === 'string painnkaakpainnkaak', 10 | 'painnkaak doubled should equal "string painnkaakpainnkaak"'); 11 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/01. Pseudoclassical 1 (intermediate): -------------------------------------------------------------------------------- 1 | Utilize the prototype property to have Car instances share the honk method rather than each creating their own. 2 | -- 3 | var Car = function Car () { 4 | this.honk = function () { 5 | console.log('Honk!'); 6 | }; 7 | }; 8 | 9 | //Car.proto.. 10 | 11 | var bmw = new Car(); 12 | bmw.honk(); 13 | 14 | var honda = new Car(); 15 | honda.honk(); 16 | 17 | assert(bmw.honk === honda.honk); 18 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/04. Functional (intermediate): -------------------------------------------------------------------------------- 1 | Make honda h "inherit" car's honk method by changing one line of code. 2 | -- 3 | var car = function (spec) { 4 | spec = spec || {}; 5 | var that = {}; 6 | 7 | that.honk = function () { 8 | return spec.sound; 9 | }; 10 | 11 | return that; 12 | }; 13 | 14 | var honda = function (spec) { 15 | var that = {}; 16 | return that; 17 | }; 18 | 19 | var h = honda({ sound: 'honk!' }); 20 | assert(h.honk() === 'honk!'); 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/02. Traversing arrays with forEach: -------------------------------------------------------------------------------- 1 | Print all the numbers in the array using your own forEach 2 | -- 3 | var data = [1, 2, 3, 4, 5]; 4 | 5 | // Implement forEach so that it calls func with the current element, the index 6 | // and the array on all the items. 7 | 8 | function forEach(array, func) { 9 | 10 | } 11 | 12 | forEach(data, function(element, index, array) { 13 | console.log(element); 14 | }); 15 | 16 | // What about hashes, does your forEach support traversing them? 17 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/02. Pseudoclassical 2 (intermediate): -------------------------------------------------------------------------------- 1 | Hondas cannot honk! Try to fix this so that new Hondas will share Car's honk method, utilizing Hondas prototype property! 2 | -- 3 | function Car () {} 4 | Car.prototype.honk = function () { 5 | console.log('Honk!'); 6 | }; 7 | 8 | function Honda () {} 9 | // Honda.proto.. 10 | 11 | var h1 = new Honda(); 12 | h1.honk(); 13 | 14 | var h2 = new Honda(); 15 | h2.honk(); 16 | 17 | assert(h1.honk === h2.honk, "the Hondas does not share Car's honk method"); 18 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/01. Functional fibonaci: -------------------------------------------------------------------------------- 1 | Write a basic functional fibonaci in JS :) 2 | -- 3 | 4 | // Feel free to skip this one if you feel you have done it enough times before 5 | // :) 6 | 7 | // Implement a fibonaci function using basic functional programing. 8 | 9 | function fib(n) { 10 | 11 | } 12 | 13 | assert(fib(1) == 1, 'fib(1) != 1'); 14 | assert(fib(2) == 1, 'fib(2) != 1'); 15 | assert(fib(3) == 2, 'fib(3) != 2'); 16 | assert(fib(4) == 3, 'fib(4) != 3'); 17 | assert(fib(9) == 34, 'fib(9) != 34'); 18 | -------------------------------------------------------------------------------- /tasks/03. Inheritance/03. Prototypal (hard): -------------------------------------------------------------------------------- 1 | Give the car the honk method using protoypal inheritance, inheriting directly from the utilities object. 2 | -- 3 | var utilities = { 4 | honk: function () { 5 | console.log('Honk!'); 6 | } 7 | }; 8 | 9 | var create = function create (prototype) { 10 | function F () {} 11 | // .. 12 | return new F(); 13 | }; 14 | 15 | var bmw = create(utilities); 16 | bmw.honk(); 17 | 18 | var honda = create(utilities); 19 | honda.honk(); 20 | 21 | assert(bmw.honk === honda.honk); 22 | -------------------------------------------------------------------------------- /tasks/01. Object Literals/03. What have you got: -------------------------------------------------------------------------------- 1 | Complete createCopy, so that all properties of the original are transfered to the copy. 2 | -- 3 | function createCopy (original) { 4 | var copy = []; 5 | for ( ... ) { 6 | copy[k] = original[k]; 7 | } 8 | return copy; 9 | } 10 | 11 | var a = []; 12 | a.key = 1; 13 | a['key2'] = 1; 14 | a.push(2); 15 | 16 | var b = createCopy(a); 17 | 18 | assert(b[0] === 2, 'element was not copied'); 19 | assert(b.key === 1, 'key was not copied'); 20 | assert(b['key2'] === 1, 'key2 was not copied'); 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/03. Find what you want: -------------------------------------------------------------------------------- 1 | Using your forEach function build a odd number filter 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 9, 10]; 4 | 5 | function isOdd(element, index, array) { 6 | return element % 2 == 1; 7 | } 8 | 9 | // Implement filter so that it returns an array with all the array elements that 10 | // func returned true for. 11 | 12 | function filter(array, func) { 13 | 14 | } 15 | 16 | var result = filter(data, isOdd); 17 | 18 | console.log(result); 19 | 20 | // How would you use this basis to create a filterOdd function? 21 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/07. Reduce our answer: -------------------------------------------------------------------------------- 1 | Now lets see if we can accumulate an answer... 2 | -- 3 | var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | 5 | function multiply(previousValue, currentValue, index, array) { 6 | return previousValue * currentValue; 7 | } 8 | 9 | // The reduce function needs to keep track of an accumulator which is the 10 | // return value from the callback. The callback should be called in the current 11 | // accumulator, the current value, index and array like the other iterator 12 | // methods. 13 | 14 | function reduce(array, func, initial) { 15 | 16 | } 17 | 18 | var result = reduce(data, multiply, 1); 19 | 20 | assert(result === 3628800, result); 21 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'json' 3 | 4 | mime_type :less, 'text/css' 5 | 6 | get '/' do 7 | File.new('public/index.html').readlines 8 | end 9 | 10 | get '/tasks.js' do 11 | JSON(Dir['tasks/*/*'].sort.inject({}) { |sections, path| 12 | section, task = path.split('/')[1..2] 13 | sections[section] ||= {:name => section, :tasks => []} 14 | 15 | description, code = File.readlines(path).join.split(/^--$/m). 16 | collect { |text| text.strip }[0..1] 17 | 18 | sections[section][:tasks].push({ 19 | :name => task, 20 | :description => description || '', 21 | :code => code || '' 22 | }) 23 | sections 24 | }.values.sort_by {|section| section[:name] }) 25 | end 26 | 27 | run Sinatra::Application 28 | -------------------------------------------------------------------------------- /tasks/02. Functions/03. JavaScript pwns your scope (hard): -------------------------------------------------------------------------------- 1 | Make all asserts pass by uncommenting each assert and only changing double dots into variables, function calls and typeof-s. 2 | -- 3 | var a = 1; 4 | b = 3; 5 | var s = 1; 6 | 7 | (function () { 8 | a = 2; 9 | b = 4; 10 | var C = 3; 11 | d = 1; 12 | s = 2; 13 | 14 | if (false) { 15 | var s; 16 | function f () { 17 | var s = 7; 18 | return s; 19 | } 20 | } 21 | 22 | assert( .. === 2); 23 | // assert( .. === 3); 24 | // assert( .. === 4); 25 | // assert( .. === 1); 26 | // assert( .. === 7); 27 | // assert( .. === 2); 28 | 29 | })(); 30 | 31 | // assert( .. === 2); 32 | // assert( .. === 4); 33 | // assert( .. === 'undefined'); 34 | // assert( .. === 1); 35 | // assert( .. === 'undefined'); 36 | // assert( .. === 1); 37 | -------------------------------------------------------------------------------- /tasks/04. Iteration methods/06. Lets find that needle: -------------------------------------------------------------------------------- 1 | Find the needle in the haystack 2 | -- 3 | var data = [/* 0 .. 100 */]; 4 | for (var i = 0; i < 100; i++) { 5 | data.push(i); 6 | } 7 | 8 | function sumEqualProduct(element, index, array) { 9 | var parts = (element + "").split("", 2); 10 | var first = parseInt(parts[0], 10); 11 | var second = parseInt(parts[1], 10); 12 | return first+second == first*second; 13 | } 14 | 15 | // Implement the some function so that it indicates if we have at least one 16 | // result from the function that is true. 17 | 18 | function some(array, func) { 19 | 20 | } 21 | 22 | console.log(some(data, sumEqualProduct)); 23 | 24 | // Now use another list manipulation technique to find out which number we 25 | // found :) 26 | 27 | // Does your implementation loop over all the values for all cases, is this 28 | // necessary? 29 | -------------------------------------------------------------------------------- /tasks/02. Functions/06. Globally ugly, privately slow, closing sweet (intermediate): -------------------------------------------------------------------------------- 1 | Create a third variant of the isOneTwoOrThree-methods utilizing JavaScript's closures; where data is private, the function is fast - it's sweet. 2 | -- 3 | var data = [1, 2, 3]; 4 | var uglyIsOneTwoOrThree = function (n) { 5 | return data.indexOf(n) != -1; 6 | }; 7 | assert(uglyIsOneTwoOrThree(1) === true); 8 | assert(uglyIsOneTwoOrThree(2) === true); 9 | assert(uglyIsOneTwoOrThree(4) === false); 10 | 11 | 12 | var slowIsOneTwoOrThree = function (n) { 13 | var data = [1, 2, 3]; 14 | return data.indexOf(n) != -1; 15 | }; 16 | assert(slowIsOneTwoOrThree(1) === true); 17 | assert(slowIsOneTwoOrThree(2) === true); 18 | assert(slowIsOneTwoOrThree(4) === false); 19 | 20 | 21 | var sweetIsOneTwoOrThree = function () { 22 | 23 | }(); 24 | assert(sweetIsOneTwoOrThree(1) === true); 25 | assert(sweetIsOneTwoOrThree(2) === true); 26 | assert(sweetIsOneTwoOrThree(4) === false); 27 | -------------------------------------------------------------------------------- /public/js/globals.js: -------------------------------------------------------------------------------- 1 | var iter = { 2 | forEach: function (list, func) { 3 | for (var key in list) { 4 | func(list[key], key, list); 5 | } 6 | }, 7 | filter: function(list, func) { 8 | var res = []; 9 | this.forEach(list, function(element, index, list) { 10 | if (func(element, index, list)) { 11 | res.push(element); 12 | } 13 | }); 14 | return res; 15 | }, 16 | map: function(list, func) { 17 | var res = []; 18 | this.forEach(list, function(element, index, list) { 19 | res.push(func(element, index, list)); 20 | }); 21 | return res; 22 | }, 23 | every: function(list, func) { 24 | var res = true; 25 | this.forEach(list, function(element, index, list) { 26 | res = res && func(element, index, list); 27 | }); 28 | return res; 29 | }, 30 | reduce: function(list, func, initial) { 31 | var accumelator = initial; 32 | this.forEach(list, function(element, index, list) { 33 | accumelator = func(accumelator, element, index, list); 34 | }); 35 | return accumelator; 36 | }, 37 | groupBy: function(list, keyFunc, valFunc) { 38 | var groups = {}; 39 | iter.forEach(list, function(element, index, list) { 40 | var key = element[0]; 41 | if (!groups[key]) { 42 | groups[key] = []; 43 | } 44 | groups[key].push(element[1]); 45 | }); 46 | return groups; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |