Working through @chrisdickinson's guide to JS
Last active
December 15, 2015 02:39
-
-
Save AWinterman/5188959 to your computer and use it in GitHub Desktop.
Working through exercises from https://gist.github.com/chrisdickinson/821359fb8d95570ab22a
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var assert = require('assert') | |
/*** | |
these exercises are executable! | |
whenever you see something like `require('./example')`, you should | |
open up a file in the current directory called `example.js` (in vim, | |
do `:vsplit example.js`). at the bottom of your file, export your function | |
by writing `module.exports = <function name you want to export>`. | |
so, for example you might see: | |
// *** EXERCISE 1: | |
// please sir, i'd like to turn a string into an array! | |
require('./exercise-1')("hello world") | |
then you should open `exercise-1.js` and write: | |
module.exports = function(string) { | |
return // make that thing work | |
} | |
so, without further ado! let's talk about types: primitives and objects. | |
*** QUICK VIM NOTE: *** | |
in NORMAL mode: | |
* type `:vsplit filename` and hit <enter> to open a new vertical pane. `:split filename` <enter> will | |
open a horizontal pane. | |
* to flip between panes, press <Ctrl-W> + <Up>, <Down>, <Left> or <Right>. | |
* remember `:!node %`? you should be using it a lot during this lesson. | |
keep in mind that the `%` stands for the file in the pane you have focused! | |
***/ | |
// first, the basic types: | |
var type | |
// an object. in javascript, everything can be treated like an object. | |
// an object is just a mapping of properties to attributes. | |
// you can declare an object like so: | |
type = {} | |
// a property is just a string. | |
type["greeting"] = "sup dudes" | |
console.log('type is:', type) | |
// if the property string only contains alphanumeric characters and underscores, | |
// you can also access it like so: | |
type.greeting = "this works too" | |
console.log('setting `type.greeting`:', type.greeting) | |
// same goes for declaring objects: | |
type = { | |
'this needs quotes': 'yep' | |
, this_doesnt: 'nope' | |
} | |
// you can check if a key is present in an object with `in`, or `obj.key !== undefined`: | |
console.log('is this_doesnt in type?', 'this_doesnt' in type) | |
console.log('is type.this_doesnt defined?', type.this_doesnt !== undefined) | |
// you can loop over the keys of an object with `for(var key in <object>)` | |
// loops. | |
console.log('listing keys and values of type ----') | |
for(var key in type) { | |
// to look up a key from an object, use the `obj[<expression>]` syntax: | |
console.log(' ', key, type[key]) | |
} | |
console.log('----') | |
// so, everything in javascript can be thought of as an object. | |
// let's go with an easy example: arrays. | |
// arrays are objects with integer (0, 1, 2, 3, 4...) indices. | |
type = ['hi', 'there', 'guys'] | |
// but they're also objects. | |
type.thing = 'lol' | |
console.log('array property `type.thing`:', type.thing) | |
// so, we get into a bit of an issue here: for/in doesn't work quite | |
// as expected with objects: | |
console.log('listing keys and values of an array ----') | |
for(var key in type) { | |
console.log(' ', key, type[key]) | |
} | |
console.log('----') | |
// it outputs "thing" even though we only care about the integer indices! | |
// instead, with arrays, you should always use `for(var i = 0; i < array.length; ++i)` | |
// style loops: | |
console.log('listing *just the integer keys and values* of an array ----') | |
for(var i = 0, len = type.length; i < len; ++i) { | |
console.log(' ', i, type[i]) | |
} | |
console.log('----') | |
// *** EXERCISE 1: | |
// using what we know about looping over arrays and creating objects, | |
// write a function that takes two arrays of equal length, and returns an object | |
// using the first array as the keys, and the second array as values. | |
// HINT: you can nest lookups: `out[lhs[i]]`! | |
var keys = ['gary', 'human', 'hat'] | |
, vals = ['busey', 'being', 'town'] | |
, expected = {gary: 'busey', human: 'being', hat: 'town'} | |
var your_function = require('./exercise-1') | |
, your_result = your_function(keys, vals) | |
assert.deepEqual(your_result, expected) | |
// ---- moar objects ---- | |
// functions are objects, too, that just happen to be callable. | |
var my_function = function() { | |
return my_function.value | |
} | |
my_function.value = 13 | |
// as are regexen. | |
var my_regex = /([^\/]+)/g | |
my_regex.value = 26 | |
// see? | |
console.log('my_function.value', my_function.value) | |
console.log('my_regex.value', my_function.value) | |
// *** EXERCISE 2: | |
// functions are objects! how can we use this to our advantage? | |
// well, there's a technique called "memoization" -- the theory is that | |
// sometimes you have a big expensive function that will always return | |
// the same result if it's given the same argument. | |
// | |
// given that, write a function that: | |
// * when called, looks for `<your_function_name>.cache` for an object | |
// * if it's not there, it creates an empty object there. | |
// * checks that cache object for the presence or absence of the first argument | |
// * if `fn.cache[argument1]` is defined, returns that. if not, it should call the second argument | |
// store it in `fn.cache[argument1]`, and return that value. | |
var your_function = require('./exercise-2') | |
, argument_one = 'random-'+~~(Math.random() * 10) | |
, expected = Math.random() | |
, result | |
assert.strictEqual(your_function.cache, undefined, 'Exercise 2: `<yourfunction>.cache should be undefined if it hasn\'t been called yet.') | |
result = your_function(argument_one, function argument_two() { | |
return expected | |
}) | |
assert.ok(your_function.cache, 'Exercise 2: <yourfunction>.cache should exist.') | |
assert.equal(result, expected, 'Exercise 2: your_function should return the value of `argument_two()`.') | |
result = your_function(argument_one, function argument_two() { | |
assert.fail("this shouldn't get called at all, since `your_function` is memoized.") | |
}) | |
assert.equal(result, expected, 'Exercise 2: your_function should return the memoized value.') | |
// --- primitives --- | |
// so we've found that several things are objects but serve different | |
// uses (functions, arrays, regexen). what about simpler objects, like | |
// boolean values (true, false), strings ("hello world"), and numbers | |
// (1, 2, 1.3333, Infinity)? | |
// | |
// well, it turns out that while they can be treated *like* objects, | |
// they're not quite fully-fledged objects. | |
var str = "hello world" | |
str.attribute = "yeah" | |
console.log('"hello world".attribute = "yeah": ', str.attribute) | |
// what the what? | |
var num = 12. | |
num.attribute = "yeah" | |
console.log('12..attribute = "yeah": ', num.attribute) | |
// okay, weird, weird | |
var bool = true | |
bool.attribute = "yeah" | |
console.log('(true).attribute = "yeah": ', bool.attribute) | |
// what's going on?! | |
// well, these types are "primitive" -- they're building block types. | |
// they can still be *treated* like objects (you can lookup attributes from them, | |
// in this case, `toUpperCase`): | |
console.log("string.toUpperCase()", "i am object, hear me roar".toUpperCase()) | |
// but you can't assign new properties to them. | |
// so what's really happening? | |
// well, when JS detects that you're using a primitive as an object, it creates | |
// a **temporary** throwaway object that stands in for that value. | |
// | |
// so: "asdf".toUpperCase() is actually equivalent to `new String("asdf").toUpperCase()`. | |
// | |
// JavaScript actually maps each primitive type to a function constructor: | |
console.log('"string".constructor.name =', "string".constructor.name) | |
console.log('12..constructor.name =', 12..constructor.name) | |
console.log('true.constructor.name =', true.constructor.name) | |
// *** Exercise 3: | |
// | |
// INTERNET FIELD TRIP! | |
// * background this process and open up a node REPL. | |
// > str = "yeah" | |
// "yeah" | |
// > num = 21 | |
// 21 | |
// * now type `str.` at the next prompt and hit <TAB>. | |
// * you'll see two horizontal lists of methods. | |
// * pick two or three from the bottom list and write them down someplace (a piece of paper or something.) | |
// * open up google and search `mdn <that method name minus the str.>` | |
// * open up the article and read about the function. | |
// * open up `exercise-3.js` and use those methods in some way, shape or form, `console.log`-ing the | |
// "before" and "after" values. | |
// * repeat for numbers. | |
// bonus points: document that file like i've written this one. | |
require('./exercise-3') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var keys = ['gary', 'human', 'hat'] | |
, vals = ['busey', 'being', 'town'] | |
, expected = {gary: 'busey', human: 'being', hat: 'town'} | |
module.exports = function(keys, vals){ | |
var zipped_obj = {}, | |
//get max length just in case. | |
max_length = Math.max(keys.length, vals.length), | |
i | |
for (i = 0; i < max_length; ++i){ | |
zipped_obj[keys[i]] = vals[i]; | |
} | |
return zipped_obj; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var memoizer; | |
memoizer = function(a, b){ | |
if (memoizer.cache === undefined){ | |
memoizer.cache = {} | |
} | |
if (a in memoizer.cache){ | |
return memoizer.cache[a]; | |
} | |
else { | |
var to_return = b(); | |
memoizer.cache[a] = to_return; | |
return to_return | |
} | |
} | |
module.exports = memoizer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Part A. | |
// | |
//I picked anchor, constructor, and link. | |
// | |
//Write a function of three arguments that checks to make sure it's arguments | |
//are native strings, and returns its first argument anchored to its second and | |
//linked to its third. | |
var assert = require('assert'); | |
var anchorNative = function(text, name, url){ | |
for (var argIdx = 0; argIdx < arguments.length; ++argIdx){ | |
//make sure they're all native, meaning the constructor is '' | |
//if it isn't make it so. | |
// | |
//There are easier ways to do this for strings, but this would work for | |
//an arbitrary type, if we changed the rhs of the deep equals, of | |
//course. | |
if (arguments[argIdx].constructor() === ''){ | |
arguments[argIdx] = ''.constructor(arguments[argIdx]) | |
} | |
} | |
return text.anchor(name).link(url); | |
} | |
var expected = '<a href="undefined"><a name="1">hello</a></a>', | |
result = anchorNative("hello", 1); | |
assert.equal(expected, result); | |
//Part B | |
// | |
// | |
var num = 21; | |
// > num. | |
// num.__defineGetter__ num.__defineSetter__ num.__lookupGetter__ | |
// num.__lookupSetter__ num.constructor num.hasOwnProperty | |
// num.isPrototypeOf num.propertyIsEnumerable num.toLocaleString | |
// num.toString num.valueOf | |
// | |
// num.constructor num.toExponential num.toFixed | |
// num.toLocaleString num.toPrecision num.toString | |
// num.valueOf | |
//Picking toLcaleString, toPrecision, and valueOf | |
//looks like Number.toLocaleString just handles commas vs. periods thousands and | |
//decimal seperators correctly. | |
// | |
//Number.toPrecision converts a number to one with a given number of | |
//significant figures | |
// | |
//valueOf is not very exciting. I'm going to look at toFixed instead, which | |
//deterimines how many figures to include after the decimal point. Seems | |
//related to toPrecision. | |
//Write a function which compares a number with two significant figures to a | |
//number with two values after the decimal point. Make sure these are rendered | |
//correctly for the user's locale. | |
var compareSigDec = function(num, n){ | |
if (n === undefined){ | |
var n = 2 | |
} | |
var sig = parseFloat(num.toPrecision(n)).toLocaleString(), | |
dec = parseFloat(num.toFixed(n)).toLocaleString(); | |
//toLocaleString is behaving funny on node. | |
//It should be inserting commas (I'm definitely in an english locale), and | |
//it's not. :( | |
return [sig, dec] | |
} | |
var num = 123456.123456, | |
expected = ['120000', '123456.12'], | |
returned = compareSigDec(num); | |
console.log(expected, returned); | |
assert.equal(expected[0], returned[0]) | |
assert.equal(expected[1], returned[1]) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment