diff --git a/manuscript/markdown/Functions/args-again.md b/manuscript/markdown/Functions/args-again.md index 0d4113b..c863509 100644 --- a/manuscript/markdown/Functions/args-again.md +++ b/manuscript/markdown/Functions/args-again.md @@ -4,46 +4,57 @@ As we've discussed, when a function is applied to arguments (or "called"), JavaS You should never attempt to define your own bindings against these names. Consider them read-only at all times. The first is called `this` and it is bound to something called the function's [context](#context). We will explore that when we start discussing objects and classes. The second is very interesting, it's called `arguments`, and the most interesting thing about it is that it contains a list of arguments passed to the function: - function plus (a, b) { - return arguments[0] + arguments[1] - } - - plus(2,3) - //=> 5 - +{:lang="js"} +~~~~~~~~ +function plus (a, b) { + return arguments[0] + arguments[1] +} + +plus(2,3) + //=> 5 +~~~~~~~~ Although `arguments` looks like an array, it isn't an array:[^pojo] It's more like an object[^pojo] that happens to bind some values to properties with names that look like integers starting with zero: - function args (a, b) { - return arguments - } - - args(2,3) - //=> { '0': 2, '1': 3 } +{:lang="js"} +~~~~~~~~ +function args (a, b) { + return arguments +} + +args(2,3) + //=> { '0': 2, '1': 3 } +~~~~~~~~ `arguments` always contains all of the arguments passed to a function, regardless of how many are declared. Therefore, we can write `plus` like this: - function plus () { - return arguments[0] + arguments[1] - } - - plus(2,3) - //=> 5 +{:lang="js"} +~~~~~~~~ +function plus () { + return arguments[0] + arguments[1] +} + +plus(2,3) + //=> 5 +~~~~~~~~ When discussing objects, we'll discuss properties in more depth. Here's something interesting about `arguments`: - function howMany () { - return arguments['length'] - } - - howMany() - //=> 0 - - howMany('hello') - //=> 1 - - howMany('sharks', 'are', 'apex', 'predators') - //=> 4 - +{:lang="js"} +~~~~~~~~ +function howMany () { + return arguments['length'] +} + +howMany() + //=> 0 + +howMany('hello') + //=> 1 + +howMany('sharks', 'are', 'apex', 'predators') + //=> 4 +~~~~~~~~ + The most common use of the `arguments` binding is to build functions that can take a variable number of arguments. We'll see it used in many of the recipes, starting off with [partial application](#simple-partial) and [ellipses](#ellipses). - -[^pojo]: We'll look at [arrays](#arrays) and [plain old javascript objects](#objects) in depth later. \ No newline at end of file + +[^pojo]: We'll look at [arrays](#arrays) and [plain old javascript objects](#objects) in depth later. diff --git a/manuscript/markdown/Functions/args.md b/manuscript/markdown/Functions/args.md index 9e2191f..43ab8b7 100644 --- a/manuscript/markdown/Functions/args.md +++ b/manuscript/markdown/Functions/args.md @@ -8,28 +8,43 @@ A> Most programmers are perfectly familiar with arguments (often called "paramet Let's make a function with an argument: - function (room) {} - +{:lang="js"} +~~~~~~~~ +function (room) {} +~~~~~~~~ + This function has one argument, `room`, and no body. Here's a function with two arguments and no body: - function (room, board) {} - +{:lang="js"} +~~~~~~~~ +function (room, board) {} +~~~~~~~~ + I'm sure you are perfectly comfortable with the idea that this function has two arguments, `room`, and `board`. What does one do with the arguments? Use them in the body, of course. What do you think this is? - function (diameter) { return diameter * 3.14159265 } +{:lang="js"} +~~~~~~~~ +function (diameter) { return diameter * 3.14159265 } +~~~~~~~~ It's a function for calculating the circumference of a circle given the diameter. I read that aloud as "When applied to a value representing the diameter, this function *returns* the diameter times 3.14159265." Remember that to apply a function with no arguments, we wrote `(function () {})()`. To apply a function with an argument (or arguments), we put the argument (or arguments) within the parentheses, like this: - (function (diameter) { return diameter * 3.14159265 })(2) - //=> 6.2831853 - +{:lang="js"} +~~~~~~~~ +(function (diameter) { return diameter * 3.14159265 })(2) + //=> 6.2831853 +~~~~~~~~ + You won't be surprised to see how to write and apply a function to two arguments: - (function (room, board) { return room + board })(800, 150) - //=> 950 - +{:lang="js"} +~~~~~~~~ +(function (room, board) { return room + board })(800, 150) + //=> 950 +~~~~~~~~ + T> ### a quick summary of functions and bodies T> T> How arguments are used in a body's expression is probably perfectly obvious to you from the examples, especially if you've used any programming language (except for the dialect of BASIC--which I recall from my secondary school--that didn't allow parameters when you called a procedure). @@ -50,8 +65,11 @@ Like most contemporary programming languages, JavaScript uses the "call by value So when you write: - (function (diameter) { return diameter * 3.14159265 })(1 + 1) - //=> 6.2831853 +{:lang="js"} +~~~~~~~~ +(function (diameter) { return diameter * 3.14159265 })(1 + 1) + //=> 6.2831853 +~~~~~~~~ What happened internally is that the expression `1 + 1` was evaluated first, resulting in `2`. Then our circumference function was applied to `2`.[^f2f] @@ -61,20 +79,26 @@ What happened internally is that the expression `1 + 1` was evaluated first, res Right now everything looks simple and straightforward, and we can move on to talk about arguments in more detail. And we're going to work our way up from `function (diameter) { return diameter * 3.14159265 }` to functions like: - function (x) { return (function (y) { return x }) } - +{:lang="js"} +~~~~~~~~ +function (x) { return (function (y) { return x }) } +~~~~~~~~ + A> `function (x) { return (function (y) { return x }) }` just looks crazy, as if we are learning English as a second language and the teacher promises us that soon we will be using words like *antidisestablishmentarianism*. Besides a desire to use long words to sound impressive, this is not going to seem attractive until we find ourselves wanting to discuss the role of the Church of England in 19th century British politics. A> A> But there's another reason for learning the word *antidisestablishmentarianism*: We might learn how prefixes and postfixes work in English grammar. It's the same thing with `function (x) { return (function (y) { return x }) }`. It has a certain important meaning in its own right, and it's also an excellent excuse to learn about functions that make functions, environments, variables, and more. - + In order to talk about how this works, we should agree on a few terms (you may already know them, but let's check-in together and "synchronize our dictionaries"). The first `x`, the one in `function (x) ...`, is an *argument*. The `y` in `function (y) ...` is another argument. The second `x`, the one in `{ return x }`, is not an argument, *it's an expression referring to a variable*. Arguments and variables work the same way whether we're talking about `function (x) { return (function (y) { return x }) }` or just plain `function (x) { return x }`. Every time a function is invoked ("invoked" means "applied to zero or more arguments"), a new *environment* is created. An environment is a (possibly empty) dictionary that maps variables to values by name. The `x` in the expression that we call a "variable" is itself an expression that is evaluated by looking up the value in the environment. How does the value get put in the environment? Well for arguments, that is very simple. When you apply the function to the arguments, an entry is placed in the dictionary for each argument. So when we write: - (function (x) { return x })(2) - //=> 2 +{:lang="js"} +~~~~~~~~ +(function (x) { return x })(2) + //=> 2 +~~~~~~~~ What happens is this: @@ -107,10 +131,13 @@ Because many references can share the same value, and because JavaScript passes And with that, we're ready to look at *closures*. When we combine our knowledge of value types, reference types, arguments, and closures, we'll understand why this function always evaluates to `true` no matter what argument[^NaNPedantry] you apply it to: - function (value) { - return (function (copy) { - return copy === value - })(value) - } +{:lang="js"} +~~~~~~~~ +function (value) { + return (function (copy) { + return copy === value + })(value) +} +~~~~~~~~ [^NaNPedantry]: Unless the argument is NaN, which isn't equal to anything, including itself diff --git a/manuscript/markdown/Functions/buildingblocks.md b/manuscript/markdown/Functions/buildingblocks.md index b22f0da..d4692a1 100644 --- a/manuscript/markdown/Functions/buildingblocks.md +++ b/manuscript/markdown/Functions/buildingblocks.md @@ -6,66 +6,87 @@ When you look at functions within functions in JavaScript, there's a bit of a "s One of the most basic of these building blocks is *composition*: - function cookAndEat (food) { - return eat(cook(food)) - } - +{:lang="js"} +~~~~~~~~ +function cookAndEat (food) { + return eat(cook(food)) +} +~~~~~~~~ + It's really that simple: Whenever you are chaining two or more functions together, you're composing them. You can compose them with explicit JavaScript code as we've just done. You can also generalize composition with the B Combinator or "compose" that we saw in [Combinators and Decorators](#combinators): - function compose (a, b) { - return function (c) { - return a(b(c)) - } - } +{:lang="js"} +~~~~~~~~ +function compose (a, b) { + return function (c) { + return a(b(c)) + } +} + +var cookAndEat = compose(eat, cook); +~~~~~~~~ - var cookAndEat = compose(eat, cook); - If that was all there was to it, composition wouldn't matter much. But like many patterns, using it when it applies is only 20% of the benefit. The other 80% comes from organizing your code such that you can use it: Writing functions that can be composed in various ways. In the recipes, we'll look at a decorator called [once](#once): It ensures that a function can only be executed once. Thereafter, it does nothing. Once is useful for ensuring that certain side effects are not repeated. We'll also look at [maybe](#maybe): It ensures that a function does nothing if it is given nothing (like `null` or `undefined`) as an argument. Of course, you needn't use combinators to implement either of these ideas, you can use if statements. But `once` and `maybe` compose, so you can chain them together as you see fit: - function actuallyTransfer(from, to, amount) { - // do something - } - - var invokeTransfer = once(maybe(actuallyTransfer(...))); - +{:lang="js"} +~~~~~~~~ +function actuallyTransfer(from, to, amount) { + // do something +} + +var invokeTransfer = once(maybe(actuallyTransfer(...))); +~~~~~~~~ + ### partial application Another basic building block is *partial application*. When a function takes multiple arguments, we "apply" the function to the arguments by evaluating it with all of the arguments, producing a value. But what if we only supply some of the arguments? In that case, we can't get the final value, but we can get a function that represents *part* of our application. Code is easier than words for this. The [Underscore] library provides a higher-order function called *map*.[^headache] It applies another function to each element of an array, like this: - _.map([1, 2, 3], function (n) { return n * n }) - //=> [1, 4, 9] - +{:lang="js"} +~~~~~~~~ +_.map([1, 2, 3], function (n) { return n * n }) + //=> [1, 4, 9] +~~~~~~~~ + This code implements a partial application of the map function by applying the function `function (n) { return n * n }` as its second argument: - function squareAll (array) { - return _.map(array, function (n) { return n * n }) - } +{:lang="js"} +~~~~~~~~ +function squareAll (array) { + return _.map(array, function (n) { return n * n }) +} +~~~~~~~~ The resulting function--`squareAll`--is still the map function, it's just that we've applied one of its two arguments already. `squareAll` is nice, but why write one function every time we want to partially apply a function to a map? We can abstract this one level higher. `mapWith` takes any function as an argument and returns a partially applied map function. - function mapWith (fn) { - return function (array) { - return _.map(array, fn) - } - } - - var squareAll = mapWith(function (n) { return n * n }); - - squareAll([1, 2, 3]) - //=> [1, 4, 9] +{:lang="js"} +~~~~~~~~ +function mapWith (fn) { + return function (array) { + return _.map(array, fn) + } +} + +var squareAll = mapWith(function (n) { return n * n }); + +squareAll([1, 2, 3]) + //=> [1, 4, 9] +~~~~~~~~ We'll discuss mapWith again in [the recipes](#mapWith). The important thing to see is that partial application is orthogonal to composition, and that they both work together nicely: - var safeSquareAll = mapWith(maybe(function (n) { return n * n })); - - safeSquareAll([1, null, 2, 3]) - //=> [1, null, 4, 9] +{:lang="js"} +~~~~~~~~ +var safeSquareAll = mapWith(maybe(function (n) { return n * n })); + +safeSquareAll([1, null, 2, 3]) + //=> [1, null, 4, 9] +~~~~~~~~ We generalized composition with the `compose` combinator. Partial application also has a combinator, which we'll see in the [partial](#partial) recipe. diff --git a/manuscript/markdown/Functions/closures.md b/manuscript/markdown/Functions/closures.md index 47a3368..4804595 100644 --- a/manuscript/markdown/Functions/closures.md +++ b/manuscript/markdown/Functions/closures.md @@ -2,33 +2,45 @@ It's time to see how a function within a function works: - (function (x) { - return function (y) { - return x - } - })(1)(2) - //=> 1 +{:lang="js"} +~~~~~~~~ +(function (x) { + return function (y) { + return x + } +})(1)(2) + //=> 1 +~~~~~~~~ First off, let's use what we learned above. Given `(`*some function*`)(`*some argument*`)`, we know that we apply the function to the argument, create an environment, bind the value of the argument to the name, and evaluate the function's expression. So we do that first with this code: - (function (x) { - return function (y) { - return x - } - })(1) - //=> [Function] - +{:lang="js"} +~~~~~~~~ +(function (x) { + return function (y) { + return x + } +})(1) + //=> [Function] +~~~~~~~~ + The environment belonging to the function with signature `function (x) ...` becomes `{x: 1, ...}`, and the result of applying the function is another function value. It makes sense that the result value is a function, because the expression for `function (x) ...`'s body is: - function (y) { - return x - } +{:lang="js"} +~~~~~~~~ +function (y) { + return x +} +~~~~~~~~ So now we have a value representing that function. Then we're going to take the value of that function and apply it to the argument `2`, something like this: - (function (y) { - return x - })(2) +{:lang="js"} +~~~~~~~~ +(function (y) { + return x +})(2) +~~~~~~~~ So we seem to get a new environment `{y: 2, ...}`. How is the expression `x` going to be evaluated in that function's environment? There is no `x` in its environment, it must come from somewhere else. @@ -42,26 +54,29 @@ A> Now let's enjoy a relaxed Allongé before we continue! The function `function (y) { return x }` is interesting. It contains a *free variable*, `x`.[^nonlocal] A free variable is one that is not bound within the function. Up to now, we've only seen one way to "bind" a variable, namely by passing in an argument with the same name. Since the function `function (y) { return x }` doesn't have an argument named `x`, the variable `x` isn't bound in this function, which makes it "free." -[^nonlocal]: You may also hear the term "non-local variable." [Both are correct.](https://en.wikipedia.org/wiki/Free_variables_and_bound_variables) +[^nonlocal]: You may also hear the term "non-local variable." [Both are correct.](https://en.wikipedia.org/wiki/Free_variables_and_bound_variables) Now that we know that variables used in a function are either bound or free, we can bifurcate functions into those with free variables and those without: * Functions containing no free variables are called *pure functions*. * Functions containing one or more free variables are called *closures*. - + Pure functions are easiest to understand. They always mean the same thing wherever you use them. Here are some pure functions we've already seen: - function () {} - - function (x) { - return x - } - - function (x) { - return function (y) { - return x - } - } +{:lang="js"} +~~~~~~~~ +function () {} + +function (x) { + return x +} + +function (x) { + return function (y) { + return x + } +} +~~~~~~~~ The first function doesn't have any variables, therefore doesn't have any free variables. The second doesn't have any free variables, because its only variable is bound. The third one is actually two functions, one inside the other. `function (y) ...` has a free variable, but the entire expression refers to `function (x) ...`, and it doesn't have a free variable: The only variable anywhere in its body is `x`, which is certainly bound within `function (x) ...`. @@ -86,19 +101,25 @@ A> `function (x) { return x }` is called the I Combinator or Identity Function. Functions can have grandparents too: - function (x) { - return function (y) { - return function (z) { - return x + y + z - } - } +{:lang="js"} +~~~~~~~~ +function (x) { + return function (y) { + return function (z) { + return x + y + z } + } +} +~~~~~~~~ This function does much the same thing as: - function (x, y, z) { - return x + y + z - } +{:lang="js"} +~~~~~~~~ +function (x, y, z) { + return x + y + z +} +~~~~~~~~ Only you call it with `(1)(2)(3)` instead of `(1, 2, 3)`. The other big difference is that you can call it with `(1)` and get a function back that you can later call with `(2)(3)`. @@ -113,24 +134,30 @@ A> The first function is the result of [currying] the second function. Calling a An interesting thing happens when a variable has the same name as an ancestor environment's variable. Consider: - function (x) { - return function (x, y) { - return x + y - } - } +{:lang="js"} +~~~~~~~~ +function (x) { + return function (x, y) { + return x + y + } +} +~~~~~~~~ The function `function (x, y) { return x + y }` is a pure function, because its `x` is defined within its own environment. Although its parent also defines an `x`, it is ignored when evaluating `x + y`. JavaScript always searches for a binding starting with the functions own environment and then each parent in turn until it finds one. The same is true of: - function (x) { - return function (x, y) { - return function (w, z) { - return function (w) { - return x + y + z - } - } +{:lang="js"} +~~~~~~~~ +function (x) { + return function (x, y) { + return function (w, z) { + return function (w) { + return x + y + z } } - + } +} +~~~~~~~~ + When evaluating `x + y + z`, JavaScript will find `x` and `y` in the great-grandparent scope and `z` in the parent scope. The `x` in the great-great-grandparent scope is ignored, as are both `w`s. When a variable has the same name as an ancestor environment's binding, it is said to *shadow* the ancestor. This is often a good thing. @@ -145,12 +172,15 @@ JavaScript always has the notion of at least one environment we do not control: Sometimes, programmers wish to avoid this. If you don't want your code to operate directly within the global environment, what can you do? Create an environment for them, of course. Many programmers choose to write every JavaScript file like this: - // top of the file - (function () { - - // ... lots of JavaScript ... - - })(); - // bottom of the file +{:lang="js"} +~~~~~~~~ +// top of the file +(function () { + + // ... lots of JavaScript ... + +})(); +// bottom of the file +~~~~~~~~ The effect is to insert a new, empty environment in between the global environment and your own functions: `{x: 1, '..': {'..': `*global environment*`}}`. As we'll see when we discuss mutable state, this helps to prevent programmers from accidentally changing the global state that is shared by code in every file when they use the [var keyword](#var) properly. diff --git a/manuscript/markdown/Functions/combinators.md b/manuscript/markdown/Functions/combinators.md index 441bd48..abbecb8 100644 --- a/manuscript/markdown/Functions/combinators.md +++ b/manuscript/markdown/Functions/combinators.md @@ -6,24 +6,27 @@ As we've seen, JavaScript functions take values as arguments and return values. Here's a very simple higher-order function that takes a function as an argument: - function repeat (num, fn) { - var i, value; - - for (i = 1; i <= num; ++i) - value = fn(i); - - return value; - } - - repeat(3, function () { - console.log('Hello') - }) - //=> - 'Hello' - 'Hello' - 'Hello' - undefined - +{:lang="js"} +~~~~~~~~ +function repeat (num, fn) { + var i, value; + + for (i = 1; i <= num; ++i) + value = fn(i); + + return value; +} + +repeat(3, function () { + console.log('Hello') +}) + //=> + 'Hello' + 'Hello' + 'Hello' + undefined +~~~~~~~~ + Higher-order functions dominate *JavaScript Allongé*. But before we go on, we'll talk about some specific types of higher-order functions. ### combinators @@ -42,32 +45,44 @@ In this book, we will be using a looser definition of "combinator:" Higher-order Let's start with a useful combinator: Most programmers call it *Compose*, although the logicians call it the B combinator or "Bluebird." Here is the typical[^bluebird] programming implementation: - function compose (a, b) { - return function (c) { - return a(b(c)) - } - } +{:lang="js"} +~~~~~~~~ +function compose (a, b) { + return function (c) { + return a(b(c)) + } +} +~~~~~~~~ Let's say we have: - function addOne (number) { - return number + 1 - } - - function doubleOf (number) { - return number * 2 - } +{:lang="js"} +~~~~~~~~ +function addOne (number) { + return number + 1 +} + +function doubleOf (number) { + return number * 2 +} +~~~~~~~~ With `compose`, anywhere you would write - function doubleOfAddOne (number) { - return doubleOf(addOne(number)) - } - +{:lang="js"} +~~~~~~~~ +function doubleOfAddOne (number) { + return doubleOf(addOne(number)) +} +~~~~~~~~ + You could also write: - var doubleOfAddOne = compose(doubleOf, addOne); - +{:lang="js"} +~~~~~~~~ +var doubleOfAddOne = compose(doubleOf, addOne); +~~~~~~~~ + This is, of course, just one example of many. You'll find lots more perusing the recipes in this book. While some programmers believe "There Should Only Be One Way To Do It," having combinators available as well as explicitly writing things out with lots of symbols and keywords has some advantages when used judiciously. ### a balanced statement about combinators @@ -78,27 +93,39 @@ Code that uses a lot of combinators tends to name the verbs and adverbs (like `d A *function decorator* is a higher-order function that takes one function as an argument, returns another function, and the returned function is a variation of the argument function. Here's a ridiculous example of a decorator: - function not (fn) { - return function (argument) { - return !fn(argument) - } - } +{:lang="js"} +~~~~~~~~ +function not (fn) { + return function (argument) { + return !fn(argument) + } +} +~~~~~~~~ So instead of writing `!someFunction(42)`, you can write `not(someFunction)(42)`. Hardly progress. But like `compose`, you could write either - function something (x) { - return x != null - } +{:lang="js"} +~~~~~~~~ +function something (x) { + return x != null +} +~~~~~~~~ And elsewhere, he writes: - function nothing (x) { - return !something(x) - } +{:lang="js"} +~~~~~~~~ +function nothing (x) { + return !something(x) +} +~~~~~~~~ Or: - var nothing = not(something); +{:lang="js"} +~~~~~~~~ +var nothing = not(something); +~~~~~~~~ `not` is a function decorator because it modifies a function while remaining strongly related to the original function's semantics. You'll see other function decorators in the recipes, like [once](#once), [mapWith](#mapWith), and [maybe](#maybe). Function decorators aren't strict about being pure functions, so there's more latitude for making decorators than combinators. diff --git a/manuscript/markdown/Functions/letvar.md b/manuscript/markdown/Functions/letvar.md index bbb055e..fcd3d28 100644 --- a/manuscript/markdown/Functions/letvar.md +++ b/manuscript/markdown/Functions/letvar.md @@ -4,40 +4,55 @@ Up to now, all we've really seen are *anonymous functions*, functions that don't There are other ways to name things in JavaScript, but before we learn some of those, let's see how to use what we already have to name things. Let's revisit a very simple example: - function (diameter) { - return diameter * 3.14159265 - } +{:lang="js"} +~~~~~~~~ +function (diameter) { + return diameter * 3.14159265 +} +~~~~~~~~ What is this "3.14159265" number? [Pi], obviously. We'd like to name it so that we can write something like: - function (diameter) { - return diameter * Pi - } +{:lang="js"} +~~~~~~~~ +function (diameter) { + return diameter * Pi +} +~~~~~~~~ In order to bind `3.14159265` to the name `Pi`, we'll need a function with a parameter of `Pi` applied to an argument of `3.14159265`. If we put our function expression in parentheses, we can apply it to the argument of `3.14159265`: - (function (Pi) { - return ???? - })(3.14159265) +{:lang="js"} +~~~~~~~~ +(function (Pi) { + return ???? +})(3.14159265) +~~~~~~~~ What do we put inside our new function that binds `3.14159265` to the name `Pi` when evaluated? Our circumference function, of course: [Pi]: https://en.wikipedia.org/wiki/Pi - (function (Pi) { - return function (diameter) { - return diameter * Pi - } - })(3.14159265) +{:lang="js"} +~~~~~~~~ +(function (Pi) { + return function (diameter) { + return diameter * Pi + } +})(3.14159265) +~~~~~~~~ This expression, when evaluated, returns a function that calculates circumferences. It differs from our original in that it names the constant `Pi`. Let's test it: - (function (Pi) { - return function (diameter) { - return diameter * Pi - } - })(3.14159265)(2) - //=> 6.2831853 +{:lang="js"} +~~~~~~~~ +(function (Pi) { + return function (diameter) { + return diameter * Pi + } +})(3.14159265)(2) + //=> 6.2831853 +~~~~~~~~ That works! We can bind anything we want in an expression by wrapping it in a function that is immediately invoked with the value we want to bind. @@ -45,9 +60,12 @@ That works! We can bind anything we want in an expression by wrapping it in a fu JavaScript programmers regularly use the idea of writing an expression that denotes a function and then immediately applying it to arguments. Explaining the pattern, Ben Alman coined the term [Immediately Invoked Function Expression][iife] for it, often abbreviated "IIFE." As we'll see in a moment, an IIFE need not have parameters: - (function () { - // ... do something here... - })(); +{:lang="js"} +~~~~~~~~ +(function () { + // ... do something here... +})(); +~~~~~~~~ When an IIFE binds values to names (as we did above with `Pi`), retro-grouch programmers often call it "let."[^let] And confusing the issue, upcoming versions of JavaScript have support for a `let` keyword that has a similar binding behaviour. @@ -59,47 +77,62 @@ Yes. Another way to write our "circumference" function would be to pass `Pi` along with the diameter argument, something like this: - function (diameter, Pi) { - return diameter * Pi - } +{:lang="js"} +~~~~~~~~ +function (diameter, Pi) { + return diameter * Pi +} +~~~~~~~~ And you could use it like this: - (function (diameter, Pi) { - return diameter * Pi - })(2, 3.14159265) - //=> 6.2831853 +{:lang="js"} +~~~~~~~~ +(function (diameter, Pi) { + return diameter * Pi +})(2, 3.14159265) + //=> 6.2831853 +~~~~~~~~ This differs from our example above in that there is only one environment, rather than two. We have one binding in the environment representing our regular argument, and another our "constant." That's more efficient, and it's *almost* what we wanted all along: A way to bind `3.14159265` to a readable name. JavaScript gives us a way to do that, the `var` keyword. We'll learn a lot more about `var` in future chapters, but here's the most important thing you can do with `var`: - function (diameter) { - var Pi = 3.14159265; +{:lang="js"} +~~~~~~~~ +function (diameter) { + var Pi = 3.14159265; - return diameter * Pi - } + return diameter * Pi +} +~~~~~~~~ The `var` keyword introduces one or more bindings in the current function's environment. It works just as we want: - (function (diameter) { - var Pi = 3.14159265; +{:lang="js"} +~~~~~~~~ +(function (diameter) { + var Pi = 3.14159265; - return diameter * Pi - })(2) - //=> 6.2831853 + return diameter * Pi +})(2) + //=> 6.2831853 +~~~~~~~~ You can bind any expression. Functions are expressions, so you can bind helper functions: - function (d) { - var calc = function (diameter) { - var Pi = 3.14159265; +{:lang="js"} +~~~~~~~~ +function (d) { + var calc = function (diameter) { + var Pi = 3.14159265; - return diameter * Pi - }; + return diameter * Pi + }; - return "The circumference is " + calc(d) - } + return "The circumference is " + calc(d) +} +~~~~~~~~ Notice `calc(d)`? This underscores what we've said: if you have an expression that evaluates to a function, you apply it with `()`. A name that's bound to a function is a valid expression evaluating to a function.[^namedfn] @@ -109,33 +142,39 @@ A> Amazing how such an important idea--naming functions--can be explained *en pa You can bind more than one name-value pair by separating them with commas. For readability, most people put one binding per line: - function (d) { - var Pi = 3.14159265, - calc = function (diameter) { - return diameter * Pi - }; +{:lang="js"} +~~~~~~~~ +function (d) { + var Pi = 3.14159265, + calc = function (diameter) { + return diameter * Pi + }; - return "The circumference is " + calc(d) - } + return "The circumference is " + calc(d) +} +~~~~~~~~ These examples use the `var` keyword to bind names in the same environment as our function. We can also create a new scope using an IIFE if we wish to bind some names in part of a function: - function foobar () { +{:lang="js"} +~~~~~~~~ +function foobar () { - // do something without foo or bar + // do something without foo or bar - (function () { - var foo = 'foo', - bar = 'bar'; + (function () { + var foo = 'foo', + bar = 'bar'; - // ... do something with foo and bar ... + // ... do something with foo and bar ... - })(); + })(); - // do something else without foo or bar + // do something else without foo or bar - } +} +~~~~~~~~ [^let]: To be pedantic, both main branches of Lisp today define a special construct called "let." One, Scheme, [uses `define-syntax` to rewrite `let` into an immediately invoked function expression that binds arguments to values](https://en.wikipedia.org/wiki/Scheme_(programming_language)#Minimalism) as shown above. The other, Common Lisp, leaves it up to implementations to decide how to implement `let`. -[iife]: http://www.benalman.com/news/2010/11/immediately-invoked-function-expression/ \ No newline at end of file +[iife]: http://www.benalman.com/news/2010/11/immediately-invoked-function-expression/ diff --git a/manuscript/markdown/Functions/little.md b/manuscript/markdown/Functions/little.md index ae850fe..7f1fc52 100644 --- a/manuscript/markdown/Functions/little.md +++ b/manuscript/markdown/Functions/little.md @@ -2,13 +2,19 @@ In JavaScript, functions are values, but they are also much more than simple numbers, strings, or even complex data structures like trees or maps. Functions represent computations to be performed. Like numbers, strings, and arrays, they have a representation. Let's start with the very simplest possible function. In JavaScript, it looks like this: - function () {} - +{:lang="js"} +~~~~~~~~ +function () {} +~~~~~~~~ + This is a function that is applied to no values and produces no value. How do we represent "no value" in JavaScript? We'll find out in a minute. First, let's verify that our function is a value: - (function () {}) - //=> [Function] - +{:lang="js"} +~~~~~~~~ +(function () {}) + //=> [Function] +~~~~~~~~ + What!? Why didn't it type back `function () {}` for us? This *seems* to break our rule that if an expression is also a value, JavaScript will give the same value back to us. What's going on? The simplest and easiest answer is that although the JavaScript interpreter does indeed return that value, displaying it on the screen is a slightly different matter. `[Function]` is a choice made by the people who wrote Node.js, the JavaScript environment that hosts the JavaScript REPL. If you try the same thing in a browser, you'll see the code you typed. {pagebreak} @@ -21,9 +27,12 @@ You recall that we have two types of values with respect to identity: Value type Which kind are functions? Let's try it. For reasons of appeasing the JavaScript parser, we'll enclose our functions in parentheses: - (function () {}) === (function () {}) - //=> false - +{:lang="js"} +~~~~~~~~ +(function () {}) === (function () {}) + //=> false +~~~~~~~~ + Like arrays, every time you evaluate an expression to produce a function, you get a new function that is not identical to any other function, even if you use the same expression to generate it. "Function" is a reference type. ### applying functions @@ -32,12 +41,15 @@ Let's put functions to work. The way we use functions is to *apply* them to zero Here's how we apply a function to some values in JavaScript: Let's say that *fn_expr* is an expression that when evaluated, produces a function. Let's call the arguments *args*. Here's how to apply a function to some arguments: - *fn_expr*`(`*args*`)` - + *fn_expr*`(`*args*`)` + Right now, we only know about one such expression: `function () {}`, so let's use it. We'll put it in parentheses[^ambiguous] to keep the parser happy, like we did above: `(function () {})`. Since we aren't giving it any arguments, we'll simply write `()` after the expression. So we write: - (function () {})() - //=> undefined +{:lang="js"} +~~~~~~~~ +(function () {})() + //=> undefined +~~~~~~~~ What is this `undefined`? @@ -49,20 +61,26 @@ What is this `undefined`? In JavaScript, the absence of a value is written `undefined`, and it means there is no value. It will crop up again. `undefined` is its own type of value, and it acts like a value type: - undefined - //=> undefined +{:lang="js"} +~~~~~~~~ +undefined + //=> undefined +~~~~~~~~ Like numbers, booleans and strings, JavaScript can print out the value `undefined`. - undefined === undefined - //=> true - (function () {})() === (function () {})() - //=> true - (function () {})() === undefined - //=> true - +{:lang="js"} +~~~~~~~~ +undefined === undefined + //=> true +(function () {})() === (function () {})() + //=> true +(function () {})() === undefined + //=> true +~~~~~~~~ + No matter how you evaluate `undefined`, you get an identical value back. `undefined` is a value that means "I don't have a value." But it's still a value :-) - + A> You might think that `undefined` in JavaScript is equivalent to `NULL` in SQL. No. In SQL, two things that are `NULL` are not equal to nor share the same identity, because two unknowns can't be equal. In JavaScript, every `undefined` is identical to every other `undefined`. ### void @@ -74,13 +92,16 @@ We've seen that JavaScript represents an undefined value by typing `undefined`, There's a third way, with JavaScript's `void` operator. Behold: - void 0 - //=> undefined - void 1 - //=> undefined - void (2 + 2) - //=> undefined - +{:lang="js"} +~~~~~~~~ +void 0 + //=> undefined +void 1 + //=> undefined +void (2 + 2) + //=> undefined +~~~~~~~~ + `void` is an operator that takes any value and evaluates to `undefined`, always. So, when we deliberately want an undefined value, should we use the first, second, or third form?[^fourth] The answer is, use `void`. By convention, use `void 0`. The first form works but it's cumbersome. The second form works most of the time, but it is possible to break it by reassigning `undefined` to a different value, something we'll discuss in [Reassignment and Mutation](#reassignment). The third form is guaranteed to always work, so that's what we will use.[^void] @@ -93,8 +114,11 @@ The first form works but it's cumbersome. The second form works most of the time Back to our function. We evaluated this: - (function () {})() - //=> undefined +{:lang="js"} +~~~~~~~~ +(function () {})() + //=> undefined +~~~~~~~~ Let's recall that we were applying the function `function () {}` to no arguments (because there was nothing inside of `()`). So how do we know to expect `undefined`? That's easy: @@ -108,43 +132,52 @@ We haven't discussed these *statements*. What's a statement? There are many kinds of JavaScript statements, but the first kind is one we've already met. An expression is a JavaScript statement. Although they aren't very practical, the following are all valid JavaScript functions, and they all evaluate to undefined when applied: - (function () { 2 + 2 }) - - (function () { 1 + 1; 2 + 2 }) - +{:lang="js"} +~~~~~~~~ +(function () { 2 + 2 }) + +(function () { 1 + 1; 2 + 2 }) +~~~~~~~~ + You can also separate statements with line breaks.[^asi] The convention is to use some form of consistent indenting: +{:lang="js"} +~~~~~~~~ +(function () { + 1 + 1; + 2 + 2 +}) + +(function () { + (function () { (function () { - 1 + 1; - 2 + 2 - }) - - (function () { - (function () { - (function () { - (function () { - }) - }) - }); (function () { - }) + }) }) - + }); + (function () { + }) +}) +~~~~~~~~ + That last one's a doozy, but since a function body can contain a statement, and a statement can be an expression, and a function is an expression.... You get the idea. [^asi]: Readers who follow internet flame-fests may be aware of something called [automatic semi-colon insertion](http://lucumr.pocoo.org/2011/2/6/automatic-semicolon-insertion/). Basically, there's a step where JavaScript looks at your code and follows some rules to guess where you meant to put semicolons in should you leave them out. This feature was originally created as a kind of helpful error-correction. Some programmers argue that since it's part of the language's definition, it's fair game to write code that exploits it, so they deliberately omit any semicolon that JavaScript will insert for them. So how do we get a function to return a value when applied? With the `return` keyword and any expression: - (function () { return 0 })() - //=> 0 - - (function () { return 1 })() - //=> 1 - - (function () { return 'Hello ' + 'World' })() - // 'Hello World' - +{:lang="js"} +~~~~~~~~ +(function () { return 0 })() + //=> 0 + +(function () { return 1 })() + //=> 1 + +(function () { return 'Hello ' + 'World' })() + // 'Hello World' +~~~~~~~~ + The `return` keyword creates a return statement that immediately terminates the function application and returns the result of evaluating its expression. ### functions that evaluate to functions @@ -153,29 +186,38 @@ If an expression that evaluates to a function is, well, an expression, and if a Yes: - function () { - return (function () {}) - } - +{:lang="js"} +~~~~~~~~ +function () { + return (function () {}) +} +~~~~~~~~ + That's a function! It's a function that when applied, evaluates to a function that when applied, evaluates to `undefined`.[^mouthful] Let's use a simpler terminology. Instead of saying "that when applied, evaluates to \_\_\_\_\_," we will say "gives \_\_\_\_\_." And instead of saying "gives undefined," we'll say "doesn't give anything." So we have *a function, that gives a function, that doesn't give anything*. Likewise: - function () { - return (function () { - return true - }) - } - +{:lang="js"} +~~~~~~~~ +function () { + return (function () { + return true + }) +} +~~~~~~~~ + That's a function, that gives a function, that gives `true`: - (function () { - return (function () { - return true - }) - })()() - //=> true - +{:lang="js"} +~~~~~~~~ +(function () { + return (function () { + return true + }) +})()() + //=> true +~~~~~~~~ + Well. We've been very clever, but so far this all seems very abstract. Diffraction of a crystal is beautiful and interesting in its own right, but you can't blame us for wanting to be shown a practical use for it, like being able to determine the composition of a star millions of light years away. So... In the next chapter, "[I'd Like to Have an Argument, Please](#fargs)," we'll see how to make functions practical. [^mouthful]: What a mouthful! This is why other languages with a strong emphasis on functions come up with syntaxes like ` -> -> undefined` diff --git a/manuscript/markdown/Functions/names.md b/manuscript/markdown/Functions/names.md index 58c5fc6..8f1add1 100644 --- a/manuscript/markdown/Functions/names.md +++ b/manuscript/markdown/Functions/names.md @@ -2,114 +2,150 @@ Let's get right to it. This code does *not* name a function: - var repeat = function (str) { - return str + str - }; - +{:lang="js"} +~~~~~~~~ +var repeat = function (str) { + return str + str +}; +~~~~~~~~ + It doesn't name the function "repeat" for the same reason that `var answer = 42` doesn't name the number `42`. That snippet of code binds an anonymous function to a name in an environment, but the function itself remains anonymous. JavaScript *does* have a syntax for naming a function, it looks like this: - var bindingName = function actualName () { - //... - }; +{:lang="js"} +~~~~~~~~ +var bindingName = function actualName () { + //... +}; +~~~~~~~~ In this expression, `bindingName` is the name in the environment, but `actualName` is the function's actual name. This is a *named function expression*. That may seem confusing, but think of the binding names as properties of the environment, not the function itself. And indeed the name *is* a property: - bindingName.name - //=> 'actualName' +{:lang="js"} +~~~~~~~~ +bindingName.name + //=> 'actualName' +~~~~~~~~ In this book we are not examining JavaScript's tooling such as debuggers baked into browsers, but we will note that when you are navigating call stacks in all modern tools, the function's binding name is ignored but its actual name is displayed, so naming functions is very useful even if they don't get a formal binding, e.g. - someBackboneView.on('click', function clickHandler () { - //... - }); +{:lang="js"} +~~~~~~~~ +someBackboneView.on('click', function clickHandler () { + //... +}); +~~~~~~~~ Now, the function's actual name has no effect on the environment in which it is used. To whit: - var bindingName = function actualName () { - //... - }; - - bindingName - //=> [Function: actualName] +{:lang="js"} +~~~~~~~~ +var bindingName = function actualName () { + //... +}; + +bindingName + //=> [Function: actualName] + +actualName + //=> ReferenceError: actualName is not defined +~~~~~~~~ - actualName - //=> ReferenceError: actualName is not defined - So "actualName" isn't bound in the environment where we use the named function expression. Is it bound anywhere else? Yes it is: - var fn = function even (n) { - if (n === 0) { - return true - } - else return !even(n - 1) - } - - fn(5) - //=> false - - fn(2) - //=> true - +{:lang="js"} +~~~~~~~~ +var fn = function even (n) { + if (n === 0) { + return true + } + else return !even(n - 1) +} + +fn(5) + //=> false + +fn(2) + //=> true +~~~~~~~~ + `even` is bound within the function itself, but not outside it. This is useful for making recursive functions. - + ### function declarations We've actually buried the lede.[^lede] Naming functions for the purpose of debugging is not as important as what we're about to discuss. There is another syntax for naming and/or defining a function. It's called a *function declaration*, and it looks like this: - function someName () { - // ... - } - +{:lang="js"} +~~~~~~~~ +function someName () { + // ... +} +~~~~~~~~ + This behaves a *little* like: - var someName = function someName () { - // ... - } - +{:lang="js"} +~~~~~~~~ +var someName = function someName () { + // ... +} +~~~~~~~~ + In that it binds a name in the environment to a named function. However, consider this piece of code: - (function () { - return someName; - - var someName = function someName () { - // ... - } - })() - //=> undefined - +{:lang="js"} +~~~~~~~~ +(function () { + return someName; + + var someName = function someName () { + // ... + } +})() + //=> undefined +~~~~~~~~ + This is what we expect given what we learned about [var](#var): Although `someName` is declared later in the function, JavaScript behaves as if you'd written: - (function () { - var someName; - - return someName; - - someName = function someName () { - // ... - } - })() +{:lang="js"} +~~~~~~~~ +(function () { + var someName; + + return someName; + + someName = function someName () { + // ... + } +})() +~~~~~~~~ What about a function declaration without `var`? - (function () { - return someName; - - function someName () { - // ... - } - })() - //=> [Function: someName] +{:lang="js"} +~~~~~~~~ +(function () { + return someName; + + function someName () { + // ... + } +})() + //=> [Function: someName] +~~~~~~~~ Aha! It works differently, as if you'd written: - (function () { - var someName = function someName () { - // ... - } - return someName; - })() +{:lang="js"} +~~~~~~~~ +(function () { + var someName = function someName () { + // ... + } + return someName; +})() +~~~~~~~~ That difference is intentional on the part of JavaScript's design to facilitate a certain style of programming where you put the main logic up front, and the "helper functions" at the bottom. It is not necessary to declare functions in this way in JavaScript, but understanding the syntax and its behaviour (especially the way it differs from `var`) is essential for working with production code. @@ -117,26 +153,35 @@ That difference is intentional on the part of JavaScript's design to facilitate Function declarations are formally only supposed to be made at what we might call the "top level" of a function. Although some JavaScript environments may permit it, this example is technically illegal and definitely a bad idea: - // function declarations should not happen inside of - // a block and/or be conditionally executed - if (frobbishes.arePizzled()) { - function complainToFactory () { - // ... - } - } +{:lang="js"} +~~~~~~~~ +// function declarations should not happen inside of +// a block and/or be conditionally executed +if (frobbishes.arePizzled()) { + function complainToFactory () { + // ... + } +} +~~~~~~~~ The big trouble with expressions like this is that they may work just fine in your test environment but work a different way in production. Or it may work one way today and a different way when the JavaScript engine is updated, say with a new optimization. Another caveat is that a function declaration cannot exist inside of *any* expression, otherwise it's a function expression. So this is a function declaration: - function trueDat () { return true } +{:lang="js"} +~~~~~~~~ +function trueDat () { return true } +~~~~~~~~ But this is not: - (function trueDat () { return true }) - +{:lang="js"} +~~~~~~~~ +(function trueDat () { return true }) +~~~~~~~~ + The parentheses make this an expression. [^lede]: A lead (or lede) paragraph in literature refers to the opening paragraph of an article, essay, news story or book chapter. In journalism, the failure to mention the most important, interesting or attention-grabbing elements of a story in the first paragraph is sometimes called "burying the lede." -[^caveats]: A number of the caveats discussed here were described in Jyrly Zaytsev's excellent article [Named function expressions demystified](http://kangax.github.com/nfe/). \ No newline at end of file +[^caveats]: A number of the caveats discussed here were described in Jyrly Zaytsev's excellent article [Named function expressions demystified](http://kangax.github.com/nfe/). diff --git a/manuscript/markdown/Functions/recipes/ellipses.md b/manuscript/markdown/Functions/recipes/ellipses.md index 0471e5f..8d74b8b 100644 --- a/manuscript/markdown/Functions/recipes/ellipses.md +++ b/manuscript/markdown/Functions/recipes/ellipses.md @@ -4,106 +4,121 @@ The CoffeeScript programming language has a useful feature: If a parameter of a Here's what it looks like collecting a variable number of arguments and trailing arguments: - callLeft = (fn, args...) -> - (remainingArgs...) -> - fn.apply(this, args.concat(remainingArgs)) +{:lang="js"} +~~~~~~~~ +callLeft = (fn, args...) -> + (remainingArgs...) -> + fn.apply(this, args.concat(remainingArgs)) +~~~~~~~~ These are very handy features. Here's our bogus, made-up attempt to write our own mapper function: - mapper = (fn, elements...) -> - elements.map(fn) +{:lang="js"} +~~~~~~~~ +mapper = (fn, elements...) -> + elements.map(fn) - mapper ((x) -> x*x), 1, 2, 3 - #=> [1, 4, 9] +mapper ((x) -> x*x), 1, 2, 3 + #=> [1, 4, 9] - squarer = callLeft mapper, (x) -> x*x +squarer = callLeft mapper, (x) -> x*x - squarer 1, 2, 3 - #=> [1, 4, 9] +squarer 1, 2, 3 + #=> [1, 4, 9] +~~~~~~~~ JavaScript doesn't support [ellipses](http://en.wikipedia.org/wiki/Ellipsis), those trailing periods CoffeeScript uses to collect arguments into an array. JavaScript is a *functional* language, so here is the recipe for a function that collects trailing arguments into an array for us: - var __slice = Array.prototype.slice; - - function variadic (fn) { - var fnLength = fn.length; - - if (fnLength < 1) { - return fn; - } - else if (fnLength === 1) { - return function () { - return fn.call( - this, __slice.call(arguments, 0)) - } - } - else { - return function () { - var numberOfArgs = arguments.length, - namedArgs = __slice.call( - arguments, 0, fnLength - 1), - numberOfMissingNamedArgs = Math.max( - fnLength - numberOfArgs - 1, 0), - argPadding = new Array(numberOfMissingNamedArgs), - variadicArgs = __slice.call( - arguments, fn.length - 1); - - return fn.apply( - this, namedArgs - .concat(argPadding) - .concat([variadicArgs])); - } - } - }; - - function unary (first) { - return first - } +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function variadic (fn) { + var fnLength = fn.length; - unary('why', 'hello', 'there') - //=> 'why' - - variadic(unary)('why', 'hello', 'there') - //=> [ 'why', 'hello', 'there' ] - - function binary (first, rest) { - return [first, rest] + if (fnLength < 1) { + return fn; + } + else if (fnLength === 1) { + return function () { + return fn.call( + this, __slice.call(arguments, 0)) } + } + else { + return function () { + var numberOfArgs = arguments.length, + namedArgs = __slice.call( + arguments, 0, fnLength - 1), + numberOfMissingNamedArgs = Math.max( + fnLength - numberOfArgs - 1, 0), + argPadding = new Array(numberOfMissingNamedArgs), + variadicArgs = __slice.call( + arguments, fn.length - 1); + + return fn.apply( + this, namedArgs + .concat(argPadding) + .concat([variadicArgs])); + } + } +}; + +function unary (first) { + return first +} + +unary('why', 'hello', 'there') + //=> 'why' + +variadic(unary)('why', 'hello', 'there') + //=> [ 'why', 'hello', 'there' ] + +function binary (first, rest) { + return [first, rest] +} - binary('why', 'hello', 'there') - //=> [ 'why', 'hello' ] +binary('why', 'hello', 'there') + //=> [ 'why', 'hello' ] - variadic(binary)('why', 'hello', 'there') - //=> [ 'why', [ 'hello', 'there' ] ] +variadic(binary)('why', 'hello', 'there') + //=> [ 'why', [ 'hello', 'there' ] ] +~~~~~~~~ Here's what we write to create our partial application functions gently: - var callLeft = variadic( function (fn, args) { - return variadic( function (remainingArgs) { - return fn.apply(this, args.concat(remainingArgs)) - }) - }) +{:lang="js"} +~~~~~~~~ +var callLeft = variadic( function (fn, args) { + return variadic( function (remainingArgs) { + return fn.apply(this, args.concat(remainingArgs)) + }) +}) - // Let's try it! +// Let's try it! - var mapper = variadic( function (fn, elements) { - return elements.map(fn) - }); +var mapper = variadic( function (fn, elements) { + return elements.map(fn) +}); - mapper(function (x) { return x * x }, 1, 2, 3) - //=> [1, 4, 9] +mapper(function (x) { return x * x }, 1, 2, 3) + //=> [1, 4, 9] - var squarer = callLeft(mapper, function (x) { return x * x }); +var squarer = callLeft(mapper, function (x) { return x * x }); - squarer(1, 2, 3) - //=> [1, 4, 9] +squarer(1, 2, 3) + //=> [1, 4, 9] +~~~~~~~~ While we're at it, here's our implementation of `callRight` using the same technique: - var callRight = variadic( function (fn, args) { - return variadic( function (precedingArgs) { - return fn.apply(this, precedingArgs.concat(args)) - }) - }) +{:lang="js"} +~~~~~~~~ +var callRight = variadic( function (fn, args) { + return variadic( function (precedingArgs) { + return fn.apply(this, precedingArgs.concat(args)) + }) +}) +~~~~~~~~ -Fine print: Of course, `variadic` introduces an extra function call and may not be the best choice in a highly performance-critical piece of code. Then again, using `arguments` is considerably slower than directly accessing argument bindings, so if the performance is that critical, maybe you shouldn't be using a variable number of arguments in that section. \ No newline at end of file +Fine print: Of course, `variadic` introduces an extra function call and may not be the best choice in a highly performance-critical piece of code. Then again, using `arguments` is considerably slower than directly accessing argument bindings, so if the performance is that critical, maybe you shouldn't be using a variable number of arguments in that section. diff --git a/manuscript/markdown/Functions/recipes/maybe.md b/manuscript/markdown/Functions/recipes/maybe.md index 5f4159b..9cfc4b7 100644 --- a/manuscript/markdown/Functions/recipes/maybe.md +++ b/manuscript/markdown/Functions/recipes/maybe.md @@ -4,57 +4,75 @@ A common problem in programming is checking for `null` or `undefined` (hereafter This recipe concerns a pattern that is very common: A function `fn` takes a value as a parameter, and its behaviour by design is to do nothing if the parameter is nothing: - function isSomething (value) { - return value !== null && value !== void 0; - } +{:lang="js"} +~~~~~~~~ +function isSomething (value) { + return value !== null && value !== void 0; +} - function checksForSomething (value) { - if (isSomething(value)) { - // function's true logic - } - } +function checksForSomething (value) { + if (isSomething(value)) { + // function's true logic + } +} +~~~~~~~~ Alternately, the function may be intended to work with any value, but the code calling the function wishes to emulate the behaviour of doing nothing by design when given nothing: - var something = isSomething(value) ? - doesntCheckForSomething(value) : value; - +{:lang="js"} +~~~~~~~~ +var something = isSomething(value) ? + doesntCheckForSomething(value) : value; +~~~~~~~~ + Naturally, there's a recipe for that, borrowed from Haskell's [maybe monad][maybe], Ruby's [andand], and CoffeeScript's existential method invocation: - function maybe (fn) { - return function () { - var i; - - if (arguments.length === 0) { - return - } - else { - for (i = 0; i < arguments.length; ++i) { - if (arguments[i] == null) return - } - return fn.apply(this, arguments) - } +{:lang="js"} +~~~~~~~~ +function maybe (fn) { + return function () { + var i; + + if (arguments.length === 0) { + return + } + else { + for (i = 0; i < arguments.length; ++i) { + if (arguments[i] == null) return } + return fn.apply(this, arguments) } + } +} +~~~~~~~~ `maybe` reduces the logic of checking for nothing to a function call, either: - var checksForSomething = maybe(function (value) { - // function's true logic - }); - +{:lang="js"} +~~~~~~~~ +var checksForSomething = maybe(function (value) { + // function's true logic +}); +~~~~~~~~ + Or: - - var something = maybe(doesntCheckForSomething)(value); - + +{:lang="js"} +~~~~~~~~ +var something = maybe(doesntCheckForSomething)(value); +~~~~~~~~ + As a bonus, `maybe` plays very nicely with instance methods, we'll discuss those [later](#methods): - function Model () {}; - - Model.prototype.setSomething = maybe(function (value) { - this.something = value; - }); - +{:lang="js"} +~~~~~~~~ +function Model () {}; + +Model.prototype.setSomething = maybe(function (value) { + this.something = value; +}); +~~~~~~~~ + If some code ever tries to call `model.setSomething` with nothing, the operation will be skipped. [andand]: https://github.com/raganwald/andand diff --git a/manuscript/markdown/Functions/recipes/partial.md b/manuscript/markdown/Functions/recipes/partial.md index fc50916..4a64567 100644 --- a/manuscript/markdown/Functions/recipes/partial.md +++ b/manuscript/markdown/Functions/recipes/partial.md @@ -4,38 +4,41 @@ In [Building Blocks](#buildingblocks), we discussed partial application, but we These two recipes are for quickly and simply applying a single argument, either the leftmost or rightmost.[^inspired] If you want to bind more than one argument, or you want to leave a "hole" in the argument list, you will need to either use a [generalized partial recipe](#partial), or you will need to repeatedly apply arguments. It is [context](#context)-agnostic. - var __slice = Array.prototype.slice; - - function callFirst (fn, larg) { - return function () { - var args = __slice.call(arguments, 0); - - return fn.apply(this, [larg].concat(args)) - } - } - - function callLast (fn, rarg) { - return function () { - var args = __slice.call(arguments, 0); - - return fn.apply(this, args.concat([rarg])) - } - } - - function greet (me, you) { - return "Hello, " + you + ", my name is " + me - } - - var heliosSaysHello = callFirst(greet, 'Helios'); - - heliosSaysHello('Eartha') - //=> 'Hello, Eartha, my name is Helios' - - var sayHelloToCeline = callLast(greet, 'Celine'); - - sayHelloToCeline('Eartha') - //=> 'Hello, Celine, my name is Eartha' - +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function callFirst (fn, larg) { + return function () { + var args = __slice.call(arguments, 0); + + return fn.apply(this, [larg].concat(args)) + } +} + +function callLast (fn, rarg) { + return function () { + var args = __slice.call(arguments, 0); + + return fn.apply(this, args.concat([rarg])) + } +} + +function greet (me, you) { + return "Hello, " + you + ", my name is " + me +} + +var heliosSaysHello = callFirst(greet, 'Helios'); + +heliosSaysHello('Eartha') + //=> 'Hello, Eartha, my name is Helios' + +var sayHelloToCeline = callLast(greet, 'Celine'); + +sayHelloToCeline('Eartha') + //=> 'Hello, Celine, my name is Eartha' +~~~~~~~~ + As noted above, our partial recipe allows us to create functions that are partial applications of functions that are context aware. We'd need a different recipe if we wish to create partial applications of object methods. -[^inspired]: `callFirst` and `callLast` were inspired by Michael Fogus' [Lemonad](https://github.com/fogus/lemonad). Thanks! \ No newline at end of file +[^inspired]: `callFirst` and `callLast` were inspired by Michael Fogus' [Lemonad](https://github.com/fogus/lemonad). Thanks! diff --git a/manuscript/markdown/Functions/recipes/tap.md b/manuscript/markdown/Functions/recipes/tap.md index 112260a..9679aef 100644 --- a/manuscript/markdown/Functions/recipes/tap.md +++ b/manuscript/markdown/Functions/recipes/tap.md @@ -2,78 +2,108 @@ One of the most basic combinators is the "K Combinator," nicknamed the "kestrel:" - function K (x) { - return function (y) { - return x - } - }; +{:lang="js"} +~~~~~~~~ +function K (x) { + return function (y) { + return x + } +}; +~~~~~~~~ It has some surprising applications. One is when you want to do something with a value for side-effects, but keep the value around. Behold: - function tap (value) { - return function (fn) { - if (typeof(fn) === 'function') { - fn(value) - } - return value - } +{:lang="js"} +~~~~~~~~ +function tap (value) { + return function (fn) { + if (typeof(fn) === 'function') { + fn(value) } + return value + } +} +~~~~~~~~ `tap` is a traditional name borrowed from various Unix shell commands. It takes a value and returns a function that always returns the value, but if you pass it a function, it executes the function for side-effects. Let's see it in action as a poor-man's debugger: - var drink = tap('espresso')(function (it) { - console.log("Our drink is", it) - }); - - // outputs "Our drink is 'espresso'" to the console +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso')(function (it) { + console.log("Our drink is", it) +}); + +~~~~~~~~ +// outputs "Our drink is 'espresso'" to the console It's easy to turn off: - var drink = tap('espresso')(); - - // doesn't output anything to the console +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso')(); + +// doesn't output anything to the console +~~~~~~~~ Libraries like [Underscore] use a version of `tap` that is "uncurried:" - var drink = _.tap('espresso', function () { - console.log("Our drink is", this) - }); - +{:lang="js"} +~~~~~~~~ +var drink = _.tap('espresso', function () { + console.log("Our drink is", this) +}); +~~~~~~~~ + Let's enhance our recipe so it works both ways: - function tap (value, fn) { - if (fn === void 0) { - return curried - } - else return curried(fn); - - function curried (fn) { - if (typeof(fn) === 'function') { - fn(value) - } - return value - } +{:lang="js"} +~~~~~~~~ +function tap (value, fn) { + if (fn === void 0) { + return curried + } + else return curried(fn); + + function curried (fn) { + if (typeof(fn) === 'function') { + fn(value) } + return value + } +} +~~~~~~~~ Now you can write: - var drink = tap('espresso')(function (it) { - console.log("Our drink is", it) - }); - +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso')(function (it) { + console.log("Our drink is", it) +}); +~~~~~~~~ + Or: - var drink = tap('espresso', function (it) { - console.log("Our drink is", it) - }); - +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso', function (it) { + console.log("Our drink is", it) +}); +~~~~~~~~ + And if you wish it to do nothing at all, You can write either: - var drink = tap('espresso')(); +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso')(); +~~~~~~~~ Or: - var drink = tap('espresso', null); +{:lang="js"} +~~~~~~~~ +var drink = tap('espresso', null); +~~~~~~~~ `tap` can do more than just act as a debugging aid. It's also useful for working with [object and instance methods](#tap-methods). diff --git a/manuscript/markdown/Functions/recipes/unary.md b/manuscript/markdown/Functions/recipes/unary.md index 2922d60..24b1207 100644 --- a/manuscript/markdown/Functions/recipes/unary.md +++ b/manuscript/markdown/Functions/recipes/unary.md @@ -4,41 +4,56 @@ In [Ellipses](#ellipses), we saw a function decorator that takes a function with The most common use case is to fix a common problem. JavaScript has a `.map` method for arrays, and many libraries offer a `map` function with the same semantics. Here it is in action: - ['1', '2', '3'].map(parseFloat) - //=> [1, 2, 3] - +{:lang="js"} +~~~~~~~~ +['1', '2', '3'].map(parseFloat) + //=> [1, 2, 3] +~~~~~~~~ + In that example, it looks exactly like the mapping function you'll find in most languages: You pass it a function, and it calls the function with one argument, the element of the array. However, that's not the whole story. JavaScript's `map` actually calls each function with *three* arguments: The element, the index of the element in the array, and the array itself. Let's try it: - [1, 2, 3].map(function (element, index, arr) { - console.log({element: element, index: index, arr: arr}) - }) - //=> { element: 1, index: 0, arr: [ 1, 2, 3 ] } - // { element: 2, index: 1, arr: [ 1, 2, 3 ] } - // { element: 3, index: 2, arr: [ 1, 2, 3 ] } - +{:lang="js"} +~~~~~~~~ +[1, 2, 3].map(function (element, index, arr) { + console.log({element: element, index: index, arr: arr}) +}) + //=> { element: 1, index: 0, arr: [ 1, 2, 3 ] } + // { element: 2, index: 1, arr: [ 1, 2, 3 ] } + // { element: 3, index: 2, arr: [ 1, 2, 3 ] } +~~~~~~~~ + If you pass in a function taking only one argument, it simply ignores the additional arguments. But some functions have optional second or even third arguments. For example: - ['1', '2', '3'].map(parseInt) - //=> [1, NaN, NaN] +{:lang="js"} +~~~~~~~~ +['1', '2', '3'].map(parseInt) + //=> [1, NaN, NaN] +~~~~~~~~ This doesn't work because `parseInt` is defined as `parseInt(string[, radix])`. It takes an optional radix argument. And when you call `parseInt` with `map`, the index is interpreted as a radix. Not good! What we want is to convert `parseInt` into a function taking only one argument. We could write `['1', '2', '3'].map(function (s) { return parseInt(s); })`, or we could come up with a decorator to do the job for us: - function unary (fn) { - if (fn.length == 1) { - return fn - } - else return function (something) { - return fn.call(this, something) - } - } +{:lang="js"} +~~~~~~~~ +function unary (fn) { + if (fn.length == 1) { + return fn + } + else return function (something) { + return fn.call(this, something) + } +} +~~~~~~~~ And now we can write: - ['1', '2', '3'].map(unary(parseInt)) - //=> [1, 2, 3] - +{:lang="js"} +~~~~~~~~ +['1', '2', '3'].map(unary(parseInt)) + //=> [1, 2, 3] +~~~~~~~~ + Presto! diff --git a/manuscript/markdown/Ideas/agnostic.md b/manuscript/markdown/Ideas/agnostic.md index b17c828..1996cf8 100644 --- a/manuscript/markdown/Ideas/agnostic.md +++ b/manuscript/markdown/Ideas/agnostic.md @@ -2,60 +2,78 @@ JavaScript is inflexible about certain things. One of them is invoking `new` on a constructor. In many of our recipes, we can write functions that can handle a variable number of arguments and use `.apply` to invoke a function. For example: - function fluent (methodBody) { - return function () { - methodBody.apply(this, arguments); - return this - } - } +{:lang="js"} +~~~~~~~~ +function fluent (methodBody) { + return function () { + methodBody.apply(this, arguments); + return this + } +} +~~~~~~~~ You can't do the same thing with calling a constructor. This will not work: - function User (name, password) { - this.name = name || 'Untitled'; - this.password = password - }; - - function withDefaultPassword () { - var args = Array.prototype.slice.call(arguments, 0); - args[1] = 'swordfish'; - return new User.apply(this, args); - } - - withDefaultPassword('James') - //=> TypeError: function apply() { [native code] } is not a constructor +{:lang="js"} +~~~~~~~~ +function User (name, password) { + this.name = name || 'Untitled'; + this.password = password +}; + +function withDefaultPassword () { + var args = Array.prototype.slice.call(arguments, 0); + args[1] = 'swordfish'; + return new User.apply(this, args); +} + +withDefaultPassword('James') + //=> TypeError: function apply() { [native code] } is not a constructor +~~~~~~~~ Another weakness of constructors is that if you call them without using `new`, you usually get nonsense: - User('James', 'swordfish') - //=> undefined +{:lang="js"} +~~~~~~~~ +User('James', 'swordfish') + //=> undefined +~~~~~~~~ In David Herman's [Effective JavaScript][ejs], he describes the "New-Agnostic Constructor Pattern." He gives several variations, but the simplest is this: - function User (name, password) { - if (!(this instanceof User)) { - return new User(name, password); - } - this.name = name || 'Untitled'; - this.password = password - }; +{:lang="js"} +~~~~~~~~ +function User (name, password) { + if (!(this instanceof User)) { + return new User(name, password); + } + this.name = name || 'Untitled'; + this.password = password +}; +~~~~~~~~ Now you can call the constructor without the `new` keyword: - User('James', 'swordfish') - //=> { name: 'James', password: 'swordfish' } - +{:lang="js"} +~~~~~~~~ +User('James', 'swordfish') + //=> { name: 'James', password: 'swordfish' } +~~~~~~~~ + This in turn opens up the possibility of doing dynamic things with constructors that didn't work when you were forced to use `new`: - - function withDefaultPassword () { - var args = Array.prototype.slice.call(arguments, 0); - args[1] = 'swordfish'; - return User.apply(this, args); - } - - withDefaultPassword('James') - //=> { name: 'James', password: 'swordfish' } - + +{:lang="js"} +~~~~~~~~ +function withDefaultPassword () { + var args = Array.prototype.slice.call(arguments, 0); + args[1] = 'swordfish'; + return User.apply(this, args); +} + +withDefaultPassword('James') + //=> { name: 'James', password: 'swordfish' } +~~~~~~~~ + (The pattern above has a tradeoff: It works for all circumstances except when you want to set up an inheritance hierarchy.) [ejs]: http://www.amazon.com/gp/product/B00AC1RP14/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00AC1RP14&linkCode=as2&tag=raganwald001-20 "Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript" @@ -64,13 +82,16 @@ This in turn opens up the possibility of doing dynamic things with constructors Here's another way to write a new-agnostic constructor: - function User (name, password) { - var self = this instanceof User ? this : new User(); - if (name != null) { - self.name = name; - self.password = password; - } - return self; - }; - +{:lang="js"} +~~~~~~~~ +function User (name, password) { + var self = this instanceof User ? this : new User(); + if (name != null) { + self.name = name; + self.password = password; + } + return self; +}; +~~~~~~~~ + The principle is that the constructor initializes an object assigned to the variable `self` and returns it. When you call the constructor with `new`, then `self` will be assigned the current context. But if you call this constructor as a standard function, then it will call itself without parameters and assign the newly created User to `self`. diff --git a/manuscript/markdown/Ideas/class-decorators.md b/manuscript/markdown/Ideas/class-decorators.md index f75a0af..57e57af 100644 --- a/manuscript/markdown/Ideas/class-decorators.md +++ b/manuscript/markdown/Ideas/class-decorators.md @@ -10,64 +10,70 @@ create an "empty object." Once again, our Todo class: - function Todo (name) { - var self = this instanceof Todo - ? this - : new Todo(); - self.name = name || 'Untitled'; - self.done = false; - return self; - }; - - Todo.prototype.do = fluent( function () { - this.done = true; - }); - - Todo.prototype.undo = fluent( function () { - this.done = false; - }); - - Todo.prototype; - //=> { do: [Function], undo: [Function] } +{:lang="js"} +~~~~~~~~ +function Todo (name) { + var self = this instanceof Todo + ? this + : new Todo(); + self.name = name || 'Untitled'; + self.done = false; + return self; +}; + +Todo.prototype.do = fluent( function () { + this.done = true; +}); + +Todo.prototype.undo = fluent( function () { + this.done = false; +}); + +Todo.prototype; + //=> { do: [Function], undo: [Function] } +~~~~~~~~ Here's our `ColourCoded` as a class decorator: It returns a new class rather than modifying `ToDo`: - function AndColourCoded (clazz) { - function Decorated () { - var self = this instanceof Decorated - ? this - : new Decorated(); - - return clazz.apply(self, arguments); - }; - Decorated.prototype = new clazz(); - - Decorated.prototype.setColourRGB = fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }); - - Decorated.prototype.getColourRGB = function () { - return this.colourCode; - }; - - return Decorated; - }; - - var ColourTodo = AndColourCoded(Todo); - - Todo.prototype; - //=> { do: [Function], undo: [Function] } - - var colourTodo = new ColourTodo('Write more JavaScript'); - colourTodo.setColourRGB(255, 255, 0); - //=> { name: 'Write more JavaScript', - // done: false, - // colourCode: { r: 255, g: 255, b: 0 } } - - colourTodo instanceof Todo - //=> true - - colourTodo instanceof ColourTodo - //=> true +{:lang="js"} +~~~~~~~~ +function AndColourCoded (clazz) { + function Decorated () { + var self = this instanceof Decorated + ? this + : new Decorated(); + + return clazz.apply(self, arguments); + }; + Decorated.prototype = new clazz(); + + Decorated.prototype.setColourRGB = fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }); + + Decorated.prototype.getColourRGB = function () { + return this.colourCode; + }; + + return Decorated; +}; + +var ColourTodo = AndColourCoded(Todo); + +Todo.prototype; + //=> { do: [Function], undo: [Function] } + +var colourTodo = new ColourTodo('Write more JavaScript'); +colourTodo.setColourRGB(255, 255, 0); + //=> { name: 'Write more JavaScript', + // done: false, + // colourCode: { r: 255, g: 255, b: 0 } } + +colourTodo instanceof Todo + //=> true + +colourTodo instanceof ColourTodo + //=> true +~~~~~~~~ Although the implementation is more subtle, class decorators can be an improvement on functional mixins when you wish to avoid destructively modifying an existing prototype. diff --git a/manuscript/markdown/Ideas/drunken.md b/manuscript/markdown/Ideas/drunken.md index 373fd04..930fbdb 100644 --- a/manuscript/markdown/Ideas/drunken.md +++ b/manuscript/markdown/Ideas/drunken.md @@ -77,8 +77,8 @@ LinkedList.prototype.iterator = function() { function tortoiseAndHareLoopDetector (iterable) { var tortoise = iterable.iterator(), - hare = iterable.iterator(), - tortoiseValue, + hare = iterable.iterator(), + tortoiseValue, hareValue; while (((tortoiseValue = tortoise()) != null) && ((hare(), hareValue = hare()) != null)) { if (tortoiseValue === hareValue) { @@ -134,7 +134,7 @@ var Game = (function () { function Game (size) { var i, j; - + this.size = size ? Math.floor(Math.random() * 8) + 8 : size ; @@ -146,16 +146,16 @@ var Game = (function () { } } this.initialPosition = [ - 2 + Math.floor(Math.random() * (this.size - 4)), + 2 + Math.floor(Math.random() * (this.size - 4)), 2 + Math.floor(Math.random() * (this.size - 4)) ]; return this; }; - + Game.prototype.contains = function (position) { return position[0] >= 0 && position[0] < this.size && position[1] >= 0 && position[1] < this.size; }; - + Game.prototype.iterator = function () { var position = [this.initialPosition[0], this.initialPosition[1]]; return function () { @@ -171,9 +171,9 @@ var Game = (function () { } }.bind(this); }; - + return Game; - + })(); var i = new Game().iterator(); diff --git a/manuscript/markdown/Ideas/functional-mixins.md b/manuscript/markdown/Ideas/functional-mixins.md index 3f8e66a..17b0b59 100644 --- a/manuscript/markdown/Ideas/functional-mixins.md +++ b/manuscript/markdown/Ideas/functional-mixins.md @@ -6,47 +6,56 @@ First, a quick recap: In JavaScript, a "class" is implemented as a constructor f Here's an evolved class of todo items we saw earlier: - function Todo (name) { - var self = this instanceof Todo - ? this - : new Todo(); - self.name = name || 'Untitled'; - self.done = false; - return self; - }; - - Todo.prototype.do = fluent( function () { - this.done = true; - }); - - Todo.prototype.undo = fluent( function () { - this.done = false; - }); - - Todo.prototype; - //=> { do: [Function], undo: [Function] } +{:lang="js"} +~~~~~~~~ +function Todo (name) { + var self = this instanceof Todo + ? this + : new Todo(); + self.name = name || 'Untitled'; + self.done = false; + return self; +}; + +Todo.prototype.do = fluent( function () { + this.done = true; +}); + +Todo.prototype.undo = fluent( function () { + this.done = false; +}); + +Todo.prototype; + //=> { do: [Function], undo: [Function] } +~~~~~~~~ And a "mixin:" - var ColourCoded = { - setColourRGB: fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }), - getColourRGB: function () { - return this.colourCode; - } - }; - +{:lang="js"} +~~~~~~~~ +var ColourCoded = { + setColourRGB: fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }), + getColourRGB: function () { + return this.colourCode; + } +}; +~~~~~~~~ + Mixing colour coding into our Todo prototype is straightforward: - extend(Todo.prototype, ColourCoded); - - Todo.prototype; - //=> { do: [Function], - // undo: [Function], - // setColourRGB: [Function], - // getColourRGB: [Function] } - +{:lang="js"} +~~~~~~~~ +extend(Todo.prototype, ColourCoded); + +Todo.prototype; + //=> { do: [Function], + // undo: [Function], + // setColourRGB: [Function], + // getColourRGB: [Function] } +~~~~~~~~ + ### what is a "mixin?" Like "class," the word "mixin" means different things to different people. A Ruby user will talk about modules, for example. And a JavaScript user could in truth say that everything is an object and we're just extending one object (that happens to be a prototype) with the properties of another object (that just happens to contain some functions). @@ -57,58 +66,67 @@ A simple definition that works for most purposes is to define a mixin as: *A col The mixin we have above works properly, but our little recipe had two distinct steps: Define the mixin and then extend the class prototype. Angus Croll pointed out that it's far more elegant to define a mixin as a function rather than an object. He calls this a [functional mixin][fm]. Here's our `ColourCoded` recast in functional form: - function becomeColourCoded (target) { - target.setColourRGB = fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }); - - target.getColourRGB = function () { - return this.colourCode; - }; - - return target; - }; - - becomeColourCoded(Todo.prototype); - - Todo.prototype; - //=> { do: [Function], - // undo: [Function], - // setColourRGB: [Function], - // getColourRGB: [Function] } - +{:lang="js"} +~~~~~~~~ +function becomeColourCoded (target) { + target.setColourRGB = fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }); + + target.getColourRGB = function () { + return this.colourCode; + }; + + return target; +}; + +becomeColourCoded(Todo.prototype); + +Todo.prototype; + //=> { do: [Function], + // undo: [Function], + // setColourRGB: [Function], + // getColourRGB: [Function] } +~~~~~~~~ + Notice that we mix the functionality into the prototype. This keeps our mixing flexible: You could mix functionality directly into an object if you so choose. Twitter's [Flight] framework uses a variation on this technique that targets the mixin function's context: - function asColourCoded () { - this.setColourRGB = fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }); - - this.getColourRGB = function () { - return this.colourCode; - }; - - return this; - }; - - asColourCoded.call(Todo.prototype); - +{:lang="js"} +~~~~~~~~ +function asColourCoded () { + this.setColourRGB = fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }); + + this.getColourRGB = function () { + return this.colourCode; + }; + + return this; +}; + +asColourCoded.call(Todo.prototype); +~~~~~~~~ + This approach has some subtle benefits: You can use mixins as methods, for example. It's possible to write a context-agnostic functional mixin: - function colourCoded () { - if (arguments[0] !== void 0) { - return colourCoded.call(arguments[0]); - } - this.setColourRGB = fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }); - - this.getColourRGB = function () { - return this.colourCode; - }; - - return this; - }; +{:lang="js"} +~~~~~~~~ +function colourCoded () { + if (arguments[0] !== void 0) { + return colourCoded.call(arguments[0]); + } + this.setColourRGB = fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }); + + this.getColourRGB = function () { + return this.colourCode; + }; + + return this; +}; +~~~~~~~~ Bueno! diff --git a/manuscript/markdown/Ideas/prototype.md b/manuscript/markdown/Ideas/prototype.md index 0921d89..be99355 100644 --- a/manuscript/markdown/Ideas/prototype.md +++ b/manuscript/markdown/Ideas/prototype.md @@ -12,62 +12,71 @@ That sounds tautological, until we look at JavaScript. But let's start with a qu In Ruby, classes are objects, but they're special objects. For example, here are some of the methods associated with the Ruby class `String`: - String.methods - #=> [:try_convert, :allocate, :new, :superclass, :freeze, :===, :==, - :<=>, :<, :<=, :>, :>=, :to_s, :included_modules, :include?, :name, - :ancestors, :instance_methods, :public_instance_methods, - :protected_instance_methods, :private_instance_methods, :constants, - :const_get, :const_set, :const_defined?, :const_missing, - :class_variables, :remove_class_variable, :class_variable_get, - :class_variable_set, :class_variable_defined?, :public_constant, - :private_constant, :module_exec, :class_exec, :module_eval, :class_eval, - :method_defined?, :public_method_defined?, :private_method_defined?, - :protected_method_defined?, :public_class_method, :private_class_method, - # ... - :!=, :instance_eval, :instance_exec, :__send__, :__id__] +{:lang="ruby"} +~~~~~~~~ +String.methods + #=> [:try_convert, :allocate, :new, :superclass, :freeze, :===, :==, + :<=>, :<, :<=, :>, :>=, :to_s, :included_modules, :include?, :name, + :ancestors, :instance_methods, :public_instance_methods, + :protected_instance_methods, :private_instance_methods, :constants, + :const_get, :const_set, :const_defined?, :const_missing, + :class_variables, :remove_class_variable, :class_variable_get, + :class_variable_set, :class_variable_defined?, :public_constant, + :private_constant, :module_exec, :class_exec, :module_eval, :class_eval, + :method_defined?, :public_method_defined?, :private_method_defined?, + :protected_method_defined?, :public_class_method, :private_class_method, + # ... + :!=, :instance_eval, :instance_exec, :__send__, :__id__] +~~~~~~~~ And here are some of the methods associated with an instance of a string: - String.new.methods - #=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], - :[]=, :insert, :length, :size, :bytesize, :empty?, :=~, - :match, :succ, :succ!, :next, :next!, :upto, :index, :rindex, - :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, - :to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, - :downcase, :capitalize, :swapcase, :upcase!, :downcase!, - :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, - :chars, :codepoints, :reverse, :reverse!, :concat, :<<, - :prepend, :crypt, :intern, :to_sym, :ord, :include?, - :start_with?, :end_with?, :scan, :ljust, :rjust, :center, - # ... - :instance_eval, :instance_exec, :__send__, :__id__] +{:lang="ruby"} +~~~~~~~~ +String.new.methods + #=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], + :[]=, :insert, :length, :size, :bytesize, :empty?, :=~, + :match, :succ, :succ!, :next, :next!, :upto, :index, :rindex, + :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, + :to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, + :downcase, :capitalize, :swapcase, :upcase!, :downcase!, + :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, + :chars, :codepoints, :reverse, :reverse!, :concat, :<<, + :prepend, :crypt, :intern, :to_sym, :ord, :include?, + :start_with?, :end_with?, :scan, :ljust, :rjust, :center, + # ... + :instance_eval, :instance_exec, :__send__, :__id__] +~~~~~~~~ As you can see, a "class" in Ruby is very different from an "instance of that class." And the methods of a class are very different from the methods of an instance of that class. Here's how you define a Queue in Ruby: - class Queue - def initialize - @array, @head, @tail = [], 0, -1 - end - - def pushTail value - @array[@tail += 1] = value - end - - def pullHead - if !@isEmpty - @array[@head]).tap { |value| - @array[@head] = null - @head += 1 - } - end - end - - def isEmpty - !!(@tail < @head) - end +{:lang="ruby"} +~~~~~~~~ +class Queue + def initialize + @array, @head, @tail = [], 0, -1 + end + + def pushTail value + @array[@tail += 1] = value + end + + def pullHead + if !@isEmpty + @array[@head]).tap { |value| + @array[@head] = null + @head += 1 + } end + end + + def isEmpty + !!(@tail < @head) + end +end +~~~~~~~~ There is special syntax for defining a class, and special syntax for defining the behaviour of instances. There are different ways of defining the way new instances are created in classist languages. Ruby uses a "magic method" called `initialize`. Now let's look at JavaScript. @@ -77,16 +86,19 @@ JavaScript objects don't have a formal class, and thus there's no special syntax JavaScript instances are created with a *constructor*. The constructor of an instance is a function that was invoked with the `new` operator. In JavaScript, any function can be a constructor, even if it doesn't look like one: - function square (n) { return n * n; } - //=> undefined - square(2) - //=> 4 - square(2).constructor - //=> [Function: Number] - new square(2) - //=> {} - new square(2).constructor - //=> [Function: square] +{:lang="ruby"} +~~~~~~~~ +function square (n) { return n * n; } + //=> undefined +square(2) + //=> 4 +square(2).constructor + //=> [Function: Number] +new square(2) + //=> {} +new square(2).constructor + //=> [Function: square] +~~~~~~~~ As you can see, the `square` function will act as a constructor if you call it with `new`. *There is no special kind of thing that constructs new objects, every function is (potentially) a constructor*. @@ -94,39 +106,45 @@ That's different from a true classical language, where the class is a special ki How does JavaScript define the behaviour of instances? JavaScript doesn't have a special syntax or special kind of object for that, it has "prototypes." Prototypes are objects, but unlike a classical system, there are no special methods or properties associated with a prototype. Any object can be a prototype, even an empty object. In fact, that's exactly what is associated with a constructor by default: - function Nullo () {}; - Nullo.prototype - //=> {} - -There's absolutely nothing special about a prototype object. No special class methods, no special constructor of its own, nothing. Let's look at a simple Queue in JavaScript: +{:lang="ruby"} +~~~~~~~~ +function Nullo () {}; +Nullo.prototype + //=> {} +~~~~~~~~ - var Queue = function () { - this.array = []; - this.head = 0; - this.tail = -1; - }; - - Queue.prototype.pushTail = function (value) { - return this.array[this.tail += 1] = value; - }; - Queue.prototype.pullHead = function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.head]; - this.array[this.head] = void 0; - this.head += 1; - return value; - } - }; - Queue.prototype.isEmpty = function () { - return this.tail < this.head; - }; +There's absolutely nothing special about a prototype object. No special class methods, no special constructor of its own, nothing. Let's look at a simple Queue in JavaScript: - Queue.prototype - //=> { pushTail: [Function], - // pullHead: [Function], - // isEmpty: [Function] } +{:lang="js"} +~~~~~~~~ +var Queue = function () { + this.array = []; + this.head = 0; + this.tail = -1; +}; + +Queue.prototype.pushTail = function (value) { + return this.array[this.tail += 1] = value; +}; +Queue.prototype.pullHead = function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.head]; + this.array[this.head] = void 0; + this.head += 1; + return value; + } +}; +Queue.prototype.isEmpty = function () { + return this.tail < this.head; +}; + +Queue.prototype + //=> { pushTail: [Function], + // pullHead: [Function], + // isEmpty: [Function] } +~~~~~~~~ The first way a prototype in JavaScript is different from a class in Ruby is that the prototype is an ordinary object with exactly the same properties that we expect to find in an instance: Methods `pushTail`, `pullHead`, and `isEmpty`. @@ -161,4 +179,3 @@ You will have something that works just like a simple class-based system, with t But if you want more, you have a flexible system that does allow you to do [much much more][fd]. It's up to you. [fd]: https://github.com/raganwald/homoiconic/blob/master/2013/01/function_and_method_decorators.md#function-and-method-decorators "Function and Method Decorators" - diff --git a/manuscript/markdown/Ideas/recipes/after.md b/manuscript/markdown/Ideas/recipes/after.md index bf619ba..4038f4e 100644 --- a/manuscript/markdown/Ideas/recipes/after.md +++ b/manuscript/markdown/Ideas/recipes/after.md @@ -7,63 +7,72 @@ Combinators for functions come in an unlimited panoply of purposes and effects. For example, consider this "class:" - Todo = function (name) { - this.name = name || 'Untitled'; - this.done = false - } - - extend(Todo.prototype, { - do: fluent( function { - this.done = true - }), - undo: fluent( function { - this.done = false - }), - setName: fluent( maybe( function (name) { - this.name = name - })) - }); +{:lang="js"} +~~~~~~~~ +Todo = function (name) { + this.name = name || 'Untitled'; + this.done = false +} + +extend(Todo.prototype, { + do: fluent( function { + this.done = true + }), + undo: fluent( function { + this.done = false + }), + setName: fluent( maybe( function (name) { + this.name = name + })) +}); +~~~~~~~~ If we're rolling our own model class, we might mix in [Backbone.Events]. Now we can have views listen to our todo items and render themselves when there's a change. Since we've already seen [before](#before), we'll jump right to the recipe for `after`, a combinator that turns a function into a method decorator: - function after (decoration) { - return function (method) { - return function () { - var value = method.apply(this, arguments); - decoration.call(this, value); - return value - } - } +{:lang="js"} +~~~~~~~~ +function after (decoration) { + return function (method) { + return function () { + var value = method.apply(this, arguments); + decoration.call(this, value); + return value } + } +} +~~~~~~~~ [Backbone.Events]: http://backbonejs.org/#Events And here it is in use to trigger change events on our `Todo` "class." We're going to be even *more* sophisticated and paramaterize our decorators. - extend(Todo.prototype, Backbone.Events); - - function changes (propertyName) { - return after(function () { - this.trigger('changed changed:'+propertyName, this[propertyName]) - }) - } - - extend(Todo.prototype, { - do: fluent( changes('done')( function { - this.done = true - })), - undo: fluent( changes('done')( function { - this.done = false - })), - setName: fluent( changes('name')( maybe( function (name) { - this.name = name - }))) - }); - +{:lang="js"} +~~~~~~~~ +extend(Todo.prototype, Backbone.Events); + +function changes (propertyName) { + return after(function () { + this.trigger('changed changed:'+propertyName, this[propertyName]) + }) +} + +extend(Todo.prototype, { + do: fluent( changes('done')( function { + this.done = true + })), + undo: fluent( changes('done')( function { + this.done = false + })), + setName: fluent( changes('name')( maybe( function (name) { + this.name = name + }))) +}); +~~~~~~~~ + The decorators act like keywords or annotations, documenting the method's behaviour but clearly separating these secondary concerns from the core logic of the method. --- ([before](#before), [after](#after), and many more combinators for building method decorators can be found in the [method combinators][mc] module.) -[mc]: https://github.com/raganwald/method-combinators/blob/master/README-JS.md#method-combinators \ No newline at end of file +[mc]: https://github.com/raganwald/method-combinators/blob/master/README-JS.md#method-combinators diff --git a/manuscript/markdown/Ideas/recipes/before.md b/manuscript/markdown/Ideas/recipes/before.md index ca0feff..20de3b6 100644 --- a/manuscript/markdown/Ideas/recipes/before.md +++ b/manuscript/markdown/Ideas/recipes/before.md @@ -7,99 +7,114 @@ Combinators for functions come in an unlimited panoply of purposes and effects. For example, using our [fluent](#fluent) recipe: - function Cake () { - this.ingredients = {} +{:lang="js"} +~~~~~~~~ +function Cake () { + this.ingredients = {} +} + +extend(Cake.prototype, { + setFlavour: fluent( function (flavour) { + this.flavour = flavour + }), + setLayers: fluent( function (layers) { + this.layers = layers; + }), + add: fluent( function (ingredientMap) { + var ingredient; + + for (ingredient in ingredientMap) { + this.ingredients[ingredient] || + (this.ingredients[ingredient] = 0); + this.ingredients[ingredient] = this.ingredients[ingredient] + + ingredientMap[ingredient] } - - extend(Cake.prototype, { - setFlavour: fluent( function (flavour) { - this.flavour = flavour - }), - setLayers: fluent( function (layers) { - this.layers = layers; - }), - add: fluent( function (ingredientMap) { - var ingredient; - - for (ingredient in ingredientMap) { - this.ingredients[ingredient] || - (this.ingredients[ingredient] = 0); - this.ingredients[ingredient] = this.ingredients[ingredient] + - ingredientMap[ingredient] - } - }), - mix: fluent( function () { - // mix ingredients together - }), - rise: fluent( function (duration) { - // let the ingredients rise - }), - bake: fluent( function () { - // do some baking - }) - }); + }), + mix: fluent( function () { + // mix ingredients together + }), + rise: fluent( function (duration) { + // let the ingredients rise + }), + bake: fluent( function () { + // do some baking + }) +}); +~~~~~~~~ This particular example might be better-served as a state machine, but what we want to encode is that we must always mix the ingredients before allowing the batter to rise or baking the cake. The direct way to write that is: - rise: fluent( function (duration) { - this.mix(); - // let the ingredients rise - }), - bake: fluent( function () { - this.mix(); - // do some baking - }) +{:lang="js"} +~~~~~~~~ +rise: fluent( function (duration) { + this.mix(); + // let the ingredients rise +}), +bake: fluent( function () { + this.mix(); + // do some baking +}) +~~~~~~~~ Nothing wrong with that, however it does clutter the core functionality of rising and baking with a secondary concern, preconditions. There is a similar problem with cross-cutting concerns like logging or checking permissions: You want functions to be smaller and more focused, and decomposing into smaller methods is ugly: - reallyRise: function (duration) { - // let the ingredients rise - }, - rise: fluent (function (duration) { - this.mix(); - this.reallyRise(duration) - }), - reallyBake: function () { - // do some baking - }, - bake: fluent( function () { - this.mix(); - this.reallyBake() - }) +{:lang="js"} +~~~~~~~~ +reallyRise: function (duration) { + // let the ingredients rise +}, +rise: fluent (function (duration) { + this.mix(); + this.reallyRise(duration) +}), +reallyBake: function () { + // do some baking +}, +bake: fluent( function () { + this.mix(); + this.reallyBake() +}) +~~~~~~~~ ### the before recipe This recipe is for a combinator that turns a function into a method decorator. The decorator evaluates the function before evaluating the base method. Here it is: - function before (decoration) { - return function (method) { - return function () { - decoration.apply(this, arguments); - return method.apply(this, arguments) - } - } +{:lang="js"} +~~~~~~~~ +function before (decoration) { + return function (method) { + return function () { + decoration.apply(this, arguments); + return method.apply(this, arguments) } - + } +} +~~~~~~~~ + And here we are using it in conjunction with `fluent`, showing the power of composing combinators: - var mixFirst = before(function () { - this.mix() - }); - - extend(Cake.prototype, { - - // Other methods... - - mix: fluent( function () { - // mix ingredients together - }), - rise: fluent( mixFirst( function (duration) { - // let the ingredients rise - })), - bake: fluent( mixFirst( function () { - // do some baking - })) - }); +{:lang="js"} +~~~~~~~~ +var mixFirst = before(function () { + this.mix() +}); + +extend(Cake.prototype, { + + // Other methods... + + mix: fluent( function () { + // mix ingredients together + }), + rise: fluent( mixFirst( function (duration) { + // let the ingredients rise + })), + bake: fluent( mixFirst( function () { + // do some baking + })) +}); +~~~~~~~~ The decorators act like keywords or annotations, documenting the method's behaviour but clearly separating these secondary concerns from the core logic of the method. diff --git a/manuscript/markdown/Ideas/recipes/class-decorator.md b/manuscript/markdown/Ideas/recipes/class-decorator.md index ad083a6..e59a2fc 100644 --- a/manuscript/markdown/Ideas/recipes/class-decorator.md +++ b/manuscript/markdown/Ideas/recipes/class-decorator.md @@ -2,105 +2,123 @@ As [discussed](#class-decorators), a class decorator creates a new class with some additional decoration. It's lighter weight than subclassing. It's also easy to write a factory function that makes decorators for us. Recall: - function Todo (name) { - var self = this instanceof Todo - ? this - : new Todo(); - self.name = name || 'Untitled'; - self.done = false; - return self; - }; - - Todo.prototype.do = fluent( function () { - this.done = true; - }); - - Todo.prototype.undo = fluent( function () { - this.done = false; - }); +{:lang="js"} +~~~~~~~~ +function Todo (name) { + var self = this instanceof Todo + ? this + : new Todo(); + self.name = name || 'Untitled'; + self.done = false; + return self; +}; + +Todo.prototype.do = fluent( function () { + this.done = true; +}); + +Todo.prototype.undo = fluent( function () { + this.done = false; +}); +~~~~~~~~ We wish to decorate this with: - ({ - setColourRGB: fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }), - getColourRGB: function () { - return this.colourCode; - } - }); - +{:lang="js"} +~~~~~~~~ +({ + setColourRGB: fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }), + getColourRGB: function () { + return this.colourCode; + } +}); +~~~~~~~~ + Instead of writing: - function AndColourCoded (clazz) { - function Decorated () { - var self = this instanceof Decorated - ? this - : new Decorated(); - - return clazz.apply(self, arguments); - }; - Decorated.prototype = new clazz(); - - Decorated.prototype.setColourRGB = fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }); - - Decorated.prototype.getColourRGB = function () { - return this.colourCode; - }; - - return Decorated; - }; +{:lang="js"} +~~~~~~~~ +function AndColourCoded (clazz) { + function Decorated () { + var self = this instanceof Decorated + ? this + : new Decorated(); + + return clazz.apply(self, arguments); + }; + Decorated.prototype = new clazz(); + + Decorated.prototype.setColourRGB = fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }); + + Decorated.prototype.getColourRGB = function () { + return this.colourCode; + }; + + return Decorated; +}; +~~~~~~~~ We'll extract the decoration into a parameter like this: - function classDecorator (decoration, clazz) { - function Decorated () { - var self = this instanceof Decorated - ? this - : new Decorated(); - - return clazz.apply(self, arguments); - }; - Decorated.prototype = extend(new clazz(), decoration); - return Decorated; - }; +{:lang="js"} +~~~~~~~~ +function classDecorator (decoration, clazz) { + function Decorated () { + var self = this instanceof Decorated + ? this + : new Decorated(); + + return clazz.apply(self, arguments); + }; + Decorated.prototype = extend(new clazz(), decoration); + return Decorated; +}; +~~~~~~~~ And then "curry" the function manually like this: - function classDecorator (decoration) { - - return function (clazz) { - function Decorated () { - var self = this instanceof Decorated - ? this - : new Decorated(); - - return clazz.apply(self, arguments); - }; - Decorated.prototype = extend(new clazz(), decoration); - return Decorated; - }; - +{:lang="js"} +~~~~~~~~ +function classDecorator (decoration) { + + return function (clazz) { + function Decorated () { + var self = this instanceof Decorated + ? this + : new Decorated(); + + return clazz.apply(self, arguments); }; - + Decorated.prototype = extend(new clazz(), decoration); + return Decorated; + }; + +}; +~~~~~~~~ + We can try it: - var AndColourCoded = classDecorator({ - setColourRGB: fluent( function (r, g, b) { - this.colourCode = { r: r, g: g, b: b }; - }), - getColourRGB: function () { - return this.colourCode; - } - }); - - var ColourTodo = AndColourCoded(Todo); - - new ColourTodo('Use More Decorators').setColourRGB(0, 255, 0); - //=> { name: 'Use More Decorators', - // done: false, - // colourCode: { r: 0, g: 255, b: 0 } } - -Success! Our `classDecorator` function makes class decorators. \ No newline at end of file +{:lang="js"} +~~~~~~~~ +var AndColourCoded = classDecorator({ + setColourRGB: fluent( function (r, g, b) { + this.colourCode = { r: r, g: g, b: b }; + }), + getColourRGB: function () { + return this.colourCode; + } +}); + +var ColourTodo = AndColourCoded(Todo); + +new ColourTodo('Use More Decorators').setColourRGB(0, 255, 0); + //=> { name: 'Use More Decorators', + // done: false, + // colourCode: { r: 0, g: 255, b: 0 } } +~~~~~~~~ + +Success! Our `classDecorator` function makes class decorators. diff --git a/manuscript/markdown/Ideas/recipes/functional-mixin.md b/manuscript/markdown/Ideas/recipes/functional-mixin.md index d3e7cff..036cfa8 100644 --- a/manuscript/markdown/Ideas/recipes/functional-mixin.md +++ b/manuscript/markdown/Ideas/recipes/functional-mixin.md @@ -2,90 +2,111 @@ [Functional Mixins](#functional-mixins) extend an existing class's prototype. Let's start with: - function Todo (name) { - var self = this instanceof Todo - ? this - : new Todo(); - self.name = name || 'Untitled'; - self.done = false; - return self; - }; - - Todo.prototype.do = fluent( function () { - this.done = true; - }); - - Todo.prototype.undo = fluent( function () { - this.done = false; - }); +{:lang="js"} +~~~~~~~~ +function Todo (name) { + var self = this instanceof Todo + ? this + : new Todo(); + self.name = name || 'Untitled'; + self.done = false; + return self; +}; + +Todo.prototype.do = fluent( function () { + this.done = true; +}); + +Todo.prototype.undo = fluent( function () { + this.done = false; +}); +~~~~~~~~ We wish to decorate this with: - ({ - setLocation: fluent( function (location) { - this.location = location; - }), - getLocation: function () { return this.location; } - }); - +{:lang="js"} +~~~~~~~~ +({ + setLocation: fluent( function (location) { + this.location = location; + }), + getLocation: function () { return this.location; } +}); +~~~~~~~~ + Instead of writing: - function becomeLocationAware () { - this.setLocation = fluent( function (location) { - this.location = location; - }); - - this.getLocation = function () { return this.location; }; - - return this; - }; +{:lang="js"} +~~~~~~~~ +function becomeLocationAware () { + this.setLocation = fluent( function (location) { + this.location = location; + }); + + this.getLocation = function () { return this.location; }; + + return this; +}; +~~~~~~~~ We'll extract the decoration into a parameter like this: - function mixin (decoration) { - extend(this, decoration); - return this; - }; +{:lang="js"} +~~~~~~~~ +function mixin (decoration) { + extend(this, decoration); + return this; +}; +~~~~~~~~ And then "curry" the function manually like this: - function mixin (decoration) { +{:lang="js"} +~~~~~~~~ +function mixin (decoration) { + + return function () { + extend(this, decoration); + return this; + }; + +}; +~~~~~~~~ - return function () { - extend(this, decoration); - return this; - }; - - }; - We can try it: - var MixinLocation = mixin({ - setLocation: fluent( function (location) { - this.location = location; - }), - getLocation: function () { return this.location; } - }); - - MixinLocation.call(Todo.prototype); - - new Todo('Paint Bedroom').setLocation('Home'); - //=> { name: 'Paint Bedroom', - // done: false, - // location: 'Home' +{:lang="js"} +~~~~~~~~ +var MixinLocation = mixin({ + setLocation: fluent( function (location) { + this.location = location; + }), + getLocation: function () { return this.location; } +}); + +MixinLocation.call(Todo.prototype); + +new Todo('Paint Bedroom').setLocation('Home'); + //=> { name: 'Paint Bedroom', + // done: false, + // location: 'Home' +~~~~~~~~ Success! Our `mixin` function makes functional mixins. A final refinement is to make it "context-agnostic," so that we can write either `MixinLocation.call(Todo.prototype)` or `MixinLocation(Todo.prototype)`: - function mixin (decoration) { - - return function decorate () { - if (arguments[0] !=== void 0) { - return decorate.call(arguments[0]); - } - else { - extend(this, decoration); - return this; - }; - }; - - }; \ No newline at end of file +{:lang="js"} +~~~~~~~~ +function mixin (decoration) { + + return function decorate () { + if (arguments[0] !=== void 0) { + return decorate.call(arguments[0]); + } + else { + extend(this, decoration); + return this; + }; + }; + +}; +~~~~~~~~ diff --git a/manuscript/markdown/Ideas/recipes/provided.md b/manuscript/markdown/Ideas/recipes/provided.md index 09cafd1..ba1e4ac 100644 --- a/manuscript/markdown/Ideas/recipes/provided.md +++ b/manuscript/markdown/Ideas/recipes/provided.md @@ -4,44 +4,56 @@ Neither the [before](#before) and [after](#after) decorators can actually termin The provided combinator turns a function into a method decorator. The function must evaluate to truthy for the base method to be evaluated: - function provided (predicate) { - return function(base) { - return function() { - if (predicate.apply(this, arguments)) { - return base.apply(this, arguments); - } - }; - }; +{:lang="js"} +~~~~~~~~ +function provided (predicate) { + return function(base) { + return function() { + if (predicate.apply(this, arguments)) { + return base.apply(this, arguments); + } }; + }; +}; +~~~~~~~~ `provided` can be used to create named decorators like `maybe`: - var maybe = provided( function (value) { - return value != null - }); - - SomeModel.prototype.setAttribute = maybe( function (value) { - this.attribute = value - }); - +{:lang="js"} +~~~~~~~~ +var maybe = provided( function (value) { + return value != null +}); + +SomeModel.prototype.setAttribute = maybe( function (value) { + this.attribute = value +}); +~~~~~~~~ + You can build your own domain-specific decorators: - var whenNamed = provided( function (record) { - return record.name && record.name.length > 0 - }) - +{:lang="js"} +~~~~~~~~ +var whenNamed = provided( function (record) { + return record.name && record.name.length > 0 +}) +~~~~~~~~ + `except` works identically, but with the logic reversed. - function except (predicate) { - return function(base) { - return function() { - if (!predicate.apply(this, arguments)) { - return base.apply(this, arguments); - } - }; - }; +{:lang="js"} +~~~~~~~~ +function except (predicate) { + return function(base) { + return function() { + if (!predicate.apply(this, arguments)) { + return base.apply(this, arguments); + } }; - - var exceptAdmin = except( function (user) { - return user.role.isAdmin() - }); \ No newline at end of file + }; +}; + +var exceptAdmin = except( function (user) { + return user.role.isAdmin() +}); +~~~~~~~~ diff --git a/manuscript/markdown/Ideas/trampolining.md b/manuscript/markdown/Ideas/trampolining.md index b157b03..6d0d513 100644 --- a/manuscript/markdown/Ideas/trampolining.md +++ b/manuscript/markdown/Ideas/trampolining.md @@ -15,6 +15,7 @@ Let's begin with a use case. Consider implementing `factorial` in recursive style: +{:lang="js"} ~~~~~~~~ function factorial (n) { return n @@ -29,6 +30,7 @@ This creates two problems: First, we need space O*n* for all those stack frames. For example: +{:lang="js"} ~~~~~~~~ factorial(10) //=> 3628800 @@ -50,6 +52,7 @@ What we need to do is take the expression `n * factorial(n - 1)` and push it dow If we use the symbol `_` to represent a kind of "hole" in an expression where we plan to put the result, every time `factorial` calls itself, it needs to remember `n * _` so that when it gets a result back, it can multiply it by `n` and return that. So the first time it calls itself, it remembers `10 * _`, the second time it calls itself, it remembers `9 * _`, and all these things stack up like this when we call `factorial(10)`: +{:lang="js"} ~~~~~~~~ 1 * _ 2 * _ @@ -67,10 +70,11 @@ Finally, we call `factorial(0)` and it returns `1`. Then the top is popped off t How can we get around this? Well, imagine if we don't have a hole in a computation to return. In that case, we wouldn't need to "remember" anything on the stack. To make this happen, we need to either return a value or return the result of calling another function without any further computation. -Such a call is said to be in "tail position" and to be a "tail call." The "elimination" of tail-call elimination means that we don't perform a full call including setting up a new stack frame. We perform the equivalent of a "jump." +Such a call is said to be in "tail position" and to be a "tail call." The "elimination" of tail-call elimination means that we don't perform a full call including setting up a new stack frame. We perform the equivalent of a "jump." For example: +{:lang="js"} ~~~~~~~~ function factorial (n) { var _factorial = function myself (acc, n) { @@ -78,13 +82,14 @@ function factorial (n) { ? myself(acc * n, n - 1) : acc }; - + return _factorial(1, n); } ~~~~~~~~ Now our function either returns a value or it returns the result of calling another function without doing anything with that result. This gives us the correct results, but we can see that current implementations of JavaScript don't perform this magic "tail-call elimination." +{:lang="js"} ~~~~~~~~ factorial(10) //=> 3628800 @@ -102,10 +107,11 @@ When we call a function, it returns a *thunk* that we call to get a result. Of c A *thunk* is a function taking no arguments that delays evaluating an expression. For example, this is a thunk: `function () { return 'Hello World'; }`. -An extremely simple and useful implementation of trampolining can be found in the [Lemonad] library. It works provided that you want to trampoline a function that doesn't return a function. Here it is: +An extremely simple and useful implementation of trampolining can be found in the [Lemonad] library. It works provided that you want to trampoline a function that doesn't return a function. Here it is: [Lemonad]: http://fogus.github.com/lemonad/ +{:lang="js"} ~~~~~~~~ L.trampoline = function(fun /*, args */) { var result = fun.apply(fun, _.rest(arguments)); @@ -120,6 +126,7 @@ L.trampoline = function(fun /*, args */) { We'll rewrite it in combinatorial style for consistency and composeability: +{:lang="js"} ~~~~~~~~ var trampoline = function (fn) { @@ -137,6 +144,7 @@ var trampoline = function (fn) { Now here's our implementation of `factorial` that is wrapped around a trampolined tail recursive function: +{:lang="js"} ~~~~~~~~ function factorial (n) { var _factorial = trampoline( function myself (acc, n) { @@ -144,7 +152,7 @@ function factorial (n) { ? function () { return myself(acc * n, n - 1); } : acc }); - + return _factorial(1, n); } @@ -158,6 +166,7 @@ Presto, it runs for `n = 32768`. Sadly, JavaScript's built-in support for intege [^big]: The use of a third-party big integer library is not essential to understand trampolining. +{:lang="js"} ~~~~~~~~ npm install big-integer @@ -173,7 +182,7 @@ var trampoline = function (fn) { } return result; - + }); }; @@ -183,7 +192,7 @@ function factorial (n) { ? function () { return myself(acc.times(n), n.minus(1)); } : acc }); - + return _factorial(bigInt.one, bigInt(n)); } @@ -203,6 +212,7 @@ If trampolining was only for recursive functions, it would have extremely limite Consider this delightfully simple example of two co-recursive functions: +{:lang="js"} ~~~~~~~~ function even (n) { return n == 0 @@ -219,6 +229,7 @@ function odd (n) { Like our `factorial`, it consumes *n* stack space of alternating calls to `even` and `odd`: +{:lang="js"} ~~~~~~~~ even(32768); //=> RangeError: Maximum call stack size exceeded @@ -226,6 +237,7 @@ even(32768); Obviously we can solve this problem with modulo arithmetic, but consider that what this shows is a pair of functions that call other functions in tail position, not just themselves. As with factorial, we separate the public interface that is not trampolined from the trampolined implementation: +{:lang="js"} ~~~~~~~~ var even = trampoline(_even), odd = trampoline(_odd); @@ -245,6 +257,7 @@ function _odd (n) { And presto: +{:lang="js"} ~~~~~~~~ even(32768); //=> true @@ -256,4 +269,4 @@ Trampolining works with co-recursive functions, or indeed any function that can *Trampolining* is a technique for implementing tail-call elimination. Meaning, if you take a function (whether recursive, co-recursive, or any other form) and rewrite it in tail-call form, you can eliminate the need to create a stack frame for every 'invocation'. -Trampolining is very handy in a language like JavaScript, in that it allows you to use a recursive style for functions without worrying about limitations on stack sizes. \ No newline at end of file +Trampolining is very handy in a language like JavaScript, in that it allows you to use a recursive style for functions without worrying about limitations on stack sizes. diff --git a/manuscript/markdown/Instances and Classes/binding.md b/manuscript/markdown/Instances and Classes/binding.md index 70284d9..d51a4f8 100644 --- a/manuscript/markdown/Instances and Classes/binding.md +++ b/manuscript/markdown/Instances and Classes/binding.md @@ -2,34 +2,46 @@ Recall that in [What Context Applies When We Call a Function?](#context), we adjourned our look at setting the context of a function with a look at a `contextualize` helper function: - var contextualize = function (fn, context) { - return function () { - return fn.apply(context, arguments) - } - }, - a = [1,2,3], - accrete = contextualize(a.concat, a); - - accrete([4,5]) - //=> [ 1, 2, 3, 4, 5 ] - +{:lang="js"} +~~~~~~~~ +var contextualize = function (fn, context) { + return function () { + return fn.apply(context, arguments) + } +}, +a = [1,2,3], +accrete = contextualize(a.concat, a); + +accrete([4,5]) + //=> [ 1, 2, 3, 4, 5 ] +~~~~~~~~ + How would this help us in a practical way? Consider building an event-driven application. For example, an MVC application would bind certain views to update events when their models change. The [Backbone] framework uses events just like this: - var someView = ..., - someModel = ...; +{:lang="js"} +~~~~~~~~ +var someView = ..., + someModel = ...; + +someModel.on('change', function () { + someView.render() +}); +~~~~~~~~ - someModel.on('change', function () { - someView.render() - }); - This tells `someModel` that when it invoked a `change` event, it should call the anonymous function that in turn invoked `someView`'s `.render` method. Wouldn't it be simpler to simply write: - someModel.on('change', someView.render); - +{:lang="js"} +~~~~~~~~ +someModel.on('change', someView.render); +~~~~~~~~ + It would, except that the implementation for `.on` and similar framework methods looks something like this: - Model.prototype.on = function (eventName, callback) { ... callback() ... } - +{:lang="js"} +~~~~~~~~ +Model.prototype.on = function (eventName, callback) { ... callback() ... } +~~~~~~~~ + Although `someView.render()` correctly sets the method's context as `someView`, `callback()` will not. What can we do without wrapping `someView.render()` in a function call as we did above? ### binding methods @@ -38,41 +50,59 @@ Before enumerating approaches, let's describe what we're trying to do. We want t When we write something like: - var unbound = someObject.someMethod; - +{:lang="js"} +~~~~~~~~ +var unbound = someObject.someMethod; +~~~~~~~~ + We're binding the name `unbound` to the method's function, but we aren't doing anything with the identity of the receiver. In most programming languages, such methods are called "unbound" methods because they aren't associated with, or "bound" to the intended receiver. So what we're really trying to do is get ahold of a *bound* method, a method that is associated with a specific receiver. We saw an obvious way to do that above, to wrap the method call in another function. Of course, we're responsible for replicating the *arity* of the method being bound. For example: - var boundSetter = function (value) { - return someObject.setSomeValue(value); - }; - +{:lang="js"} +~~~~~~~~ +var boundSetter = function (value) { + return someObject.setSomeValue(value); +}; +~~~~~~~~ + Now our bound method takes one argument, just like the function it calls. We can use a bound method anywhere: - someDomField.on('update', boundSetter); +{:lang="js"} +~~~~~~~~ +someDomField.on('update', boundSetter); +~~~~~~~~ This pattern is very handy, but it requires keeping track of these bound methods. One thing we can do is bind the method "in place," using the `let` pattern like this: - someObject.setSomeValue = (function () { - var unboundMethod = someObject.setSomeValue; - - return function (value) { - return unboundMethod.call(someObject, value); - } - })(); - +{:lang="js"} +~~~~~~~~ +someObject.setSomeValue = (function () { + var unboundMethod = someObject.setSomeValue; + + return function (value) { + return unboundMethod.call(someObject, value); + } +})(); +~~~~~~~~ + Now we know where to find it: - someDomField.on('update', someObject.setSomeValue); - +{:lang="js"} +~~~~~~~~ +someDomField.on('update', someObject.setSomeValue); +~~~~~~~~ + This is a very popular pattern, so much so that many frameworks provide helper functions to make this easy. [Underscore], for example, provides `_.bind` to return a bound copy of a function and `_.bindAll` to bind methods in place: - // bind *all* of someObject's methods in place - _.bindAll(someObject); - - // bind setSomeValue and someMethod in place - _.bindAll(someObject, 'setSomeValue', 'someMethod'); +{:lang="js"} +~~~~~~~~ +// bind *all* of someObject's methods in place +_.bindAll(someObject); + +// bind setSomeValue and someMethod in place +_.bindAll(someObject, 'setSomeValue', 'someMethod'); +~~~~~~~~ There are two considerations to ponder. First, we may be converting an instance method into an object method. Specifically, we're creating an object method that is bound to the object. @@ -82,22 +112,28 @@ This is one of the realities of "meta-programming." Each technique looks useful If you aren't working with old JavaScript environments in non-current browsers, you needn't use a framework or roll your own binding functions: JavaScript has a [`.bind`][bind] method defined for functions: - someObject.someMethod = someObject.someMethod.bind(someObject); +{:lang="js"} +~~~~~~~~ +someObject.someMethod = someObject.someMethod.bind(someObject); +~~~~~~~~ `.bind` also does some currying for you, you can bind one or more arguments in addition to the context. For example: - AccountModel.prototype.getBalancePromise(forceRemote) = { - // if forceRemote is true, always goes to the remote - // database for the most real-time value, returns - // a promise. - }; - - var account = new AccountModel(...); - - var boundGetRemoteBalancePromise = account. - getBalancePromise. - bind(account, true); - +{:lang="js"} +~~~~~~~~ +AccountModel.prototype.getBalancePromise(forceRemote) = { + // if forceRemote is true, always goes to the remote + // database for the most real-time value, returns + // a promise. +}; + +var account = new AccountModel(...); + +var boundGetRemoteBalancePromise = account. + getBalancePromise. + bind(account, true); +~~~~~~~~ + Very handy, and not just for binding contexts! [Backbone]: http://backbonejs.org diff --git a/manuscript/markdown/Instances and Classes/class.md b/manuscript/markdown/Instances and Classes/class.md index 8eba2b5..a861883 100644 --- a/manuscript/markdown/Instances and Classes/class.md +++ b/manuscript/markdown/Instances and Classes/class.md @@ -7,45 +7,54 @@ JavaScript has "classes," for some definition of "class." You've met them alread Let's see it again: Here's a class of todo items: - function Todo (name) { - this.name = name || 'Untitled'; - this.done = false; - }; - - Todo.prototype.do = function () { - this.done = true; - }; - - Todo.prototype.undo = function () { - this.done = false; - }; - +{:lang="js"} +~~~~~~~~ +function Todo (name) { + this.name = name || 'Untitled'; + this.done = false; +}; + +Todo.prototype.do = function () { + this.done = true; +}; + +Todo.prototype.undo = function () { + this.done = false; +}; +~~~~~~~~ + You can mix other functionality into this class by extending the prototype with an object: - extend(Todo.prototype, { - prioritize: function (priority) { - this.priority = priority; - }; - }); - +{:lang="js"} +~~~~~~~~ +extend(Todo.prototype, { + prioritize: function (priority) { + this.priority = priority; + }; +}); +~~~~~~~~ + Naturally, that allows us to define mixins for other classes: - var ColourCoded = { - setColourRGB: function (r, g, b) { - // ... - }, - getColourRGB: function () { - // ... - }, - setColourCSS: function (css) { - // ... - }, - getColourCSS: function () { - // ... - } - }; - - extend(Todo.prototype, ColourCoded); +{:lang="js"} +~~~~~~~~ +var ColourCoded = { + setColourRGB: function (r, g, b) { + // ... + }, + getColourRGB: function () { + // ... + }, + setColourCSS: function (css) { + // ... + }, + getColourCSS: function () { + // ... + } +}; + +extend(Todo.prototype, ColourCoded); +~~~~~~~~ This does exactly the same thing as declaring a "class," defining a "method," and adding a "mixin." How does it differ? It doesn't use the words *class*, *method*, *def(ine)* or *mixin*. And it has this `prototype` property that most other popular languages eschew. It also doesn't deal with inheritance, a deal-breaker for programmers who are attached to taxonomies. @@ -57,4 +66,4 @@ Nevertheless, JavaScript right out of the box has everything you need for defini [Extending Classes with Inheritance]: #classextension -A> One note of caution: A few libraries, such as the vile creation [YouAreDaChef](https://github.com/raganwald/YouAreDaChef#you-are-da-chef), manipulate JavaScript such that ordinary programming such as extending a prototype either don't work at all or break the library's abstraction. Think long and carefully before adopting such a library. The best libraries "Cut with JavaScript's grain." \ No newline at end of file +A> One note of caution: A few libraries, such as the vile creation [YouAreDaChef](https://github.com/raganwald/YouAreDaChef#you-are-da-chef), manipulate JavaScript such that ordinary programming such as extending a prototype either don't work at all or break the library's abstraction. Think long and carefully before adopting such a library. The best libraries "Cut with JavaScript's grain." diff --git a/manuscript/markdown/Instances and Classes/extends.md b/manuscript/markdown/Instances and Classes/extends.md index ea3a55c..56db239 100644 --- a/manuscript/markdown/Instances and Classes/extends.md +++ b/manuscript/markdown/Instances and Classes/extends.md @@ -4,68 +4,74 @@ You recall from [Composition and Extension](#extensible) that we extended a Plai Here's our `Queue`: - var Queue = function () { - extend(this, { - array: [], - head: 0, - tail: -1 - }) - }; - - extend(Queue.prototype, { - pushTail: function (value) { - return this.array[this.tail += 1] = value - }, - pullHead: function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.head] - this.array[this.head] = void 0; - this.head += 1; - return value - } - }, - isEmpty: function () { - return this.tail < this.head - } - }); +{:lang="js"} +~~~~~~~~ +var Queue = function () { + extend(this, { + array: [], + head: 0, + tail: -1 + }) +}; + +extend(Queue.prototype, { + pushTail: function (value) { + return this.array[this.tail += 1] = value + }, + pullHead: function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.head] + this.array[this.head] = void 0; + this.head += 1; + return value + } + }, + isEmpty: function () { + return this.tail < this.head + } +}); +~~~~~~~~ And here's what our `Deque` would look like before we wire things together: - var Dequeue = function () { - Queue.prototype.constructor.call(this) - }; - - Dequeue.INCREMENT = 4; - - extend(Dequeue.prototype, { - size: function () { - return this.tail - this.head + 1 - }, - pullTail: function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.tail]; - this.array[this.tail] = void 0; - this.tail -= 1; - return value - } - }, - pushHead: function (value) { - var i; - - if (this.head === 0) { - for (i = this.tail; i >= this.head; --i) { - this.array[i + this.constructor.INCREMENT] = this.array[i] - } - this.tail += this.constructor.INCREMENT; - this.head += this.constructor.INCREMENT - } - this.array[this.head -= 1] = value +{:lang="js"} +~~~~~~~~ +var Dequeue = function () { + Queue.prototype.constructor.call(this) +}; + +Dequeue.INCREMENT = 4; + +extend(Dequeue.prototype, { + size: function () { + return this.tail - this.head + 1 + }, + pullTail: function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.tail]; + this.array[this.tail] = void 0; + this.tail -= 1; + return value + } + }, + pushHead: function (value) { + var i; + + if (this.head === 0) { + for (i = this.tail; i >= this.head; --i) { + this.array[i + this.constructor.INCREMENT] = this.array[i] } - }); + this.tail += this.constructor.INCREMENT; + this.head += this.constructor.INCREMENT + } + this.array[this.head -= 1] = value + } +}); +~~~~~~~~ A> We obviously want to do all of a `Queue`'s initialization, thus we called `Queue.prototype.constructor.call(this)`. But why not just call `Queue.call(this)`? As we'll see when we wire everything together, this ensures that we're calling the correct constructor even when `Queue` itself is wired to inherit from another constructor function. @@ -77,60 +83,66 @@ Yes they can. Imagine that `Deque.prototype` was a proxy for an instance of `Que Here's such a proxy: - var QueueProxy = function () {} - - QueueProxy.prototype = Queue.prototype - -Our `QueueProxy` isn't actually a `Queue`, but its `prototype` is an alias of `Queue.prototype`. Thus, it can pick up `Queue`'s behaviour. We want to use it for our `Deque`'s prototype. Let's insert that code in our class definition: - - var Dequeue = function () { - Queue.prototype.constructor.call(this) - }; - - Dequeue.INCREMENT = 4; - - Dequeue.prototype = new QueueProxy(); - - extend(Dequeue.prototype, { - size: function () { - return this.tail - this.head + 1 - }, - pullTail: function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.tail]; - this.array[this.tail] = void 0; - this.tail -= 1; - return value - } - }, - pushHead: function (value) { - var i; - - if (this.head === 0) { - for (i = this.tail; i >= this.head; --i) { - this.array[i + this.constructor.INCREMENT] = this.array[i] - } - this.tail += this.constructor.INCREMENT; - this.head += this.constructor.INCREMENT - } - this.array[this.head -= 1] = value +{:lang="js"} +~~~~~~~~ +var QueueProxy = function () {} + +QueueProxy.prototype = Queue.prototype + +`QueueProxy` isn't actually a `Queue`, but its `prototype` is an alias of `Queue.prototype`. Thus, it can pick up `Queue`'s behaviour. We want to use it for our `Deque`'s prototype. Let's insert that code in our class definition: + +var Dequeue = function () { + Queue.prototype.constructor.call(this) +}; + +Dequeue.INCREMENT = 4; + +Dequeue.prototype = new QueueProxy(); + +extend(Dequeue.prototype, { + size: function () { + return this.tail - this.head + 1 + }, + pullTail: function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.tail]; + this.array[this.tail] = void 0; + this.tail -= 1; + return value + } + }, + pushHead: function (value) { + var i; + + if (this.head === 0) { + for (i = this.tail; i >= this.head; --i) { + this.array[i + this.constructor.INCREMENT] = this.array[i] } - }); + this.tail += this.constructor.INCREMENT; + this.head += this.constructor.INCREMENT + } + this.array[this.head -= 1] = value + } +}); +~~~~~~~~ And it seems to work: - d = new Dequeue() - d.pushTail('Hello') - d.pushTail('JavaScript') - d.pushTail('!') - d.pullHead() - //=> 'Hello' - d.pullTail() - //=> '!' - d.pullHead() - //=> 'JavaScript' +{:lang="js"} +~~~~~~~~ +d = new Dequeue() +d.pushTail('Hello') +d.pushTail('JavaScript') +d.pushTail('!') +d.pullHead() + //=> 'Hello' +d.pullTail() + //=> '!' +d.pullHead() + //=> 'JavaScript' +~~~~~~~~ Wonderful! @@ -138,20 +150,29 @@ Wonderful! How about some of the other things we've come to expect from instances? - d.constructor == Dequeue - //=> false +{:lang="js"} +~~~~~~~~ +d.constructor == Dequeue + //=> false +~~~~~~~~ Oops! Messing around with Dequeue's prototype broke this important equivalence. Luckily for us, the `constructor` property is mutable for objects we create. So, let's make a small change to `QueueProxy`: - var QueueProxy = function () { - this.constructor = Dequeue; - } - QueueProxy.prototype = Queue.prototype +{:lang="js"} +~~~~~~~~ +var QueueProxy = function () { + this.constructor = Dequeue; +} +QueueProxy.prototype = Queue.prototype +~~~~~~~~ Repeat. Now it works: - d.constructor === Dequeue - //=> true +{:lang="js"} +~~~~~~~~ +d.constructor === Dequeue + //=> true +~~~~~~~~ The `QueueProxy` function now sets the `constructor` for every instance of a `QueueProxy` (hopefully just the one we need for the `Dequeue` class). It returns the object being created (it could also return `undefined` and work. But if it carelessly returned something else, that would be assigned to `Dequeue`'s prototype, which would break our code). @@ -159,50 +180,56 @@ The `QueueProxy` function now sets the `constructor` for every instance of a `Qu Let's turn our mechanism into a function: - var child = function (parent, child) { - var proxy = function () { - this.constructor = child - } - proxy.prototype = parent.prototype; - child.prototype = new proxy(); - return child; - } +{:lang="js"} +~~~~~~~~ +var child = function (parent, child) { + var proxy = function () { + this.constructor = child + } + proxy.prototype = parent.prototype; + child.prototype = new proxy(); + return child; +} +~~~~~~~~ And use it in `Dequeue`: - var Dequeue = child(Queue, function () { - Queue.prototype.constructor.call(this) - }); - - Dequeue.INCREMENT = 4; - - extend(Dequeue.prototype, { - size: function () { - return this.tail - this.head + 1 - }, - pullTail: function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.tail]; - this.array[this.tail] = void 0; - this.tail -= 1; - return value - } - }, - pushHead: function (value) { - var i; - - if (this.head === 0) { - for (i = this.tail; i >= this.head; --i) { - this.array[i + this.constructor.INCREMENT] = this.array[i] - } - this.tail += this.constructor.INCREMENT; - this.head += this.constructor.INCREMENT - } - this.array[this.head -= 1] = value +{:lang="js"} +~~~~~~~~ +var Dequeue = child(Queue, function () { + Queue.prototype.constructor.call(this) +}); + +Dequeue.INCREMENT = 4; + +extend(Dequeue.prototype, { + size: function () { + return this.tail - this.head + 1 + }, + pullTail: function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.tail]; + this.array[this.tail] = void 0; + this.tail -= 1; + return value + } + }, + pushHead: function (value) { + var i; + + if (this.head === 0) { + for (i = this.tail; i >= this.head; --i) { + this.array[i + this.constructor.INCREMENT] = this.array[i] } - }); + this.tail += this.constructor.INCREMENT; + this.head += this.constructor.INCREMENT + } + this.array[this.head -= 1] = value + } +}); +~~~~~~~~ ### future directions diff --git a/manuscript/markdown/Instances and Classes/object-methods.md b/manuscript/markdown/Instances and Classes/object-methods.md index d9faece..52515c3 100644 --- a/manuscript/markdown/Instances and Classes/object-methods.md +++ b/manuscript/markdown/Instances and Classes/object-methods.md @@ -6,54 +6,60 @@ There is a third kind of method, one that any object (obviously including all in Object methods are really easy to create with Plain Old JavaScript Objects, because they're the only kind of method you can use. Recall from [This and That](#this): - QueueMaker = function () { - return { - array: [], - head: 0, - tail: -1, - pushTail: function (value) { - return this.array[this.tail += 1] = value - }, - pullHead: function () { - var value; - - if (this.tail >= this.head) { - value = this.array[this.head]; - this.array[this.head] = void 0; - this.head += 1; - return value - } - }, - isEmpty: function () { - return this.tail < this.head - } +{:lang="js"} +~~~~~~~~ +QueueMaker = function () { + return { + array: [], + head: 0, + tail: -1, + pushTail: function (value) { + return this.array[this.tail += 1] = value + }, + pullHead: function () { + var value; + + if (this.tail >= this.head) { + value = this.array[this.head]; + this.array[this.head] = void 0; + this.head += 1; + return value } - }; - + }, + isEmpty: function () { + return this.tail < this.head + } + } +}; +~~~~~~~~ + `pushTail`, `pullHead`, and `isEmpty` are object methods. Also, from [encapsulation](#hiding-state): - var stack = (function () { - var obj = { - array: [], - index: -1, - push: function (value) { - return obj.array[obj.index += 1] = value - }, - pop: function () { - var value = obj.array[obj.index]; - obj.array[obj.index] = void 0; - if (obj.index >= 0) { - obj.index -= 1 - } - return value - }, - isEmpty: function () { - return obj.index < 0 - } - }; - - return obj; - })(); +{:lang="js"} +~~~~~~~~ +var stack = (function () { + var obj = { + array: [], + index: -1, + push: function (value) { + return obj.array[obj.index += 1] = value + }, + pop: function () { + var value = obj.array[obj.index]; + obj.array[obj.index] = void 0; + if (obj.index >= 0) { + obj.index -= 1 + } + return value + }, + isEmpty: function () { + return obj.index < 0 + } + }; + + return obj; +})(); +~~~~~~~~ Although they don't refer to the object, `push`, `pop`, and `isEmpty` semantically interact with the opaque data structure represented by the object, so they are object methods too. @@ -61,21 +67,24 @@ Although they don't refer to the object, `push`, `pop`, and `isEmpty` semantical Instances of constructors can have object methods as well. Typically, object methods are added in the constructor. Here's a gratuitous example, a widget model that has a read-only `id`: - var WidgetModel = function (id, attrs) { - extend(this, attrs || {}); - this.id = function () { return id } - } - - extend(WidgetModel.prototype, { - set: function (attr, value) { - this[attr] = value; - return this; - }, - get: function (attr) { - return this[attr] - } - }); +{:lang="js"} +~~~~~~~~ +var WidgetModel = function (id, attrs) { + extend(this, attrs || {}); + this.id = function () { return id } +} + +extend(WidgetModel.prototype, { + set: function (attr, value) { + this[attr] = value; + return this; + }, + get: function (attr) { + return this[attr] + } +}); +~~~~~~~~ `set` and `get` are instance methods, but `id` is an object method: Each object has its own `id` closure, where `id` is bound to the id of the widget by the argument `id` in the constructor. The advantage of this approach is that instances can have different object methods, or object methods with their own closures as in this case. The disadvantage is that every object has its own methods, which uses up much more memory than instance methods, which are shared amongst all instances. -T> Object methods are defined within the object. So if you have several different "instances" of the same object, there will be an object method for each object. Object methods can be associated with any object, not just those created with the `new` keyword. Instance methods apply to instances, objects created with the `new` keyword. Instance methods are defined in a prototype and are shared by all instances. \ No newline at end of file +T> Object methods are defined within the object. So if you have several different "instances" of the same object, there will be an object method for each object. Object methods can be associated with any object, not just those created with the `new` keyword. Instance methods apply to instances, objects created with the `new` keyword. Instance methods are defined in a prototype and are shared by all instances. diff --git a/manuscript/markdown/Instances and Classes/recipes/bound.md b/manuscript/markdown/Instances and Classes/recipes/bound.md index fdedd07..39df6ee 100644 --- a/manuscript/markdown/Instances and Classes/recipes/bound.md +++ b/manuscript/markdown/Instances and Classes/recipes/bound.md @@ -2,111 +2,132 @@ Earlier, we saw a recipe for [getWith](#getWith) that plays nicely with properties: - function get (attr) { - return function (obj) { - return obj[attr] - } - } +{:lang="js"} +~~~~~~~~ +function get (attr) { + return function (obj) { + return obj[attr] + } +} +~~~~~~~~ Simple and useful. But now that we've spent some time looking at objects with methods we can see that `get` (and `pluck`) has a failure mode. Specifically, it's not very useful if we ever want to get a *method*, since we'll lose the context. Consider some hypothetical class: - function InventoryRecord (apples, oranges, eggs) { - this.record = { - apples: apples, - oranges: oranges, - eggs: eggs - } - } - - InventoryRecord.prototype.apples = function apples () { - return this.record.apples - } - - InventoryRecord.prototype.oranges = function oranges () { - return this.record.oranges - } - - InventoryRecord.prototype.eggs = function eggs () { - return this.record.eggs - } - - var inventories = [ - new InventoryRecord( 0, 144, 36 ), - new InventoryRecord( 240, 54, 12 ), - new InventoryRecord( 24, 12, 42 ) - ]; - +{:lang="js"} +~~~~~~~~ +function InventoryRecord (apples, oranges, eggs) { + this.record = { + apples: apples, + oranges: oranges, + eggs: eggs + } +} + +InventoryRecord.prototype.apples = function apples () { + return this.record.apples +} + +InventoryRecord.prototype.oranges = function oranges () { + return this.record.oranges +} + +InventoryRecord.prototype.eggs = function eggs () { + return this.record.eggs +} + +var inventories = [ + new InventoryRecord( 0, 144, 36 ), + new InventoryRecord( 240, 54, 12 ), + new InventoryRecord( 24, 12, 42 ) +~~~~~~~~ +]; + Now how do we get all the egg counts? - mapWith(getWith('eggs'))(inventories) - //=> [ [Function: eggs], - // [Function: eggs], - // [Function: eggs] ] +{:lang="js"} +~~~~~~~~ +mapWith(getWith('eggs'))(inventories) + //=> [ [Function: eggs], + // [Function: eggs], + // [Function: eggs] ] +~~~~~~~~ And if we try applying those functions... - mapWith(getWith('eggs'))(inventories).map( - function (unboundmethod) { - return unboundmethod() - } - ) - //=> TypeError: Cannot read property 'eggs' of undefined - -Of course, these are unbound methods we're "getting" from each object. Here's a new version of `get` that plays nicely with methods. It uses [variadic](#ellipses): +{:lang="js"} +~~~~~~~~ +mapWith(getWith('eggs'))(inventories).map( + function (unboundmethod) { + return unboundmethod() + } +) + //=> TypeError: Cannot read property 'eggs' of undefined +~~~~~~~~ - var bound = variadic( function (messageName, args) { - - if (args.length === 0) { - return function (instance) { - return instance[messageName].bind(instance) - } - } - else { - return function (instance) { - return Function.prototype.bind.apply( - instance[messageName], [instance].concat(args) - ) - } - } - }); - - mapWith(bound('eggs'))(inventories).map( - function (boundmethod) { - return boundmethod() - } - ) - //=> [ 36, 12, 42 ] +Of course, these are unbound methods we're "getting" from each object. Here's a new version of `get` that plays nicely with methods. It uses [variadic](#ellipses): -`bound` is the recipe for getting a bound method from an object by name. It has other uses, such as callbacks. `bound('render')(aView)` is equivalent to `aView.render.bind(aView)`. There's an option to add a variable number of additional arguments, handled by: +{:lang="js"} +~~~~~~~~ +var bound = variadic( function (messageName, args) { + if (args.length === 0) { + return function (instance) { + return instance[messageName].bind(instance) + } + } + else { return function (instance) { return Function.prototype.bind.apply( instance[messageName], [instance].concat(args) ) } - + } +}); + +mapWith(bound('eggs'))(inventories).map( + function (boundmethod) { + return boundmethod() + } +) + //=> [ 36, 12, 42 ] +~~~~~~~~ + +`bound` is the recipe for getting a bound method from an object by name. It has other uses, such as callbacks. `bound('render')(aView)` is equivalent to `aView.render.bind(aView)`. There's an option to add a variable number of additional arguments, handled by: + +{:lang="js"} +~~~~~~~~ +return function (instance) { + return Function.prototype.bind.apply( + instance[messageName], [instance].concat(args) + ) +} +~~~~~~~~ + The exact behaviour will be covered in [Binding Functions to Contexts](#binding). You can use it like this to add arguments to the bound function to be evaluated: - InventoryRecord.prototype.add = function (item, amount) { - this.record[item] || (this.record[item] = 0); - this.record[item] += amount; - return this; - } - - mapWith(bound('add', 'eggs', 12))(inventories).map( - function (boundmethod) { - return boundmethod() - } - ) - //=> [ { record: - // { apples: 0, - // oranges: 144, - // eggs: 48 } }, - // { record: - // { apples: 240, - // oranges: 54, - // eggs: 24 } }, - // { record: - // { apples: 24, - // oranges: 12, - // eggs: 54 } } ] +{:lang="js"} +~~~~~~~~ +InventoryRecord.prototype.add = function (item, amount) { + this.record[item] || (this.record[item] = 0); + this.record[item] += amount; + return this; +} + +mapWith(bound('add', 'eggs', 12))(inventories).map( + function (boundmethod) { + return boundmethod() + } +) + //=> [ { record: + // { apples: 0, + // oranges: 144, + // eggs: 48 } }, + // { record: + // { apples: 240, + // oranges: 54, + // eggs: 24 } }, + // { record: + // { apples: 24, + // oranges: 12, + // eggs: 54 } } ] +~~~~~~~~ diff --git a/manuscript/markdown/Instances and Classes/recipes/curry.md b/manuscript/markdown/Instances and Classes/recipes/curry.md index 04a84e7..a2b2ccd 100644 --- a/manuscript/markdown/Instances and Classes/recipes/curry.md +++ b/manuscript/markdown/Instances and Classes/recipes/curry.md @@ -2,49 +2,55 @@ We discussed currying in [Closures](#closures) and [Partial Application, Binding, and Currying](#pabc). Here is the recipe for a higher-order function that curries its argument function. It works with any function that has a fixed length, and it lets you provide as many arguments as you like. - var __slice = Array.prototype.slice; - - function curry (fn) { - var arity = fn.length; - - return given([]); - - function given (argsSoFar) { - return function helper () { - var updatedArgsSoFar = argsSoFar.concat(__slice.call(arguments, 0)); - - if (updatedArgsSoFar.length >= arity) { - return fn.apply(this, updatedArgsSoFar) - } - else return given(updatedArgsSoFar) - } +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function curry (fn) { + var arity = fn.length; + + return given([]); + + function given (argsSoFar) { + return function helper () { + var updatedArgsSoFar = argsSoFar.concat(__slice.call(arguments, 0)); + + if (updatedArgsSoFar.length >= arity) { + return fn.apply(this, updatedArgsSoFar) } - + else return given(updatedArgsSoFar) } - - function sumOfFour (a, b, c, d) { return a + b + c + d } - - var curried = curry(sumOfFour); - - curried(1)(2)(3)(4) - //=> 10 - - curried(1,2)(3,4) - //=> 10 - - curried(1,2,3,4) - //=> 10 + } + +} + +function sumOfFour (a, b, c, d) { return a + b + c + d } + +var curried = curry(sumOfFour); + +curried(1)(2)(3)(4) + //=> 10 + +curried(1,2)(3,4) + //=> 10 + +curried(1,2,3,4) + //=> 10 +~~~~~~~~ We saw earlier that you can derive a curry function from a partial application function. The reverse is also true: - function callLeft (fn) { - return curry(fn).apply(null, __slice.call(arguments, 1)) - } - - callLeft(sumOfFour, 1)(2, 3, 4) - //=> 10 - - callLeft(sumOfFour, 1, 2)(3, 4) - //=> 10 - -(This is a little different from the previous left partial functions in that it returns a *curried* function). \ No newline at end of file +{:lang="js"} +~~~~~~~~ +function callLeft (fn) { + return curry(fn).apply(null, __slice.call(arguments, 1)) +} + +callLeft(sumOfFour, 1)(2, 3, 4) + //=> 10 + +callLeft(sumOfFour, 1, 2)(3, 4) + //=> 10 +~~~~~~~~ + +(This is a little different from the previous left partial functions in that it returns a *curried* function). diff --git a/manuscript/markdown/Instances and Classes/recipes/fluent.md b/manuscript/markdown/Instances and Classes/recipes/fluent.md index 9fd4292..ab767b1 100644 --- a/manuscript/markdown/Instances and Classes/recipes/fluent.md +++ b/manuscript/markdown/Instances and Classes/recipes/fluent.md @@ -2,52 +2,61 @@ Object and instance methods can be bifurcated into two classes: Those that query something, and those that update something. Most design philosophies arrange things such that update methods return the value being updated. For example: - function Cake () {} - - extend(Cake.prototype, { - setFlavour: function (flavour) { - return this.flavour = flavour - }, - setLayers: function (layers) { - return this.layers = layers - }, - bake: function () { - // do some baking - } - }); - - var cake = new Cake(); - cake.setFlavour('chocolate'); - cake.setLayers(3); - cake.bake(); +{:lang="js"} +~~~~~~~~ +function Cake () {} + +extend(Cake.prototype, { + setFlavour: function (flavour) { + return this.flavour = flavour + }, + setLayers: function (layers) { + return this.layers = layers + }, + bake: function () { + // do some baking + } +}); + +var cake = new Cake(); +cake.setFlavour('chocolate'); +cake.setLayers(3); +cake.bake(); +~~~~~~~~ Having methods like `setFlavour` return the value being set mimics the behaviour of assignment, where `cake.flavour = 'chocolate'` is an expression that in addition to setting a property also evaluates to the value `'chocolate'`. The [fluent] style presumes that most of the time when you perform an update you are more interested in doing other things with the receiver than the values being passed as argument(s), so the rule is to return the receiver unless the method is a query: - function Cake () {} - - extend(Cake.prototype, { - setFlavour: function (flavour) { - this.flavour = flavour; - return this - }, - setLayers: function (layers) { - this.layers = layers; - return this - }, - bake: function () { - // do some baking - return this - } - }); +{:lang="js"} +~~~~~~~~ +function Cake () {} + +extend(Cake.prototype, { + setFlavour: function (flavour) { + this.flavour = flavour; + return this + }, + setLayers: function (layers) { + this.layers = layers; + return this + }, + bake: function () { + // do some baking + return this + } +}); +~~~~~~~~ The code to work with cakes is now easier to read and less repetitive: - var cake = new Cake(). - setFlavour('chocolate'). - setLayers(3). - bake(); +{:lang="js"} +~~~~~~~~ +var cake = new Cake(). + setFlavour('chocolate'). + setLayers(3). + bake(); +~~~~~~~~ For one-liners like setting a property, this is fine. But some functions are longer, and we want to signal the intent of the method at the top, not buried at the bottom. Normally this is done in the method's name, but fluent interfaces are rarely written to include methods like `setLayersAndReturnThis`. @@ -55,19 +64,25 @@ For one-liners like setting a property, this is fine. But some functions are lon The `fluent` method decorator solves this problem: - function fluent (methodBody) { - return function () { - methodBody.apply(this, arguments); - return this; - } - } +{:lang="js"} +~~~~~~~~ +function fluent (methodBody) { + return function () { + methodBody.apply(this, arguments); + return this; + } +} +~~~~~~~~ Now you can write methods like this: - Cake.prototype.bake = fluent( function () { - // do some baking - // using many lines of code - // and possibly multiple returns - }); +{:lang="js"} +~~~~~~~~ +Cake.prototype.bake = fluent( function () { + // do some baking + // using many lines of code + // and possibly multiple returns +}); +~~~~~~~~ -It's obvious at a glance that this method is "fluent." \ No newline at end of file +It's obvious at a glance that this method is "fluent." diff --git a/manuscript/markdown/Instances and Classes/recipes/invoke.md b/manuscript/markdown/Instances and Classes/recipes/invoke.md index fe5f8a8..5f31a0b 100644 --- a/manuscript/markdown/Instances and Classes/recipes/invoke.md +++ b/manuscript/markdown/Instances and Classes/recipes/invoke.md @@ -8,47 +8,62 @@ It's not an unprecedented use case. The Ruby programming language has a handy fe The only trouble with `.apply` is that being a method, it doesn't compose nicely with other functions like combinators. So, we create a function that allows us to use it as a combinator: - var __slice = Array.prototype.slice; +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; - function invoke (fn) { - var args = __slice.call(arguments, 1); - - return function (instance) { - return fn.apply(instance, args) - } - } +function invoke (fn) { + var args = __slice.call(arguments, 1); + + return function (instance) { + return fn.apply(instance, args) + } +} +~~~~~~~~ For example, let's say someone else's code gives you an array of objects that are in part, but not entirely like arrays. Something like: - var data = [ - { 0: 'zero', - 1: 'one', - 2: 'two', - foo: 'foo', - length: 3 }, - // ... - ]; +{:lang="js"} +~~~~~~~~ +var data = [ + { 0: 'zero', + 1: 'one', + 2: 'two', + foo: 'foo', + length: 3 }, + // ... +]; +~~~~~~~~ We can use the pattern from [Partial Application, Binding, and Currying](#pabc) to create a context-dependent copy function: - var __copy = callFirst(Array.prototype.slice, 0); - +{:lang="js"} +~~~~~~~~ +var __copy = callFirst(Array.prototype.slice, 0); +~~~~~~~~ + And now we can compose `mapWith` with `invoke` to convert the data to arrays: - - mapWith(invoke(__copy))(data) - //=> [ - // [ 'zero', 'one', 'two' ], - // // ... - // ] + +{:lang="js"} +~~~~~~~~ +mapWith(invoke(__copy))(data) + //=> [ + // [ 'zero', 'one', 'two' ], + // // ... + // ] +~~~~~~~~ `invoke` is useful when you have the function and are looking for the instance. It can be written "the other way around," for when you have the instance and are looking for the function: - function instanceEval (instance) { - return function (fn) { - var args = __slice.call(arguments, 1); - - return fn.apply(instance, args) - } - } - - var args = instanceEval(arguments)(__slice, 0); \ No newline at end of file +{:lang="js"} +~~~~~~~~ +function instanceEval (instance) { + return function (fn) { + var args = __slice.call(arguments, 1); + + return fn.apply(instance, args) + } +} + +var args = instanceEval(arguments)(__slice, 0); +~~~~~~~~ diff --git a/manuscript/markdown/Instances and Classes/recipes/named-once.md b/manuscript/markdown/Instances and Classes/recipes/named-once.md index 6219798..1c6e5c8 100644 --- a/manuscript/markdown/Instances and Classes/recipes/named-once.md +++ b/manuscript/markdown/Instances and Classes/recipes/named-once.md @@ -2,85 +2,97 @@ As we noted when we saw the recipe for [once](#once), you do have to be careful that you are calling the function `once` returns multiple times. If you keep calling `once`, you'll get a new function that executes once, so you'll keep calling your function: - once(function () { - return 'sure, why not?' - })() - //=> 'sure, why not?' +{:lang="js"} +~~~~~~~~ +once(function () { + return 'sure, why not?' +})() + //=> 'sure, why not?' - once(function () { - return 'sure, why not?' - })() - //=> 'sure, why not?' +once(function () { + return 'sure, why not?' +})() + //=> 'sure, why not?' +~~~~~~~~ This is expected, but sometimes not what we want. Instead of the simple implementation, we can use a *named once*: - function once (fn) { - var done = false, - testAndSet; - - if (!!fn.name) { - testAndSet = function () { - this["__once__"] || (this["__once__"] = {}) - if (this["__once__"][fn.name]) return true; - this["__once__"][fn.name] = true; - return false - } - } - else { - testAndSet = function (fn) { - if (done) return true; - done = true; - return false - } - } - - return function () { - return testAndSet.call(this) ? void 0 : fn.apply(this, arguments) - } +{:lang="js"} +~~~~~~~~ +function once (fn) { + var done = false, + testAndSet; + + if (!!fn.name) { + testAndSet = function () { + this["__once__"] || (this["__once__"] = {}) + if (this["__once__"][fn.name]) return true; + this["__once__"][fn.name] = true; + return false + } + } + else { + testAndSet = function (fn) { + if (done) return true; + done = true; + return false } + } + + return function () { + return testAndSet.call(this) ? void 0 : fn.apply(this, arguments) + } +} +~~~~~~~~ If you call this with just a function, it behaves exactly like our first recipe. But if you supply a named function, you get a different behaviour: - once(function askedOnDate () { - return 'sure, why not?' - })() - //=> 'sure, why not?' - - once(function askedOnDate () { - return 'sure, why not?' - })() - //=> undefined +{:lang="js"} +~~~~~~~~ +once(function askedOnDate () { + return 'sure, why not?' +})() + //=> 'sure, why not?' + +once(function askedOnDate () { + return 'sure, why not?' +})() + //=> undefined +~~~~~~~~ The named once adds a property, `__once__`, to the context where the function is called and uses it to keep track of which named functions have and haven't been run. One very powerful use is for defining object methods that should only be evaluated once, such as an initialization method. Normally this is done in the constructor, but you might write a "fluent" object that lets you call various setters: - function Widget () {}; - - Widget.prototype.setVolume = function setVolume (volume) { - this.volume = volume; - return this; - } - - Widget.prototype.setDensity = function setDensity (density) { - this.density = density; - return this; - } - - Widget.prototype.setLength = function setLength (length) { - this.length = length; - return this; - } - - Widget.prototype.initialize = once(function initialize() { - // do some initialization - return this; - }); - - var w = new Widget() - .setVolume(...) - .setDensity(...) - .setLength(...) - .initialize(); - +{:lang="js"} +~~~~~~~~ +function Widget () {}; + +Widget.prototype.setVolume = function setVolume (volume) { + this.volume = volume; + return this; +} + +Widget.prototype.setDensity = function setDensity (density) { + this.density = density; + return this; +} + +Widget.prototype.setLength = function setLength (length) { + this.length = length; + return this; +} + +Widget.prototype.initialize = once(function initialize() { + // do some initialization + return this; +}); + +var w = new Widget() + .setVolume(...) + .setDensity(...) + .setLength(...) + .initialize(); +~~~~~~~~ + If you later call `w.initialize()`, it won't be initialized again. You need a named `once`, because an ordinary `once` would be called once for every instance sharing the same prototype, whereas the named once will keep track of whether it has been run separately for each instance. Caveat: Every instance will have a `__once__` property. If you later write code that iterates over every property, you'll have to take care not to interact with it. diff --git a/manuscript/markdown/Instances and Classes/recipes/send.md b/manuscript/markdown/Instances and Classes/recipes/send.md index 4878012..535e335 100644 --- a/manuscript/markdown/Instances and Classes/recipes/send.md +++ b/manuscript/markdown/Instances and Classes/recipes/send.md @@ -2,38 +2,50 @@ Previously, we saw that the recipe [bound](#bound) can be used to get a bound method from an instance. Unfortunately, invoking such methods is a little messy: - mapWith(bound('eggs'))(inventories).map( - function (boundmethod) { - return boundmethod() - } - ) - //=> [ 36, 12, 42 ] +{:lang="js"} +~~~~~~~~ +mapWith(bound('eggs'))(inventories).map( + function (boundmethod) { + return boundmethod() + } +) + //=> [ 36, 12, 42 ] +~~~~~~~~ As we noted, it's ugly to write - function (boundmethod) { - return boundmethod() - } +{:lang="js"} +~~~~~~~~ +function (boundmethod) { + return boundmethod() +} +~~~~~~~~ So instead, we write a new recipe: - var send = variadic( function (args) { - var fn = bound.apply(this, args); - - return function (instance) { - return fn(instance)(); - } - }) - - mapWith(send('apples'))(inventories) - //=> [ 0, 240, 24 ] - +{:lang="js"} +~~~~~~~~ +var send = variadic( function (args) { + var fn = bound.apply(this, args); + + return function (instance) { + return fn(instance)(); + } +}) + +mapWith(send('apples'))(inventories) + //=> [ 0, 240, 24 ] +~~~~~~~~ + `send('apples')` works very much like `&:apples` in the Ruby programming language. You may ask, why retain `bound`? Well, sometimes we want the function but don't want to evaluate it immediately, such as when creating callbacks. `bound` does that well. Here's a robust version that doesn't rely on `bound`: - var send = variadic( function (methodName, leftArguments) { - return variadic( function (receiver, rightArguments) { - return receiver[methodName].apply(receiver, leftArguments.concat(rightArguments)) - }) - }); \ No newline at end of file +{:lang="js"} +~~~~~~~~ +var send = variadic( function (methodName, leftArguments) { + return variadic( function (receiver, rightArguments) { + return receiver[methodName].apply(receiver, leftArguments.concat(rightArguments)) + }) +}); +~~~~~~~~ diff --git a/manuscript/markdown/Instances and Classes/recipes/unbind.md b/manuscript/markdown/Instances and Classes/recipes/unbind.md index 1be8b1c..a58dbd3 100644 --- a/manuscript/markdown/Instances and Classes/recipes/unbind.md +++ b/manuscript/markdown/Instances and Classes/recipes/unbind.md @@ -2,89 +2,98 @@ One of the specifications for `Function.prototype.bind` is that it creates a binding that cannot be overridden. In other words: - function myName () { return this.name } - - var harpo = { name: 'Harpo' }, - chico = { name: 'Chico' }, - groucho = { name: 'Groucho' }; - - var fh = myName.bind(harpo); - fh() - //=> 'Harpo' - - var fc = myName.bind(chico); - fc() - //=> 'Chico' +{:lang="js"} +~~~~~~~~ +function myName () { return this.name } + +var harpo = { name: 'Harpo' }, + chico = { name: 'Chico' }, + groucho = { name: 'Groucho' }; + +var fh = myName.bind(harpo); +fh() + //=> 'Harpo' + +var fc = myName.bind(chico); +fc() + //=> 'Chico' +~~~~~~~~ This looks great. But what happens if we attempt to **re**-bind a bound function, either with `bind` or `.call`? - var fhg = fh.bind(groucho); - fhg() - //=> 'Harpo' - - fc.call(groucho) - //=> 'Chico' - - fh.apply(groucho, []) - //=> 'Harpo' - +{:lang="js"} +~~~~~~~~ +var fhg = fh.bind(groucho); +fhg() + //=> 'Harpo' + +fc.call(groucho) + //=> 'Chico' + +fh.apply(groucho, []) + //=> 'Harpo' +~~~~~~~~ + Bzzt! You cannot override the context of a function that has already been bound, even if you're creating a new function with `.bind`. You also don't want to roll your own `bind` function that allows rebinding, lest you be bitten by someone else's code that expects that a bound function cannot be rebound. (One such case is where bound functions--such as callbacks--are stored in an array. Evaluating `callbacks[index]()` will override the bound context with the array unless the context cannot be overridden.)[^reddit] ### the recipe Our version of `bind` memoizes the original function so that you can later call `unbind` to restore it for rebinding. - var unbind = function unbind (fn) { - return fn.unbound ? unbind(fn.unbound()) : fn - }; - - function bind (fn, context, force) { - var unbound, bound; - - if (force) { - fn = unbind(fn) - } - bound = function () { - return fn.apply(context, arguments) - }; - bound.unbound = function () { - return fn; - }; - - return bound; - } - - function myName () { return this.name } - - var harpo = { name: 'Harpo' }, - chico = { name: 'Chico' }, - groucho = { name: 'Groucho' }; - - var fh = bind(myName, harpo); - fh() - //=> 'Harpo' - - var fc = bind(myName, chico); - fc() - //=> 'Chico' - - var fhg = bind(fh, groucho); - fhg() - //=> 'Harpo' - - var fhug = bind(fh, groucho, true); - fhug() - //=> 'Groucho' - - var fhug2 = bind(unbind(fh), groucho); - fhug() - //=> 'Groucho' - - fc.unbound().call(groucho) - //=> 'Groucho' - - unbind(fh).apply(groucho, []) - //=> 'Groucho' - +{:lang="js"} +~~~~~~~~ +var unbind = function unbind (fn) { + return fn.unbound ? unbind(fn.unbound()) : fn +}; + +function bind (fn, context, force) { + var unbound, bound; + + if (force) { + fn = unbind(fn) + } + bound = function () { + return fn.apply(context, arguments) + }; + bound.unbound = function () { + return fn; + }; + + return bound; +} + +function myName () { return this.name } + +var harpo = { name: 'Harpo' }, + chico = { name: 'Chico' }, + groucho = { name: 'Groucho' }; + +var fh = bind(myName, harpo); +fh() + //=> 'Harpo' + +var fc = bind(myName, chico); +fc() + //=> 'Chico' + +var fhg = bind(fh, groucho); +fhg() + //=> 'Harpo' + +var fhug = bind(fh, groucho, true); +fhug() + //=> 'Groucho' + +var fhug2 = bind(unbind(fh), groucho); +fhug() + //=> 'Groucho' + +fc.unbound().call(groucho) + //=> 'Groucho' + +unbind(fh).apply(groucho, []) + //=> 'Groucho' +~~~~~~~~ + [walled garden]: https://github.com/raganwald/homoiconic/blob/master/2012/12/walled-gardens.md#programmings-walled-gardens -[^reddit]: Isnotlupus on Reddit suggested [this line of thinking](http://www.reddit.com/r/javascript/comments/15ix7s/weak_binding_in_javascript/c7n10yd) against "weak binding" functions. \ No newline at end of file +[^reddit]: Isnotlupus on Reddit suggested [this line of thinking](http://www.reddit.com/r/javascript/comments/15ix7s/weak_binding_in_javascript/c7n10yd) against "weak binding" functions. diff --git a/manuscript/markdown/Instances and Classes/revisiting-partial.md b/manuscript/markdown/Instances and Classes/revisiting-partial.md index d13074c..d81d716 100644 --- a/manuscript/markdown/Instances and Classes/revisiting-partial.md +++ b/manuscript/markdown/Instances and Classes/revisiting-partial.md @@ -2,43 +2,58 @@ Now that we've seen how function contexts work, we can revisit the subject of partial application. Recall our recipe for a generalized left partial application: - var callLeft = variadic( function (fn, args) { - return variadic( function (remainingArgs) { - return fn.apply(this, args.concat(remainingArgs)) - }) - }) - +{:lang="js"} +~~~~~~~~ +var callLeft = variadic( function (fn, args) { + return variadic( function (remainingArgs) { + return fn.apply(this, args.concat(remainingArgs)) + }) +}) +~~~~~~~~ + `Function.prototype.bind` can sometimes be used to accomplish the same thing, but will be much faster. For example, instead of: - function add (verb, a, b) { - return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) - } - - var sumFive = callLeft(add, 'sum', 5); - - sumFive(6) - //=> 'The sum of 5 and 6 is 11' - +{:lang="js"} +~~~~~~~~ +function add (verb, a, b) { + return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) +} + +var sumFive = callLeft(add, 'sum', 5); + +sumFive(6) + //=> 'The sum of 5 and 6 is 11' +~~~~~~~~ + You can write: - var totalSix = add.bind(null, 'total', 6); - - totalSix(5) - //=> 'The total of 6 and 5 is 11' +{:lang="js"} +~~~~~~~~ +var totalSix = add.bind(null, 'total', 6); + +totalSix(5) + //=> 'The total of 6 and 5 is 11' +~~~~~~~~ The catch is the first parameter to `.bind`: It sets the context. If you write functions that don't use the context, like our `.add`, You can use `.bind` to do left partial application. But if you want to partially apply a method or other function where the context must be preserved, you can't use `.bind`. You can use the recipes given in *JavaScript Allongé* because they preserve the context properly. Typically, context matters when you want to perform partial application on methods. So for an extremely simple example, we often use `Array.prototype.slice` to convert `arguments` to an array. So instead of: - var __slice = Array.prototype.slice; - - var array = __slice.call(arguments, 0); - +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +var array = __slice.call(arguments, 0); +~~~~~~~~ + We could write: - var __copy = callFirst(Array.prototype.slice, 0); - - var array = __copy.call(arguments) +{:lang="js"} +~~~~~~~~ +var __copy = callFirst(Array.prototype.slice, 0); + +var array = __copy.call(arguments) +~~~~~~~~ The other catch is that `.bind` only does left partial evaluation. If you want to do right partial application, you'll need `callLast` or `callRight`. @@ -48,65 +63,79 @@ The terms "partial application" and "currying" are closely related but not synon Code is, as usual, much clearer than words. Recall: - function add (verb, a, b) { - return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) - } - - add('sum', 5, '6') - //=> 'The sum of 5 and 6 is 11' - +{:lang="js"} +~~~~~~~~ +function add (verb, a, b) { + return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) +} + +add('sum', 5, '6') + //=> 'The sum of 5 and 6 is 11' +~~~~~~~~ + Here is the curried version: - function addCurried (verb) { - return function (a) { - return function (b) { - return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) - } - } +{:lang="js"} +~~~~~~~~ +function addCurried (verb) { + return function (a) { + return function (b) { + return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) } - - addCurried('total')(6)(5) - //=> 'The total of 6 and 5 is 11' - + } +} + +addCurried('total')(6)(5) + //=> 'The total of 6 and 5 is 11' +~~~~~~~~ + Currying by hand would be an incredible effort, but its close relationship with partial application means that if you have left partial application, you can derive currying. Or if you have currying, you can derive left partial application. Let's derive currying from `callFirst`. [Recall](#simple-partial): - var __slice = Array.prototype.slice; - - function callFirst (fn, larg) { - return function () { - var args = __slice.call(arguments, 0); - - return fn.apply(this, [larg].concat(args)) - } - } +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function callFirst (fn, larg) { + return function () { + var args = __slice.call(arguments, 0); + + return fn.apply(this, [larg].concat(args)) + } +} +~~~~~~~~ Here's a function that curries any function with two arguments: - function curryTwo (fn) { - return function (x) { - return callFirst(fn, x) - } - } - - function add2 (a, b) { return a + b } - - curryTwo(add2)(5)(6) - //=> 11 +{:lang="js"} +~~~~~~~~ +function curryTwo (fn) { + return function (x) { + return callFirst(fn, x) + } +} + +function add2 (a, b) { return a + b } + +curryTwo(add2)(5)(6) + //=> 11 +~~~~~~~~ And from there we can curry a function with three arguments: - function curryThree (fn) { - return function (x) { - return curryTwo(callFirst(fn, x)) - } - } +{:lang="js"} +~~~~~~~~ +function curryThree (fn) { + return function (x) { + return curryTwo(callFirst(fn, x)) + } +} + +function add3 (verb, a, b) { + return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) +} + +curryThree(add3)('sum')(5)(6) + //=> 'The sum of 5 and 6 is 11' +~~~~~~~~ - function add3 (verb, a, b) { - return "The " + verb + " of " + a + ' and ' + b + ' is ' + (a + b) - } - - curryThree(add3)('sum')(5)(6) - //=> 'The sum of 5 and 6 is 11' - We'll develop a generalized curry function in the recipes. But to summarize the difference between currying and partial application, currying is an operation that transforms a function taking two or more arguments into a function that takes a single argument and partially applies it to the function and then curries the rest of the arguments. - \ No newline at end of file diff --git a/manuscript/markdown/Instances and Classes/simple.md b/manuscript/markdown/Instances and Classes/simple.md index 0e91d47..854525a 100644 --- a/manuscript/markdown/Instances and Classes/simple.md +++ b/manuscript/markdown/Instances and Classes/simple.md @@ -4,17 +4,26 @@ As you recall from our code for making objects [extensible](#extensible), we wro Let's strip a function down to the very bare essentials: - var Ur = function () {}; +{:lang="js"} +~~~~~~~~ +var Ur = function () {}; +~~~~~~~~ This doesn't look like a factory function: It doesn't have an expression that yields a Plain Old JavaScript Object when the function is applied. Yet, there is a way to make an object out of it. Behold the power of the `new` keyword: - new Ur() - //=> {} - +{:lang="js"} +~~~~~~~~ +new Ur() + //=> {} +~~~~~~~~ + We got an object back! What can we find out about this object? - new Ur() === new Ur() - //=> false +{:lang="js"} +~~~~~~~~ +new Ur() === new Ur() + //=> false +~~~~~~~~ Every time we call `new` with a function and get an object back, we get a unique object. We could call these "Objects created with the `new` keyword," but this would be cumbersome. So we're going to call them *instances*. Instances of what? Instances of the function that creates them. So given `var i = new Ur()`, we say that `i` is an instance of `Ur`. @@ -24,45 +33,63 @@ For reasons that will be explained after we've discussed prototypes, we also say There's more. Here's something you may not know about functions: - Ur.prototype - //=> {} - +{:lang="js"} +~~~~~~~~ +Ur.prototype + //=> {} +~~~~~~~~ + What's this prototype? Let's run our standard test: - (function () {}).prototype === (function () {}).prototype - //=> false +{:lang="js"} +~~~~~~~~ +(function () {}).prototype === (function () {}).prototype + //=> false +~~~~~~~~ Every function is initialized with its own unique `prototype`. What does it do? Let's try something: - Ur.prototype.language = 'JavaScript'; - - var continent = new Ur(); - //=> {} - continent.language - //=> 'JavaScript' +{:lang="js"} +~~~~~~~~ +Ur.prototype.language = 'JavaScript'; + +var continent = new Ur(); + //=> {} +continent.language + //=> 'JavaScript' +~~~~~~~~ That's very interesting! Instances seem to behave as if they had the same elements as their constructor's prototype. Let's try a few things: - continent.language = 'CoffeeScript'; - continent - //=> {language: 'CoffeeScript'} - continent.language - //=> 'CoffeeScript' - Ur.prototype.language - 'JavaScript' +{:lang="js"} +~~~~~~~~ +continent.language = 'CoffeeScript'; +continent + //=> {language: 'CoffeeScript'} +continent.language + //=> 'CoffeeScript' +Ur.prototype.language + 'JavaScript' +~~~~~~~~ You can set elements of an instance, and they "override" the constructor's prototype, but they don't actually change the constructor's prototype. Let's make another instance and try something else. - var another = new Ur(); - //=> {} - another.language - //=> 'JavaScript' - +{:lang="js"} +~~~~~~~~ +var another = new Ur(); + //=> {} +another.language + //=> 'JavaScript' +~~~~~~~~ + New instances don't acquire any changes made to other instances. Makes sense. And: - Ur.prototype.language = 'Sumerian' - another.language - //=> 'Sumerian' +{:lang="js"} +~~~~~~~~ +Ur.prototype.language = 'Sumerian' +another.language + //=> 'Sumerian' +~~~~~~~~ Even more interesting: Changing the constructor's prototype changes the behaviour of all of its instances. This strongly implies that there is a dynamic relationship between instances and their constructors, rather than some kind of mechanism that makes objects by copying.[^dynamic] @@ -70,23 +97,32 @@ Even more interesting: Changing the constructor's prototype changes the behaviou Speaking of prototypes, here's something else that's very interesting: - continent.constructor - //=> [Function] - - continent.constructor === Ur - //=> true +{:lang="js"} +~~~~~~~~ +continent.constructor + //=> [Function] + +continent.constructor === Ur + //=> true +~~~~~~~~ Every instance acquires a `constructor` element that is initialized to their constructor. This is true even for objects we don't create with `new` in our own code: - {}.constructor - //=> [Function: Object] - +{:lang="js"} +~~~~~~~~ +{}.constructor + //=> [Function: Object] +~~~~~~~~ + If that's true, what about prototypes? Do they have constructors? - Ur.prototype.constructor - //=> [Function] - Ur.prototype.constructor === Ur - //=> true +{:lang="js"} +~~~~~~~~ +Ur.prototype.constructor + //=> [Function] +Ur.prototype.constructor === Ur + //=> true +~~~~~~~~ Very interesting! We will take another look at the `constructor` element when we discuss [class extension](#classextension). @@ -94,32 +130,35 @@ Very interesting! We will take another look at the `constructor` element when we Let's rewrite our Queue to use `new` and `.prototype`, using `this` and our `extend` helper from [Composition and Extension](#composition): - var Queue = function () { - extend(this, { - array: [], - head: 0, - tail: -1 - }) - }; - - extend(Queue.prototype, { - pushTail: function (value) { - return this.array[this.tail += 1] = value - }, - pullHead: function () { - var value; - - if (!this.isEmpty()) { - value = this.array[this.head] - this.array[this.head] = void 0; - this.head += 1; - return value - } - }, - isEmpty: function () { - return this.tail < this.head - } - }) +{:lang="js"} +~~~~~~~~ +var Queue = function () { + extend(this, { + array: [], + head: 0, + tail: -1 + }) +}; + +extend(Queue.prototype, { + pushTail: function (value) { + return this.array[this.tail += 1] = value + }, + pullHead: function () { + var value; + + if (!this.isEmpty()) { + value = this.array[this.head] + this.array[this.head] = void 0; + this.head += 1; + return value + } + }, + isEmpty: function () { + return this.tail < this.head + } +}) +~~~~~~~~ You recall that when we first looked at `this`, we only covered the case where a function that belongs to an object is invoked. Now we see another case: When a function is invoked by the `new` operator, `this` is set to the new object being created. Thus, our code for `Queue` initializes the queue. @@ -131,21 +170,24 @@ Now that you know about prototypes, it's time to acknowledge something that even For example: - 3.14159265.toPrecision(5) - //=> '3.1415' - - 'FORTRAN, SNOBOL, LISP, BASIC'.split(', ') - //=> [ 'FORTRAN', - # 'SNOBOL', - # 'LISP', - # 'BASIC' ] - - [ 'FORTRAN', - 'SNOBOL', - 'LISP', - 'BASIC' ].length - //=> 4 - +{:lang="js"} +~~~~~~~~ +3.14159265.toPrecision(5) + //=> '3.1415' + +'FORTRAN, SNOBOL, LISP, BASIC'.split(', ') + //=> [ 'FORTRAN', + # 'SNOBOL', + # 'LISP', + # 'BASIC' ] + +[ 'FORTRAN', + 'SNOBOL', + 'LISP', + 'BASIC' ].length +//=> 4 +~~~~~~~~ + Functions themselves are instances, and they have methods. For example, every function has a method `call`. `call`'s first argument is a *context*: When you invoke `.call` on a function, it invoked the function, setting `this` to the context. It passes the remainder of the arguments to the function. It seems like objects are everywhere in JavaScript! ### impostors @@ -156,8 +198,11 @@ The full explanation is this: As you know, JavaScript has "value types" like `St So. Value types don't have methods or constructors. And yet: - "Spence Olham".split(' ') - //=> ["Spence", "Olham"] +{:lang="js"} +~~~~~~~~ +"Spence Olham".split(' ') + //=> ["Spence", "Olham"] +~~~~~~~~ Somehow, when we write `"Spence Olham".split(' ')`, the string `"Spence Olham"` isn't an instance, it doesn't have methods, but it does a damn fine job of impersonating an instance of a `String` constructor. How does `"Spence Olham"` impersonate an instance? @@ -167,61 +212,82 @@ JavaScript pulls some legerdemain. When you do something that treats a value lik These kinda-sorta copies are called String *instances* as opposed to String *primitives*. And the instances have methods, while the primitives do not. How does JavaScript make an instance out of a primitive? With `new`, of course. Let's try it: - new String("Spence Olham") - //=> "Spence Olham" - +{:lang="js"} +~~~~~~~~ +new String("Spence Olham") + //=> "Spence Olham" +~~~~~~~~ + The string instance looks just like our string primitive. But does it behave like a string primitive? Not entirely: - new String("Spence Olham") === "Spence Olham" - //=> false - +{:lang="js"} +~~~~~~~~ +new String("Spence Olham") === "Spence Olham" + //=> false +~~~~~~~~ + Aha! It's an object with its own identity, unlike string primitives that behave as if they have a canonical representation. If we didn't care about their identity, that wouldn't be a problem. But if we carelessly used a string instance where we thought we had a string primitive, we could run into a subtle bug: - if (userName === "Spence Olham") { - getMarried(); - goCamping() - } - +{:lang="js"} +~~~~~~~~ +if (userName === "Spence Olham") { + getMarried(); + goCamping() +} +~~~~~~~~ + That code is not going to work as we expect should we accidentally bind `new String("Spence Olham")` to `userName` instead of the primitive `"Spence Olham"`. This basic issue that instances have unique identities but primitives with the same contents have the same identities--is true of all primitive types, including numbers and booleans: If you create an instance of anything with `new`, it gets its own identity. There are more pitfalls to beware. Consider the truthiness of string, number and boolean primitives: - '' ? 'truthy' : 'falsy' - //=> 'falsy' - 0 ? 'truthy' : 'falsy' - //=> 'falsy' - false ? 'truthy' : 'falsy' - //=> 'falsy' - +{:lang="js"} +~~~~~~~~ +'' ? 'truthy' : 'falsy' + //=> 'falsy' +0 ? 'truthy' : 'falsy' + //=> 'falsy' +false ? 'truthy' : 'falsy' + //=> 'falsy' +~~~~~~~~ + Compare this to their corresponding instances: - new String('') ? 'truthy' : 'falsy' - //=> 'truthy' - new Number(0) ? 'truthy' : 'falsy' - //=> 'truthy' - new Boolean(false) ? 'truthy' : 'falsy' - //=> 'truthy' - +{:lang="js"} +~~~~~~~~ +new String('') ? 'truthy' : 'falsy' + //=> 'truthy' +new Number(0) ? 'truthy' : 'falsy' + //=> 'truthy' +new Boolean(false) ? 'truthy' : 'falsy' + //=> 'truthy' +~~~~~~~~ + Our notion of "truthiness" and "falsiness" is that all instances are truthy, even string, number, and boolean instances corresponding to primitives that are falsy. There is one sure cure for "JavaScript Impostor Syndrome." Just as `new PrimitiveType(...)` creates an instance that is an impostor of a primitive, `PrimitiveType(...)` creates an original, canonicalized primitive from a primitive or an instance of a primitive object. For example: - String(new String("Spence Olham")) === "Spence Olham" - //=> true - +{:lang="js"} +~~~~~~~~ +String(new String("Spence Olham")) === "Spence Olham" + //=> true +~~~~~~~~ + Getting clever, we can write this: - var original = function (unknown) { - return unknown.constructor(unknown) - } - - original(true) === true - //=> true - original(new Boolean(true)) === true - //=> true - +{:lang="js"} +~~~~~~~~ +var original = function (unknown) { + return unknown.constructor(unknown) +} + +original(true) === true + //=> true +original(new Boolean(true)) === true + //=> true +~~~~~~~~ + Of course, `original` will not work for your own creations unless you take great care to emulate the same behaviour. But it does work for strings, numbers, and booleans. diff --git a/manuscript/markdown/Objects, Mutation, and State/composition-and-extension.md b/manuscript/markdown/Objects, Mutation, and State/composition-and-extension.md index 481c628..b639c81 100644 --- a/manuscript/markdown/Objects, Mutation, and State/composition-and-extension.md +++ b/manuscript/markdown/Objects, Mutation, and State/composition-and-extension.md @@ -2,7 +2,7 @@ ### composition -A deeply fundamental practice is to build components out of smaller components. The choice of how to divide a component into smaller components is called *factoring*, after the operation in number theory [^refactoring]. +A deeply fundamental practice is to build components out of smaller components. The choice of how to divide a component into smaller components is called *factoring*, after the operation in number theory [^refactoring]. [^refactoring]: And when you take an already factored component and rearrange things so that it is factored into a different set of subcomponents without altering its behaviour, you are *refactoring*. @@ -10,65 +10,68 @@ The simplest and easiest way to build components out of smaller components in Ja Here's an abstract "model" that supports undo and redo composed from a pair of stacks (see [Encapsulating State](#encapsulation)) and a Plain Old JavaScript Object: - // helper function - // - // For production use, consider what to do about - // deep copies and own keys - var shallowCopy = function (source) { - var dest = {}, - key; - - for (key in source) { - dest[key] = source[key] - } - return dest - }; - - // our model maker - var ModelMaker = function (initialAttributes) { - var attributes = shallowCopy(initialAttributes || {}), - undoStack = StackMaker(), - redoStack = StackMaker(), - obj = { - set: function (attrsToSet) { - var key; - - undoStack.push(shallowCopy(attributes)); - if (!redoStack.isEmpty()) { - redoStack = StackMaker() - } - for (key in (attrsToSet || {})) { - attributes[key] = attrsToSet[key] - } - return obj - }, - undo: function () { - if (!undoStack.isEmpty()) { - redoStack.push(shallowCopy(attributes)); - attributes = undoStack.pop() - } - return obj - }, - redo: function () { - if (!redoStack.isEmpty()) { - undoStack.push(shallowCopy(attributes)); - attributes = redoStack.pop() - } - return obj - }, - get: function (key) { - return attributes(key) - }, - has: function (key) { - return attributes.hasOwnProperty(key) - }, - attributes: function { - shallowCopy(attributes) - } - }; - return obj +{:lang="js"} +~~~~~~~~ +// helper function +// +// For production use, consider what to do about +// deep copies and own keys +var shallowCopy = function (source) { + var dest = {}, + key; + + for (key in source) { + dest[key] = source[key] + } + return dest +}; + +// our model maker +var ModelMaker = function (initialAttributes) { + var attributes = shallowCopy(initialAttributes || {}), + undoStack = StackMaker(), + redoStack = StackMaker(), + obj = { + set: function (attrsToSet) { + var key; + + undoStack.push(shallowCopy(attributes)); + if (!redoStack.isEmpty()) { + redoStack = StackMaker() + } + for (key in (attrsToSet || {})) { + attributes[key] = attrsToSet[key] + } + return obj + }, + undo: function () { + if (!undoStack.isEmpty()) { + redoStack.push(shallowCopy(attributes)); + attributes = undoStack.pop() + } + return obj + }, + redo: function () { + if (!redoStack.isEmpty()) { + undoStack.push(shallowCopy(attributes)); + attributes = redoStack.pop() + } + return obj + }, + get: function (key) { + return attributes(key) + }, + has: function (key) { + return attributes.hasOwnProperty(key) + }, + attributes: function { + shallowCopy(attributes) + } }; - + return obj +}; +~~~~~~~~ + The techniques used for encapsulation work well with composition. In this case, we have a "model" that hides its attribute store as well as its implementation that is composed of an undo stack and redo stack. ### extension {#extensible} @@ -77,29 +80,32 @@ Another practice that many people consider fundamental is to *extend* an impleme Consider a [queue]: - var QueueMaker = function () { - var array = [], - head = 0, - tail = -1; - return { - pushTail: function (value) { - return array[tail += 1] = value - }, - pullHead: function () { - var value; - - if (tail >= head) { - value = array[head]; - array[head] = void 0; - head += 1; - return value - } - }, - isEmpty: function () { - return tail < head - } +{:lang="js"} +~~~~~~~~ +var QueueMaker = function () { + var array = [], + head = 0, + tail = -1; + return { + pushTail: function (value) { + return array[tail += 1] = value + }, + pullHead: function () { + var value; + + if (tail >= head) { + value = array[head]; + array[head] = void 0; + head += 1; + return value } - }; + }, + isEmpty: function () { + return tail < head + } + } +}; +~~~~~~~~ Now we wish to create a [deque] by adding `pullTail` and `pushHead` operations to our queue.[^wasa] Unfortunately, encapsulation prevents us from adding operations that interact with the hidden data structures. @@ -111,66 +117,72 @@ This isn't really surprising: The entire point of encapsulation is to create an Let's "de-encapsulate" our queue: - var QueueMaker = function () { - var queue = { - array: [], - head: 0, - tail: -1, - pushTail: function (value) { - return queue.array[queue.tail += 1] = value - }, - pullHead: function () { - var value; - - if (queue.tail >= queue.head) { - value = queue.array[queue.head]; - queue.array[queue.head] = void 0; - queue.head += 1; - return value - } - }, - isEmpty: function () { - return queue.tail < queue.head - } - }; - return queue - }; +{:lang="js"} +~~~~~~~~ +var QueueMaker = function () { + var queue = { + array: [], + head: 0, + tail: -1, + pushTail: function (value) { + return queue.array[queue.tail += 1] = value + }, + pullHead: function () { + var value; + + if (queue.tail >= queue.head) { + value = queue.array[queue.head]; + queue.array[queue.head] = void 0; + queue.head += 1; + return value + } + }, + isEmpty: function () { + return queue.tail < queue.head + } + }; + return queue +}; +~~~~~~~~ Now we can extend a queue into a deque: - var DequeMaker = function () { - var deque = QueueMaker(), - INCREMENT = 4; - - return extend(deque, { - size: function () { - return deque.tail - deque.head + 1 - }, - pullTail: function () { - var value; - - if (!deque.isEmpty()) { - value = deque.array[deque.tail]; - deque.array[deque.tail] = void 0; - deque.tail -= 1; - return value - } - }, - pushHead: function (value) { - var i; - - if (deque.head === 0) { - for (i = deque.tail; i <= deque.head; i++) { - deque.array[i + INCREMENT] = deque.array[i] - } - deque.tail += INCREMENT - deque.head += INCREMENT - } - return deque.array[deque.head -= 1] = value +{:lang="js"} +~~~~~~~~ +var DequeMaker = function () { + var deque = QueueMaker(), + INCREMENT = 4; + + return extend(deque, { + size: function () { + return deque.tail - deque.head + 1 + }, + pullTail: function () { + var value; + + if (!deque.isEmpty()) { + value = deque.array[deque.tail]; + deque.array[deque.tail] = void 0; + deque.tail -= 1; + return value + } + }, + pushHead: function (value) { + var i; + + if (deque.head === 0) { + for (i = deque.tail; i <= deque.head; i++) { + deque.array[i + INCREMENT] = deque.array[i] } - }) - }; + deque.tail += INCREMENT + deque.head += INCREMENT + } + return deque.array[deque.head -= 1] = value + } + }) +}; +~~~~~~~~ Presto, we have reuse through extension, at the cost of encapsulation. -T> Encapsulation and Extension exist in a natural state of tension. A program with elaborate encapsulation resists breakage but can also be difficult to refactor in other ways. Be mindful of when it's best to Compose and when it's best to Extend. \ No newline at end of file +T> Encapsulation and Extension exist in a natural state of tension. A program with elaborate encapsulation resists breakage but can also be difficult to refactor in other ways. Be mindful of when it's best to Compose and when it's best to Extend. diff --git a/manuscript/markdown/Objects, Mutation, and State/context.md b/manuscript/markdown/Objects, Mutation, and State/context.md index 56d3ab8..d813abe 100644 --- a/manuscript/markdown/Objects, Mutation, and State/context.md +++ b/manuscript/markdown/Objects, Mutation, and State/context.md @@ -2,15 +2,18 @@ In [This and That](#this), we learned that when a function is called as an object method, the name `this` is bound in its environment to the object acting as a "receiver." For example: - var someObject = { - returnMyThis: function () { - return this; - } - }; - - someObject.returnMyThis() === someObject - //=> true - +{:lang="js"} +~~~~~~~~ +var someObject = { + returnMyThis: function () { + return this; + } +}; + +someObject.returnMyThis() === someObject + //=> true +~~~~~~~~ + We've constructed a method that returns whatever value is bound to `this` when it is called. It returns the object when called, just as described. ### it's all about the way the function is called @@ -25,83 +28,104 @@ This is an important distinction. Consider closures: As we discussed in [Closure A function's context cannot be determined by examining the source code of a JavaScript program. Let's look at our example again: - var someObject = { - someFunction: function () { - return this; - } - }; +{:lang="js"} +~~~~~~~~ +var someObject = { + someFunction: function () { + return this; + } +}; + +someObject.someFunction() === someObject + //=> true +~~~~~~~~ - someObject.someFunction() === someObject - //=> true - What is the context of the function `someObject.someFunction`? Don't say `someObject`! Watch this: - var someFunction = someObject.someFunction; +{:lang="js"} +~~~~~~~~ +var someFunction = someObject.someFunction; + +someFunction === someObject.someFunction + //=> true + +someFunction() === someObject + //=> false +~~~~~~~~ - someFunction === someObject.someFunction - //=> true - - someFunction() === someObject - //=> false - It gets weirder: - var anotherObject = { - someFunction: someObject.someFunction - } - - anotherObject.someFunction === someObject.someFunction - //=> true - - anotherObject.someFunction() === anotherObject - //=> true - - anotherObject.someFunction() === someObject - //=> false - +{:lang="js"} +~~~~~~~~ +var anotherObject = { + someFunction: someObject.someFunction +} + +anotherObject.someFunction === someObject.someFunction + //=> true + +anotherObject.someFunction() === anotherObject + //=> true + +anotherObject.someFunction() === someObject + //=> false +~~~~~~~~ + So it amounts to this: The exact same function can be called in two different ways, and you end up with two different contexts. If you call it using `someObject.someFunction()` syntax, the context is set to the receiver. If you call it using any other expression for resolving the function's value (such as `someFunction()`), you get something else. Let's investigate: - (someObject.someFunction)() == someObject - //=> true - - someObject['someFunction']() === someObject - //=> true - - var name = 'someFunction'; - - someObject[name]() === someObject - //=> true +{:lang="js"} +~~~~~~~~ +(someObject.someFunction)() == someObject + //=> true + +someObject['someFunction']() === someObject + //=> true + +var name = 'someFunction'; + +someObject[name]() === someObject + //=> true +~~~~~~~~ Interesting! - var baz; - - (baz = someObject.someFunction)() === this - //=> true - +{:lang="js"} +~~~~~~~~ +var baz; + +(baz = someObject.someFunction)() === this + //=> true +~~~~~~~~ + How about: - var arr = [ someObject.someFunction ]; - - arr[0]() == arr - //=> true - -It seems that whether you use `a.b()` or `a['b']()` or `a[n]()` or `(a.b)()`, you get context `a`. - - var returnThis = function () { return this }; - - var aThirdObject = { - someFunction: function () { - return returnThis() - } - } - - returnThis() === this - //=> true - - aThirdObject.someFunction() === this - //=> true - +{:lang="js"} +~~~~~~~~ +var arr = [ someObject.someFunction ]; + +arr[0]() == arr + //=> true +~~~~~~~~ + +It seems that whether you use `a.b()` or `a['b']()` or `a[n]()` or `(a.b)()`, you get context `a`. + +{:lang="js"} +~~~~~~~~ +var returnThis = function () { return this }; + +var aThirdObject = { + someFunction: function () { + return returnThis() + } +} + +returnThis() === this + //=> true + +aThirdObject.someFunction() === this + //=> true +~~~~~~~~ + And if you don't use `a.b()` or `a['b']()` or `a[n]()` or `(a.b)()`, you get the global environment for a context, not the context of whatever function is doing the calling. To simplify things, when you call a function with `.` or `[]` access, you get an object as context, otherwise you get the global environment. ### setting your own context @@ -110,26 +134,32 @@ There are actually two other ways to set the context of a function. And once aga Here's `call` in action: - returnThis() === aThirdObject - //=> false +{:lang="js"} +~~~~~~~~ +returnThis() === aThirdObject + //=> false + +returnThis.call(aThirdObject) === aThirdObject + //=> true + +anotherObject.someFunction.call(someObject) === someObject + //=> true +~~~~~~~~ - returnThis.call(aThirdObject) === aThirdObject - //=> true - - anotherObject.someFunction.call(someObject) === someObject - //=> true - When You call a function with `call`, you set the context by passing it in as the first parameter. Other arguments are passed to the function in the normal manner. Much hilarity can result from `call` shenanigans like this: - var a = [1,2,3], - b = [4,5,6]; - - a.concat([2,1]) - //=> [1,2,3,2,1] - - a.concat.call(b,[2,1]) - //=> [4,5,6,2,1] - +{:lang="js"} +~~~~~~~~ +var a = [1,2,3], + b = [4,5,6]; + +a.concat([2,1]) + //=> [1,2,3,2,1] + +a.concat.call(b,[2,1]) + //=> [4,5,6,2,1] +~~~~~~~~ + But now we thoroughly understand what `a.b()` really means: It's synonymous with `a.b.call(a)`. Whereas in a browser, `c()` is synonymous with `c.call(window)`. ### apply, arguments, and contextualization @@ -140,64 +170,82 @@ JavaScript has another automagic binding in every function's environment. `argum For example: - var third = function () { - return arguments[2] - } +{:lang="js"} +~~~~~~~~ +var third = function () { + return arguments[2] +} - third(77, 76, 75, 74, 73) - //=> 75 +third(77, 76, 75, 74, 73) + //=> 75 +~~~~~~~~ Hold that thought for a moment. JavaScript also provides a fourth way to set the context for a function. `apply` is a method implemented by every function that takes a context as its first argument, and it takes an array or array-like thing of arguments as its second argument. That's a mouthful, let's look at an example: - third.call(this, 1,2,3,4,5) - //=> 3 +{:lang="js"} +~~~~~~~~ +third.call(this, 1,2,3,4,5) + //=> 3 + +third.apply(this, [1,2,3,4,5]) + //=> 3 +~~~~~~~~ - third.apply(this, [1,2,3,4,5]) - //=> 3 - Now let's put the two together. Here's another travesty: - var a = [1,2,3], - accrete = a.concat; - - accrete([4,5]) - //=> Gobbledygook! +{:lang="js"} +~~~~~~~~ +var a = [1,2,3], + accrete = a.concat; + +accrete([4,5]) + //=> Gobbledygook! +~~~~~~~~ We get the result of concatenating `[4,5]` onto an array containing the global environment. Not what we want! Behold: - var contextualize = function (fn, context) { - return function () { - return fn.apply(context, arguments); - } - } - - accrete = contextualize(a.concat, a); - accrete([4,5]); - //=> [ 1, 2, 3, 4, 5 ] - +{:lang="js"} +~~~~~~~~ +var contextualize = function (fn, context) { + return function () { + return fn.apply(context, arguments); + } +} + +accrete = contextualize(a.concat, a); +accrete([4,5]); + //=> [ 1, 2, 3, 4, 5 ] +~~~~~~~~ + Our `contextualize` function returns a new function that calls a function with a fixed context. It can be used to fix some of the unexpected results we had above. Consider: - var aFourthObject = {}, - returnThis = function () { return this; }; - - aFourthObject.uncontextualized = returnThis; - aFourthObject.contextualized = contextualize(returnThis, aFourthObject); - - aFourthObject.uncontextualized() === aFourthObject - //=> true - aFourthObject.contextualized() === aFourthObject - //=> true - +{:lang="js"} +~~~~~~~~ +var aFourthObject = {}, + returnThis = function () { return this; }; + +aFourthObject.uncontextualized = returnThis; +aFourthObject.contextualized = contextualize(returnThis, aFourthObject); + +aFourthObject.uncontextualized() === aFourthObject + //=> true +aFourthObject.contextualized() === aFourthObject + //=> true +~~~~~~~~ + Both are `true` because we are accessing them with `aFourthObject.` Now we write: - var uncontextualized = aFourthObject.uncontextualized, - contextualized = aFourthObject.contextualized; - - uncontextualized() === aFourthObject; - //=> false - contextualized() === aFourthObject - //=> true - +{:lang="js"} +~~~~~~~~ +var uncontextualized = aFourthObject.uncontextualized, + contextualized = aFourthObject.contextualized; + +uncontextualized() === aFourthObject; + //=> false +contextualized() === aFourthObject + //=> true +~~~~~~~~ + When we call these functions without using `aFourthObject.`, only the contextualized version maintains the context of `aFourthObject`. - -We'll return to contextualizing methods later, in [Binding](#binding). But before we dive too deeply into special handling for methods, we need to spend a little more time looking at how functions and methods work. \ No newline at end of file + +We'll return to contextualizing methods later, in [Binding](#binding). But before we dive too deeply into special handling for methods, we need to spend a little more time looking at how functions and methods work. diff --git a/manuscript/markdown/Objects, Mutation, and State/encapsulation.md b/manuscript/markdown/Objects, Mutation, and State/encapsulation.md index e33e67a..9e0174b 100644 --- a/manuscript/markdown/Objects, Mutation, and State/encapsulation.md +++ b/manuscript/markdown/Objects, Mutation, and State/encapsulation.md @@ -38,44 +38,47 @@ We've been introduced to JavaScript's objects, and it's fairly easy to see that Given an object that holds our state (an array and an index[^length]), we can easily implement our three operations as functions. Bundling the functions with the state does not require any special "magic" features. JavaScript objects can have elements of any type, including functions: - var stack = (function () { - var obj = { - array: [], - index: -1, - push: function (value) { - return obj.array[obj.index += 1] = value - }, - pop: function () { - var value = obj.array[obj.index]; - obj.array[obj.index] = void 0; - if (obj.index >= 0) { - obj.index -= 1 - } - return value - }, - isEmpty: function () { - return obj.index < 0 - } - }; - - return obj; - })(); - - stack.isEmpty() - //=> true - stack.push('hello') - //=> 'hello' - stack.push('JavaScript') - //=> 'JavaScript' - stack.isEmpty() - //=> false - stack.pop() - //=> 'JavaScript' - stack.pop() - //=> 'hello' - stack.isEmpty() - //=> true - +{:lang="js"} +~~~~~~~~ +var stack = (function () { + var obj = { + array: [], + index: -1, + push: function (value) { + return obj.array[obj.index += 1] = value + }, + pop: function () { + var value = obj.array[obj.index]; + obj.array[obj.index] = void 0; + if (obj.index >= 0) { + obj.index -= 1 + } + return value + }, + isEmpty: function () { + return obj.index < 0 + } + }; + + return obj; +})(); + +stack.isEmpty() + //=> true +stack.push('hello') + //=> 'hello' +stack.push('JavaScript') + //=> 'JavaScript' +stack.isEmpty() + //=> false +stack.pop() + //=> 'JavaScript' +stack.pop() + //=> 'hello' +stack.isEmpty() + //=> true +~~~~~~~~ + ### method-ology In this text, we lurch from talking about "functions that belong to an object" to "methods." Other languages may separate methods from functions very strictly, but in JavaScript every method is a function but not all functions are methods. @@ -84,116 +87,128 @@ The view taken in this book is that a function is a method of an object if it be But these two wouldn't be methods. Although they "belong" to an object, they don't interact with it: - { - min: function (x, y) { - if (x < y) { - return x - } - else { - return y - } - } - max: function (x, y) { - if (x > y) { - return x - } - else { - return y - } - } +{:lang="js"} +~~~~~~~~ +{ + min: function (x, y) { + if (x < y) { + return x + } + else { + return y } + } + max: function (x, y) { + if (x > y) { + return x + } + else { + return y + } + } +} +~~~~~~~~ ### hiding state Our stack does bundle functions with data, but it doesn't hide its state. "Foreign" code could interfere with its array or index. So how do we hide these? We already have a closure, let's use it: - var stack = (function () { - var array = [], - index = -1; - - return { - push: function (value) { - array[index += 1] = value - }, - pop: function () { - var value = array[index]; - if (index >= 0) { - index -= 1 - } - return value - }, - isEmpty: function () { - return index < 0 - } +{:lang="js"} +~~~~~~~~ +var stack = (function () { + var array = [], + index = -1; + + return { + push: function (value) { + array[index += 1] = value + }, + pop: function () { + var value = array[index]; + if (index >= 0) { + index -= 1 } - })(); - - stack.isEmpty() - //=> true - stack.push('hello') - //=> 'hello' - stack.push('JavaScript') - //=> 'JavaScript' - stack.isEmpty() - //=> false - stack.pop() - //=> 'JavaScript' - stack.pop() - //=> 'hello' - stack.isEmpty() - //=> true - + return value + }, + isEmpty: function () { + return index < 0 + } + } +})(); + +stack.isEmpty() + //=> true +stack.push('hello') + //=> 'hello' +stack.push('JavaScript') + //=> 'JavaScript' +stack.isEmpty() + //=> false +stack.pop() + //=> 'JavaScript' +stack.pop() + //=> 'hello' +stack.isEmpty() + //=> true +~~~~~~~~ + ![Coffee DOES grow on trees](images/coffee-trees-1200.jpg) We don't want to repeat this code every time we want a stack, so let's make ourselves a "stack maker." The temptation is to wrap what we have above in a function: - var StackMaker = function () { - return (function () { - var array = [], - index = -1; - - return { - push: function (value) { - array[index += 1] = value - }, - pop: function () { - var value = array[index]; - if (index >= 0) { - index -= 1 - } - return value - }, - isEmpty: function () { - return index < 0 - } +{:lang="js"} +~~~~~~~~ +var StackMaker = function () { + return (function () { + var array = [], + index = -1; + + return { + push: function (value) { + array[index += 1] = value + }, + pop: function () { + var value = array[index]; + if (index >= 0) { + index -= 1 } - })() + return value + }, + isEmpty: function () { + return index < 0 + } } + })() +} +~~~~~~~~ But there's an easier way :-) - var StackMaker = function () { - var array = [], - index = -1; - - return { - push: function (value) { - array[index += 1] = value - }, - pop: function () { - var value = array[index]; - if (index >= 0) { - index -= 1 - } - return value - }, - isEmpty: function () { - return index < 0 - } +{:lang="js"} +~~~~~~~~ +var StackMaker = function () { + var array = [], + index = -1; + + return { + push: function (value) { + array[index += 1] = value + }, + pop: function () { + var value = array[index]; + if (index >= 0) { + index -= 1 } - }; + return value + }, + isEmpty: function () { + return index < 0 + } + } +}; - stack = StackMaker() +stack = StackMaker() +~~~~~~~~ Now we can make stacks freely, and we've hidden their internal data elements. We have methods and encapsulation, and we've built them out of JavaScript's fundamental functions and objects. In [Instances and Classes](#methods), we'll look at JavaScript's support for class-oriented programming and some of the idioms that functions bring to the party. @@ -203,4 +218,4 @@ A> We've built something with hidden internal state and "methods," all without n A> A> Then again, the key lesson experienced programmers repeat--although it often falls on deaf ears--is [Composition instead of Inheritance](http://www.c2.com/cgi/wiki?CompositionInsteadOfInheritance). So maybe we aren't missing much. -[^length]: Yes, there's another way to track the size of the array, but we don't need it to demonstrate encapsulation and hiding of state. \ No newline at end of file +[^length]: Yes, there's another way to track the size of the array, but we don't need it to demonstrate encapsulation and hiding of state. diff --git a/manuscript/markdown/Objects, Mutation, and State/method-decorators.md b/manuscript/markdown/Objects, Mutation, and State/method-decorators.md index 2ad70a3..da68f09 100644 --- a/manuscript/markdown/Objects, Mutation, and State/method-decorators.md +++ b/manuscript/markdown/Objects, Mutation, and State/method-decorators.md @@ -4,38 +4,50 @@ In [function decorators](#decorators), we learned that a decorator takes a funct Decorators can be used to decorate methods provided that they carefully preserve the function's context. For example, here is a naïve version of `maybe` for one argument: - function maybe (fn) { - return function (argument) { - if (argument != null) { - return fn(argument) - } - } +{:lang="js"} +~~~~~~~~ +function maybe (fn) { + return function (argument) { + if (argument != null) { + return fn(argument) } + } +} +~~~~~~~~ This version doesn't preserve the context, so it can't be used as a method decorator. Instead, we have to write: - function maybe (fn) { - return function (argument) { - if (argument != null) { - return fn.call(this, argument) - } - } +{:lang="js"} +~~~~~~~~ +function maybe (fn) { + return function (argument) { + if (argument != null) { + return fn.call(this, argument) } + } +} +~~~~~~~~ Now we can write things like: - someObject = { - setSize: maybe(function (size) { - this.size = size; - return this - }) - } +{:lang="js"} +~~~~~~~~ +someObject = { + setSize: maybe(function (size) { + this.size = size; + return this + }) +} +~~~~~~~~ And `this` is correctly set: - someObject.setSize(5) - //=> { setSize: [Function], size: 5 } +{:lang="js"} +~~~~~~~~ +someObject.setSize(5) + //=> { setSize: [Function], size: 5 } +~~~~~~~~ Using `.call` or `.apply` and `arguments` is substantially slower than writing function decorators that don't set the context, so it might be right to sometimes write function decorators that aren't usable as method decorators. However, in practice you're far more likely to introduce a defect by failing to pass the context through a decorator than by introducing a performance pessimization, so the default choice should be to write all function decorators in such a way that they are "context agnostic." -In some cases, there are other considerations to writing a method decorator. If the decorator introduces state of any kind (such as `once` and `memoize` do), this must be carefully managed for the case when several objects share the same method through the mechanism of the [prototype](#prototypes) or through sharing references to the same function. \ No newline at end of file +In some cases, there are other considerations to writing a method decorator. If the decorator introduces state of any kind (such as `once` and `memoize` do), this must be carefully managed for the case when several objects share the same method through the mechanism of the [prototype](#prototypes) or through sharing references to the same function. diff --git a/manuscript/markdown/Objects, Mutation, and State/recipes/deep-map-with.md b/manuscript/markdown/Objects, Mutation, and State/recipes/deep-map-with.md index da7634d..f3137de 100644 --- a/manuscript/markdown/Objects, Mutation, and State/recipes/deep-map-with.md +++ b/manuscript/markdown/Objects, Mutation, and State/recipes/deep-map-with.md @@ -2,61 +2,70 @@ [mapWith](#mapWith) is an excellent tool, but from time to time you will find yourself working with arrays that represent trees rather than lists. For example, here is a partial list of sales extracted from a report of some kind. It's grouped in some mysterious way, and we need to operate on each item in the report. - var report = - [ [ { price: 1.99, id: 1 }, - { price: 4.99, id: 2 }, - { price: 7.99, id: 3 }, - { price: 1.99, id: 4 }, - { price: 2.99, id: 5 }, - { price: 6.99, id: 6 } ], - [ { price: 5.99, id: 21 }, - { price: 1.99, id: 22 }, - { price: 1.99, id: 23 }, - { price: 1.99, id: 24 }, - { price: 5.99, id: 25 } ], - - // ... - - [ { price: 7.99, id: 221 }, - { price: 4.99, id: 222 }, - { price: 7.99, id: 223 }, - { price: 10.99, id: 224 }, - { price: 9.99, id: 225 }, - { price: 9.99, id: 226 } ] ]; +{:lang="js"} +~~~~~~~~ +var report = + [ [ { price: 1.99, id: 1 }, + { price: 4.99, id: 2 }, + { price: 7.99, id: 3 }, + { price: 1.99, id: 4 }, + { price: 2.99, id: 5 }, + { price: 6.99, id: 6 } ], + [ { price: 5.99, id: 21 }, + { price: 1.99, id: 22 }, + { price: 1.99, id: 23 }, + { price: 1.99, id: 24 }, + { price: 5.99, id: 25 } ], + + // ... + + [ { price: 7.99, id: 221 }, + { price: 4.99, id: 222 }, + { price: 7.99, id: 223 }, + { price: 10.99, id: 224 }, + { price: 9.99, id: 225 }, + { price: 9.99, id: 226 } ] ]; +~~~~~~~~ We could nest some mapWiths, but we humans are tool users. If we can use a stick to extract tasty ants from a hole to eat, we can automate working with arrays: - function deepMapWith (fn) { - return function innerdeepMapWith (tree) { - return Array.prototype.map.call(tree, function (element) { - if (Array.isArray(element)) { - return innerdeepMapWith(element); - } - else return fn(element); - }); - }; - }; +{:lang="js"} +~~~~~~~~ +function deepMapWith (fn) { + return function innerdeepMapWith (tree) { + return Array.prototype.map.call(tree, function (element) { + if (Array.isArray(element)) { + return innerdeepMapWith(element); + } + else return fn(element); + }); + }; +}; +~~~~~~~~ And now we can use `deepMapWith` on a tree the way we use `mapWith` on a flat array: - deepMapWith(getWith('price'))(report) - //=> [ [ 1.99, - 4.99, - 7.99, - 1.99, - 2.99, - 6.99 ], - [ 5.99, - 1.99, - 1.99, - 1.99, - 5.99 ], - - // ... - - [ 7.99, - 4.99, - 7.99, - 10.99, - 9.99, - 9.99 ] ] \ No newline at end of file +{:lang="js"} +~~~~~~~~ +deepMapWith(getWith('price'))(report) + //=> [ [ 1.99, + 4.99, + 7.99, + 1.99, + 2.99, + 6.99 ], + [ 5.99, + 1.99, + 1.99, + 1.99, + 5.99 ], + + // ... + + [ 7.99, + 4.99, + 7.99, + 10.99, + 9.99, + 9.99 ] ] +~~~~~~~~ diff --git a/manuscript/markdown/Objects, Mutation, and State/recipes/get-with.md b/manuscript/markdown/Objects, Mutation, and State/recipes/get-with.md index c03a508..bf2c972 100644 --- a/manuscript/markdown/Objects, Mutation, and State/recipes/get-with.md +++ b/manuscript/markdown/Objects, Mutation, and State/recipes/get-with.md @@ -2,40 +2,55 @@ `getWith` is a very simple function. It takes the name of an attribute and returns a function that extracts the value of that attribute from an object: - function getWith (attr) { - return function (object) { return object[attr]; } - } +{:lang="js"} +~~~~~~~~ +function getWith (attr) { + return function (object) { return object[attr]; } +} +~~~~~~~~ You can use it like this: - var inventory = { - apples: 0, - oranges: 144, - eggs: 36 - }; +{:lang="js"} +~~~~~~~~ +var inventory = { + apples: 0, + oranges: 144, + eggs: 36 +}; - getWith('oranges')(inventory) - //=> 144 +getWith('oranges')(inventory) + //=> 144 +~~~~~~~~ This isn't much of a recipe yet. But let's combine it with [mapWith](#mapWith): - var inventories = [ - { apples: 0, oranges: 144, eggs: 36 }, - { apples: 240, oranges: 54, eggs: 12 }, - { apples: 24, oranges: 12, eggs: 42 } - ]; +{:lang="js"} +~~~~~~~~ +var inventories = [ + { apples: 0, oranges: 144, eggs: 36 }, + { apples: 240, oranges: 54, eggs: 12 }, + { apples: 24, oranges: 12, eggs: 42 } +]; - mapWith(getWith('oranges'))(inventories) - //=> [ 144, 54, 12 ] +mapWith(getWith('oranges'))(inventories) + //=> [ 144, 54, 12 ] +~~~~~~~~ That's nicer than writing things out "longhand:" - mapWith(function (inventory) { return inventory.oranges })(inventories) - //=> [ 144, 54, 12 ] +{:lang="js"} +~~~~~~~~ +mapWith(function (inventory) { return inventory.oranges })(inventories) + //=> [ 144, 54, 12 ] +~~~~~~~~ `getWith` plays nicely with [maybe](#maybe) as well. Consider a sparse array. You can use: - mapWith(maybe(getWith('oranges'))) +{:lang="js"} +~~~~~~~~ +mapWith(maybe(getWith('oranges'))) +~~~~~~~~ To get the orange count from all the non-null inventories in a list. @@ -43,14 +58,23 @@ To get the orange count from all the non-null inventories in a list. Why is this called `getWith`? Consider this function that is common in languages that have functions and dictionaries but not methods: - function get (object, attr) { - return object[attr]; - }; +{:lang="js"} +~~~~~~~~ +function get (object, attr) { + return object[attr]; +}; +~~~~~~~~ You might ask, "Why use a function instead of just using `[]`?" The answer is, we can manipulate functions in ways that we can't manipulate syntax. For example, do you remember from [flip](#flip) that we can define `mapWith` from `map`? - var mapWith = flip(map); +{:lang="js"} +~~~~~~~~ +var mapWith = flip(map); +~~~~~~~~ We can do the same thing with `getWith`, and that's why it's named in this fashion: - var getWith = flip(get) +{:lang="js"} +~~~~~~~~ +var getWith = flip(get) +~~~~~~~~ diff --git a/manuscript/markdown/Objects, Mutation, and State/recipes/memoize.md b/manuscript/markdown/Objects, Mutation, and State/recipes/memoize.md index 32da23f..d034375 100644 --- a/manuscript/markdown/Objects, Mutation, and State/recipes/memoize.md +++ b/manuscript/markdown/Objects, Mutation, and State/recipes/memoize.md @@ -2,94 +2,115 @@ Consider that age-old interview quiz, writing a recursive fibonacci function (there are other ways to derive a fibonacci number, of course). Here's an implementation that doesn't use a [named function expression](#named-function-expressions). The reason for that omission will be explained later: - var fibonacci = function (n) { - if (n < 2) { - return n - } - else { - return fibonacci(n-2) + fibonacci(n-1) - } - } +{:lang="js"} +~~~~~~~~ +var fibonacci = function (n) { + if (n < 2) { + return n + } + else { + return fibonacci(n-2) + fibonacci(n-1) + } +} +~~~~~~~~ We'll time it: - s = (new Date()).getTime() - fibonacci(45) - ( (new Date()).getTime() - s ) / 1000 - //=> 28.565 - +{:lang="js"} +~~~~~~~~ +s = (new Date()).getTime() +fibonacci(45) +( (new Date()).getTime() - s ) / 1000 + //=> 28.565 +~~~~~~~~ + Why is it so slow? Well, it has a nasty habit of recalculating the same results over and over and over again. We could rearrange the computation to avoid this, but let's be lazy and trade space for time. What we want to do is use a lookup table. Whenever we want a result, we look it up. If we don't have it, we calculate it and write the result in the table to use in the future. If we do have it, we return the result without recalculating it. Here's our recipe: - function memoized (fn, keymaker) { - var lookupTable = {}, - key; - - keymaker || (keymaker = function (args) { - return JSON.stringify(args) - }); - - return function () { - var key = keymaker.call(this, arguments); - - return lookupTable[key] || ( - lookupTable[key] = fn.apply(this, arguments) - ) - } - } +{:lang="js"} +~~~~~~~~ +function memoized (fn, keymaker) { + var lookupTable = {}, + key; + + keymaker || (keymaker = function (args) { + return JSON.stringify(args) + }); + + return function () { + var key = keymaker.call(this, arguments); + + return lookupTable[key] || ( + lookupTable[key] = fn.apply(this, arguments) + ) + } +} +~~~~~~~~ We can apply `memoized` to a function and we will get back a new function that "memoizes" its results so that it never has to recalculate the same value twice. It only works for functions that are "idempotent," meaning functions that always return the same result given the same argument(s). Like `fibonacci`: Let's try it: - var fastFibonacci = memoized( function (n) { - if (n < 2) { - return n - } - else { - return fastFibonacci(n-2) + fastFibonacci(n-1) - } - }); - - fastFibonacci(45) - //=> 1134903170 +{:lang="js"} +~~~~~~~~ +var fastFibonacci = memoized( function (n) { + if (n < 2) { + return n + } + else { + return fastFibonacci(n-2) + fastFibonacci(n-1) + } +}); + +fastFibonacci(45) + //=> 1134903170 +~~~~~~~~ We get the result back instantly. It works! You can use memoize with all sorts of "idempotent" pure functions. by default, it works with any function that takes arguments which can be transformed into JSON using JavaScript's standard library function for this purpose. If you have another strategy for turning the arguments into a string key, you can supply it as a second parameter. - + ### memoizing recursive functions We deliberately picked a recursive function to memoize, because it demonstrates a pitfall when combining decorators with named functional expressions. Consider this implementation that uses a named functional expression: - var fibonacci = function fibonacci (n) { - if (n < 2) { - return n - } - else { - return fibonacci(n-2) + fibonacci(n-1) - } - } - +{:lang="js"} +~~~~~~~~ +var fibonacci = function fibonacci (n) { + if (n < 2) { + return n + } + else { + return fibonacci(n-2) + fibonacci(n-1) + } +} +~~~~~~~~ + If we try to memoize it, we don't get the expected speedup: - var fibonacci = memoized( function fibonacci (n) { - if (n < 2) { - return n - } - else { - return fibonacci(n-2) + fibonacci(n-1) - } - }); +{:lang="js"} +~~~~~~~~ +var fibonacci = memoized( function fibonacci (n) { + if (n < 2) { + return n + } + else { + return fibonacci(n-2) + fibonacci(n-1) + } +}); +~~~~~~~~ That's because the function bound to the name `fibonacci` in the outer environment has been memoized, but the named functional expression binds the name `fibonacci` inside the unmemoized function, so none of the recursive calls to fibonacci are *ever* memoized. Therefore we must write: - var fibonacci = memoized( function (n) { - if (n < 2) { - return n - } - else { - return fibonacci(n-2) + fibonacci(n-1) - } - }); +{:lang="js"} +~~~~~~~~ +var fibonacci = memoized( function (n) { + if (n < 2) { + return n + } + else { + return fibonacci(n-2) + fibonacci(n-1) + } +}); +~~~~~~~~ If we need to prevent a rebinding from breaking the function, we'll need to use the [module](#modules) pattern. diff --git a/manuscript/markdown/Objects, Mutation, and State/recipes/pluck.md b/manuscript/markdown/Objects, Mutation, and State/recipes/pluck.md index 83a2944..26324e6 100644 --- a/manuscript/markdown/Objects, Mutation, and State/recipes/pluck.md +++ b/manuscript/markdown/Objects, Mutation, and State/recipes/pluck.md @@ -2,36 +2,57 @@ This pattern of combining [mapWith](#mapWith) and [getWith](#getWith) is very frequent in JavaScript code. So much so, that we can take it up another level: - function pluckWith (attr) { - return mapWith(getWith(attr)) - } - +{:lang="js"} +~~~~~~~~ +function pluckWith (attr) { + return mapWith(getWith(attr)) +} +~~~~~~~~ + Or even better: - var pluckWith = compose(mapWith, getWith); - +{:lang="js"} +~~~~~~~~ +var pluckWith = compose(mapWith, getWith); +~~~~~~~~ + And now we can write: - - pluckWith('eggs')(inventories) - //=> [ 36, 12, 42 ] - + +{:lang="js"} +~~~~~~~~ +pluckWith('eggs')(inventories) + //=> [ 36, 12, 42 ] +~~~~~~~~ + Libraries like [Underscore] provide `pluck`, the flipped version of `pluckWith`: - _.pluck(inventories, 'eggs') - //=> [ 36, 12, 42 ] +{:lang="js"} +~~~~~~~~ +_.pluck(inventories, 'eggs') + //=> [ 36, 12, 42 ] +~~~~~~~~ Our recipe is terser when you want to name a function: - var eggsByStore = pluckWith('eggs'); - +{:lang="js"} +~~~~~~~~ +var eggsByStore = pluckWith('eggs'); +~~~~~~~~ + vs. - function eggsByStore (inventories) { - return _.pluck(inventories, 'eggs') - } - +{:lang="js"} +~~~~~~~~ +function eggsByStore (inventories) { + return _.pluck(inventories, 'eggs') +} +~~~~~~~~ + And of course, if we have `pluck` we can use [flip](#flip) to derive `pluckWith`: - var pluckWith = flip(_.pluck); +{:lang="js"} +~~~~~~~~ +var pluckWith = flip(_.pluck); +~~~~~~~~ [Underscore]: http://underscorejs.org diff --git a/manuscript/markdown/Objects, Mutation, and State/this.md b/manuscript/markdown/Objects, Mutation, and State/this.md index eca6883..9d386b4 100644 --- a/manuscript/markdown/Objects, Mutation, and State/this.md +++ b/manuscript/markdown/Objects, Mutation, and State/this.md @@ -2,93 +2,111 @@ Let's take another look at [extensible objects](#extensible). Here's a Queue: - var QueueMaker = function () { - var queue = { - array: [], - head: 0, - tail: -1, - pushTail: function (value) { - return queue.array[queue.tail += 1] = value - }, - pullHead: function () { - var value; - - if (queue.tail >= queue.head) { - value = queue.array[queue.head]; - queue.array[queue.head] = void 0; - queue.head += 1; - return value - } - }, - isEmpty: function () { - return queue.tail < queue.head - } - }; - return queue - }; - - queue = QueueMaker() - queue.pushTail('Hello') - queue.pushTail('JavaScript') +{:lang="js"} +~~~~~~~~ +var QueueMaker = function () { + var queue = { + array: [], + head: 0, + tail: -1, + pushTail: function (value) { + return queue.array[queue.tail += 1] = value + }, + pullHead: function () { + var value; + + if (queue.tail >= queue.head) { + value = queue.array[queue.head]; + queue.array[queue.head] = void 0; + queue.head += 1; + return value + } + }, + isEmpty: function () { + return queue.tail < queue.head + } + }; + return queue +}; + +queue = QueueMaker() +queue.pushTail('Hello') +queue.pushTail('JavaScript') +~~~~~~~~ Let's make a copy of our queue using the `extend` recipe: - copyOfQueue = extend({}, queue); - - queue !== copyOfQueue - //=> true - +{:lang="js"} +~~~~~~~~ +copyOfQueue = extend({}, queue); + +queue !== copyOfQueue + //=> true +~~~~~~~~ + Wait a second. We know that array values are references. So it probably copied a reference to the original array. Let's make a copy of the array as well: - copyOfQueue.array = []; - for (var i = 0; i < 2; ++i) { - copyOfQueue.array[i] = queue.array[i] - } +{:lang="js"} +~~~~~~~~ +copyOfQueue.array = []; +for (var i = 0; i < 2; ++i) { + copyOfQueue.array[i] = queue.array[i] +} +~~~~~~~~ Now let's pull the head off the original: - queue.pullHead() - //=> 'Hello' - +{:lang="js"} +~~~~~~~~ +queue.pullHead() + //=> 'Hello' +~~~~~~~~ + If we've copied everything properly, we should get the exact same result when we pull the head off the copy: - - copyOfQueue.pullHead() - //=> 'JavaScript' - + +{:lang="js"} +~~~~~~~~ +copyOfQueue.pullHead() + //=> 'JavaScript' +~~~~~~~~ + What!? Even though we carefully made a copy of the array to prevent aliasing, it seems that our two queues behave like aliases of each other. The problem is that while we've carefully copied our array and other elements over, *the closures all share the same environment*, and therefore the functions in `copyOfQueue` all operate on the first queue's private data, not on the copies. A> This is a general issue with closures. Closures couple functions to environments, and that makes them very elegant in the small, and very handy for making opaque data structures. Alas, their strength in the small is their weakness in the large. When you're trying to make reusable components, this coupling is sometimes a hindrance. Let's take an impossibly optimistic flight of fancy: - var AmnesiacQueueMaker = function () { - return { - array: [], - head: 0, - tail: -1, - pushTail: function (myself, value) { - return myself.array[myself.tail += 1] = value - }, - pullHead: function (myself) { - var value; - - if (myself.tail >= myself.head) { - value = myself.array[myself.head]; - myself.array[myself.head] = void 0; - myself.head += 1; - return value - } - }, - isEmpty: function (myself) { - return myself.tail < myself.head - } +{:lang="js"} +~~~~~~~~ +var AmnesiacQueueMaker = function () { + return { + array: [], + head: 0, + tail: -1, + pushTail: function (myself, value) { + return myself.array[myself.tail += 1] = value + }, + pullHead: function (myself) { + var value; + + if (myself.tail >= myself.head) { + value = myself.array[myself.head]; + myself.array[myself.head] = void 0; + myself.head += 1; + return value } - }; + }, + isEmpty: function (myself) { + return myself.tail < myself.head + } + } +}; + +queueWithAmnesia = AmnesiacQueueMaker(); +queueWithAmnesia.pushTail(queueWithAmnesia, 'Hello'); +queueWithAmnesia.pushTail(queueWithAmnesia, 'JavaScript') +~~~~~~~~ - queueWithAmnesia = AmnesiacQueueMaker(); - queueWithAmnesia.pushTail(queueWithAmnesia, 'Hello'); - queueWithAmnesia.pushTail(queueWithAmnesia, 'JavaScript') - The `AmnesiacQueueMaker` makes queues with amnesia: They don't know who they are, so every time we invoke one of their functions, we have to tell them who they are. You can work out the implications for copying queues as a thought experiment: We don't have to worry about environments, because every function operates on the queue you pass in. The killer drawback, of course, is making sure we are always passing the correct queue in every time we invoke a function. What to do? @@ -97,47 +115,53 @@ The killer drawback, of course, is making sure we are always passing the correct Any time we must do the same repetitive thing over and over and over again, we industrial humans try to build a machine to do it for us. JavaScript is one such machine: - BanksQueueMaker = function () { - return { - array: [], - head: 0, - tail: -1, - pushTail: function (value) { - return this.array[this.tail += 1] = value - }, - pullHead: function () { - var value; - - if (this.tail >= this.head) { - value = this.array[this.head]; - this.array[this.head] = void 0; - this.head += 1; - return value - } - }, - isEmpty: function () { - return this.tail < this.head - } +{:lang="js"} +~~~~~~~~ +BanksQueueMaker = function () { + return { + array: [], + head: 0, + tail: -1, + pushTail: function (value) { + return this.array[this.tail += 1] = value + }, + pullHead: function () { + var value; + + if (this.tail >= this.head) { + value = this.array[this.head]; + this.array[this.head] = void 0; + this.head += 1; + return value } - }; + }, + isEmpty: function () { + return this.tail < this.head + } + } +}; - banksQueue = BanksQueueMaker(); - banksQueue.pushTail('Hello'); - banksQueue.pushTail('JavaScript') +banksQueue = BanksQueueMaker(); +banksQueue.pushTail('Hello'); +banksQueue.pushTail('JavaScript') +~~~~~~~~ Every time you invoke a function that is a member of an object, JavaScript binds that object to the name `this` in the environment of the function just as if it was an argument.[^this] Now we can easily make copies: - copyOfQueue = extend({}, banksQueue) - copyOfQueue.array = [] - for (var i = 0; i < 2; ++i) { - copyOfQueue.array[i] = banksQueue.array[i] - } - - banksQueue.pullHead() - //=> 'Hello' +{:lang="js"} +~~~~~~~~ +copyOfQueue = extend({}, banksQueue) +copyOfQueue.array = [] +for (var i = 0; i < 2; ++i) { + copyOfQueue.array[i] = banksQueue.array[i] +} + +banksQueue.pullHead() + //=> 'Hello' - copyOfQueue.pullHead() - //=> 'Hello' +copyOfQueue.pullHead() + //=> 'Hello' +~~~~~~~~ Presto, we now have a way to copy arrays. By getting rid of the closure and taking advantage of `this`, we have functions that are more easily portable between objects, and the code is simpler as well. @@ -145,4 +169,4 @@ There is more to `this` than we've discussed here. We'll explore things in more T> Closures tightly couple functions to the environments where they are created limiting their flexibility. Using `this` alleviates the coupling. Copying objects is but one example of where that flexibility is needed. -[^this]: JavaScript also does other things with `this` as well, but this is all we care about right now. \ No newline at end of file +[^this]: JavaScript also does other things with `this` as well, but this is all we care about right now. diff --git a/manuscript/markdown/Prefaces/legend.md b/manuscript/markdown/Prefaces/legend.md index f7bcc00..03136e2 100644 --- a/manuscript/markdown/Prefaces/legend.md +++ b/manuscript/markdown/Prefaces/legend.md @@ -2,19 +2,25 @@ Some text in monospaced type like `this` in the text represents some code being discussed. Some monospaced code in its own lines also represents code being discussed: - this.async = do (async = undefined) -> +{:lang="js"} +~~~~~~~~ +this.async = do (async = undefined) -> + + async = (fn) -> + (argv..., callback) -> + callback(fn.apply(this, argv)) +~~~~~~~~ - async = (fn) -> - (argv..., callback) -> - callback(fn.apply(this, argv)) - Sometimes it will contain some code for you to type in for yourself. When it does, the result of typing something in will often be shown using `//=>`, like this: - 2 + 2 - //=> 4 +{:lang="js"} +~~~~~~~~ +2 + 2 + //=> 4 +~~~~~~~~ T> A paragraph marked like this is a "key fact." It summarizes an idea without adding anything new. X> A paragraph marked like this is a suggested exercise to be performed on your own. -A> A paragraph marked like this is an aside. It can be safely ignored. It contains whimsey and other doupleplusunserious logorrhea that will *not* be on the test. \ No newline at end of file +A> A paragraph marked like this is an aside. It can be safely ignored. It contains whimsey and other doupleplusunserious logorrhea that will *not* be on the test. diff --git a/manuscript/markdown/Prefaces/recipes.md b/manuscript/markdown/Prefaces/recipes.md index 3de4652..f726e84 100644 --- a/manuscript/markdown/Prefaces/recipes.md +++ b/manuscript/markdown/Prefaces/recipes.md @@ -4,22 +4,28 @@ As noted, *JavaScript Allongé* alternates between chapters describing the seman The recipes share a common theme: They hail from a style of programming inspired by the creation of small functions that compose with each other. Using these recipes, you'll learn when it's appropriate to write: - return mapWith(maybe(getWith('name')))(customerList); - +{:lang="js"} +~~~~~~~~ +return mapWith(maybe(getWith('name')))(customerList); +~~~~~~~~ + Instead of: - return customerList.map(function (customer) { - if (customer) { - return customer.name - } - }); - +{:lang="js"} +~~~~~~~~ +return customerList.map(function (customer) { + if (customer) { + return customer.name + } +}); +~~~~~~~~ + As well as how it works and how to refactor it when you need. This style of programming is hardly the most common thing anyone does in JavaScript, so the argument can be made that more "practical" or "commonplace" recipes would be helpful. If you never read any other books about JavaScript, if you avoid blog posts and screen casts about JavaScript, if you don't attend workshops or talks about JavaScript, then I agree that this is not One Book to Rule Them All. But given that there are other resources out there, and that programmers are curious creatures with an unslakable thirst for personal growth, we choose to provide recipes that you are unlikely to find anywhere else in anything like this concentration. The recipes reinforce the lessons taught in the book about functions in JavaScript. You'll find all of the recipes collected online at [http://allong.es](http://allong.es). They're free to share under the MIT license. -[Reginald Braithwaite](http://braythwayt.com) -reg@braythwayt.com -@raganwald \ No newline at end of file +[Reginald Braithwaite](http://braythwayt.com) +reg@braythwayt.com +@raganwald diff --git a/manuscript/markdown/References and Rebinding/arrays.md b/manuscript/markdown/References and Rebinding/arrays.md index ba40cfc..0f8d04e 100644 --- a/manuscript/markdown/References and Rebinding/arrays.md +++ b/manuscript/markdown/References and Rebinding/arrays.md @@ -2,35 +2,47 @@ JavaScript provides two different kinds of containers for values. We've met one already, the array. Let's see how it treats values and identities. For starters, we'll learn how to extract a value from an array. We'll start with a function that makes a new value with a unique identity every time we call it. We already know that every function we create is unique, so that's what we'll use: - var unique = function () { - return function () {} - }; - - unique() - //=> [Function] - - unique() === unique() - //=> false +{:lang="js"} +~~~~~~~~ +var unique = function () { + return function () {} + }; + +unique() + //=> [Function] + +unique() === unique() + //=> false +~~~~~~~~ Let's verify that what we said about references applies to functions as well as arrays: - var x = unique(), - y = x; - - x === y - //=> true +{:lang="js"} +~~~~~~~~ +var x = unique(), + y = x; + +x === y + //=> true +~~~~~~~~ Ok. So what about things *inside* arrays? We know how to create an array with something inside it: - [ unique() ] - //=> [ [Function] ] +{:lang="js"} +~~~~~~~~ +[ unique() ] + //=> [ [Function] ] +~~~~~~~~ That's an array with one of our unique functions in it. How do we get something *out* of it? - var a = [ 'hello' ]; - - a[0] - //=> 'hello' +{:lang="js"} +~~~~~~~~ +var a = [ 'hello' ]; + +a[0] + //=> 'hello' +~~~~~~~~ Cool, arrays work a lot like arrays in other languages and are zero-based. The trouble with this example is that strings are value types in JavaScript, so we have no idea whether `a[0]` always gives us the same value back like looking up a name in an environment, or whether it does some magic that tries to give us a new value. @@ -40,24 +52,30 @@ We need to put a reference type into an array. If we get the same thing back, we Let's test it: - var unique = function () { - return function () {} - }, - x = unique(), - a = [ x ]; - - a[0] === x - //=> true +{:lang="js"} +~~~~~~~~ +var unique = function () { + return function () {} + }, + x = unique(), + a = [ x ]; + +a[0] === x + //=> true +~~~~~~~~ If we get a value out of an array using the `[]` suffix, it's the exact same value with the same identity. Question: Does that apply to other locations in the array? Yes: - var unique = function () { - return function () {} - }, - x = unique(), - y = unique(), - z = unique(), - a = [ x, y, z ]; - - a[0] === x && a[1] === y && a[2] === z - //=> true \ No newline at end of file +{:lang="js"} +~~~~~~~~ +var unique = function () { + return function () {} + }, + x = unique(), + y = unique(), + z = unique(), + a = [ x, y, z ]; + +a[0] === x && a[1] === y && a[2] === z + //=> true +~~~~~~~~ diff --git a/manuscript/markdown/References and Rebinding/modules.md b/manuscript/markdown/References and Rebinding/modules.md index 95a7891..66e02d6 100644 --- a/manuscript/markdown/References and Rebinding/modules.md +++ b/manuscript/markdown/References and Rebinding/modules.md @@ -4,21 +4,27 @@ In the section on [let and Var](#let), we learned that we can create a new environment any time we want by combining a function definition with a function invocation, to whit: - (function () { - // - })(); +{:lang="js"} +~~~~~~~~ +(function () { + // +})(); +~~~~~~~~ Because this function is invoked, if it contains a return statement, it evaluates to a value of some kind. So you can, for example, write something like: - var factorialOfTwentyFive = (function () { - var factorial = function (num) { - if (num < 2 ) { - return 1 - } - else return num * factorial (num - 1) - } - return factorial(25) - })(); +{:lang="js"} +~~~~~~~~ +var factorialOfTwentyFive = (function () { + var factorial = function (num) { + if (num < 2 ) { + return 1 + } + else return num * factorial (num - 1) + } + return factorial(25) +})(); +~~~~~~~~ This could have been written using a named function to avoid the need for a let, but as we'll see in the [memoize](#recipe) later, sometimes there's good reason to write it like this. In any event, our let serves to create a scope for the `factorial` function. Presumably we write it this way to signal that we do not want to use it elsewhere, so putting it inside of a let keeps it invisible from the rest of the code. @@ -28,113 +34,122 @@ You'll note that once we've calculated the factorial of 25, we have no further n The transient let only uses its environment to generate the result, then it can be discarded. Another type of let is the **private closure**. This let returns a closure that references one or more bindings in the let's environment. For example: - var counter = (function () { - var value = 0; - - return function () { - return value++ - } - })(); - - counter() - //=> 0 - - counter() - //=> 1 - - counter() - //=> 2 +{:lang="js"} +~~~~~~~~ +var counter = (function () { + var value = 0; + + return function () { + return value++ + } +})(); + +counter() + //=> 0 + +counter() + //=> 1 + +counter() + //=> 2 +~~~~~~~~ `counter` is bound to a function closure that references the binding `value` in the let's environment. So the environment isn't transient, it remains active until the function bound to the name `counter` is discarded. Private closures are often used to manage state as we see in the counter example, but they can also be used for helper functions. For example, this date format function cribbed from somewhere or other has a helper function that isn't used anywhere else: - function formatDate (time) { - var date; - - if (time) { - date = unformattedDate(time); - // Have to massage the date because - // we can't create a date - // based on GMT which the server gives us - - if (!(/-\d\d:\d\d/.test(time))) { - date.setHours( - date.getHours() - date.getTimezoneOffset()/60); - } - - var diff = ( - (new Date()).getTime() - date.getTime() - ) / 1000; - day_diff = Math.floor(diff / 86400); - - if ( isNaN(day_diff) || day_diff < 0 ) - return; - - return '' + (day_diff == 0 && ( - diff < 60 && "just now" || - diff < 120 && "1 minute ago" || - diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || - diff < 7200 && "1 hour ago" || - diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || - day_diff == 1 && "Yesterday" || - day_diff < 7 && day_diff + " days ago" || - day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" || - (day_diff < 360 && day_diff >= 31) && Math.ceil(day_diff / 31) + - ' month' + (day_diff == 31 ? '' : 's') + ' ago' || - day_diff > 360 && Math.floor( day_diff / 360) + " years " + - Math.floor(day_diff%360/32) + " months ago") + ''; - } - else return '-' - - function unformattedDate (time) { - return new Date((time || "").replace(/[-+]/g,"/"). - replace(/[TZ]/g," ").replace(/\/\d\d:\d\d/, '')); - } +{:lang="js"} +~~~~~~~~ +function formatDate (time) { + var date; + + if (time) { + date = unformattedDate(time); + // Have to massage the date because + // we can't create a date + // based on GMT which the server gives us + + if (!(/-\d\d:\d\d/.test(time))) { + date.setHours( + date.getHours() - date.getTimezoneOffset()/60); } - + + var diff = ( + (new Date()).getTime() - date.getTime() + ) / 1000; + day_diff = Math.floor(diff / 86400); + + if ( isNaN(day_diff) || day_diff < 0 ) + return; + + return '' + (day_diff == 0 && ( + diff < 60 && "just now" || + diff < 120 && "1 minute ago" || + diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || + diff < 7200 && "1 hour ago" || + diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || + day_diff == 1 && "Yesterday" || + day_diff < 7 && day_diff + " days ago" || + day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" || + (day_diff < 360 && day_diff >= 31) && Math.ceil(day_diff / 31) + + ' month' + (day_diff == 31 ? '' : 's') + ' ago' || + day_diff > 360 && Math.floor( day_diff / 360) + " years " + + Math.floor(day_diff%360/32) + " months ago") + ''; + } + else return '-' + + function unformattedDate (time) { + return new Date((time || "").replace(/[-+]/g,"/"). + replace(/[TZ]/g," ").replace(/\/\d\d:\d\d/, '')); + } +} +~~~~~~~~ + Every time we call `formatDate`, JavaScript will create an entirely new `unformattedDate` function. That is not necessary, since it's a pure function. In theory, a sufficiently smart interpreter would notice this and only create one function. In practice, we can rewrite it to use a private closure and only create one helper function: - var formatDate = (function () { - return function (time) { - var date; - - if (time) { - date = unformattedDate(time); - // Have to massage the date because we can't create a date - // based on GMT which the server gives us - - if (!(/-\d\d:\d\d/.test(time))) { - date.setHours(date.getHours() - date.getTimezoneOffset()/60); - } - - var diff = ((new Date()).getTime() - date.getTime()) / 1000; - day_diff = Math.floor(diff / 86400); - - if ( isNaN(day_diff) || day_diff < 0 ) - return; - - return '' + (day_diff == 0 && ( - diff < 60 && "just now" || - diff < 120 && "1 minute ago" || - diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || - diff < 7200 && "1 hour ago" || - diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || - day_diff == 1 && "Yesterday" || - day_diff < 7 && day_diff + " days ago" || - day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" || - (day_diff < 360 && day_diff >= 31) && Math.ceil(day_diff / 31) + - ' month' + (day_diff == 31 ? '' : 's') + ' ago' || - day_diff > 360 && Math.floor( day_diff / 360) + - " years " + Math.floor(day_diff%360/32) + " months ago") + ''; - } - else return '-' - } - - function unformattedDate (time) { - return new Date((time || "").replace(/[-+]/g,"/").replace(/[TZ]/g," ").replace(/\/\d\d:\d\d/, '')); +{:lang="js"} +~~~~~~~~ +var formatDate = (function () { + return function (time) { + var date; + + if (time) { + date = unformattedDate(time); + // Have to massage the date because we can't create a date + // based on GMT which the server gives us + + if (!(/-\d\d:\d\d/.test(time))) { + date.setHours(date.getHours() - date.getTimezoneOffset()/60); } - })(); + + var diff = ((new Date()).getTime() - date.getTime()) / 1000; + day_diff = Math.floor(diff / 86400); + + if ( isNaN(day_diff) || day_diff < 0 ) + return; + + return '' + (day_diff == 0 && ( + diff < 60 && "just now" || + diff < 120 && "1 minute ago" || + diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" || + diff < 7200 && "1 hour ago" || + diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || + day_diff == 1 && "Yesterday" || + day_diff < 7 && day_diff + " days ago" || + day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago" || + (day_diff < 360 && day_diff >= 31) && Math.ceil(day_diff / 31) + + ' month' + (day_diff == 31 ? '' : 's') + ' ago' || + day_diff > 360 && Math.floor( day_diff / 360) + + " years " + Math.floor(day_diff%360/32) + " months ago") + ''; + } + else return '-' + } + + function unformattedDate (time) { + return new Date((time || "").replace(/[-+]/g,"/").replace(/[TZ]/g," ").replace(/\/\d\d:\d\d/, '')); + } +})(); +~~~~~~~~ The function `unformattedDate` is still private to `formatDate`, but now we no longer need to construct an entirely new function every time `formatDate` is called. @@ -146,23 +161,26 @@ Consider a module designed to draw some bits on a virtual screen. The public API It looks like this: - var DrawModule = (function () { - - return { - drawLine: drawLine, - drawRect: drawRect, - drawCircle: drawCircle - } - - // public methods - function drawLine(screen, leftPoint, rightPoint) { ... } - function drawRect(screen, topLeft, bottomRight) { ... } - function drawCircle(screen, center, radius) { ... } - - // private helpers - function bitBlt (screen, ...) { ... } - function resize (screen, ...) { ... } - - })(); - +{:lang="js"} +~~~~~~~~ +var DrawModule = (function () { + + return { + drawLine: drawLine, + drawRect: drawRect, + drawCircle: drawCircle + } + + // public methods + function drawLine(screen, leftPoint, rightPoint) { ... } + function drawRect(screen, topLeft, bottomRight) { ... } + function drawCircle(screen, center, radius) { ... } + + // private helpers + function bitBlt (screen, ...) { ... } + function resize (screen, ...) { ... } + +})(); +~~~~~~~~ + You can then call the public functions using `DrawModule.drawCircle(...)`. The concept scales up to include the concept of state (such as setting default line styles), but when you look at it, it's really just the private closure let with a little more complexity in the form of returning an object with more than one function. diff --git a/manuscript/markdown/References and Rebinding/objects.md b/manuscript/markdown/References and Rebinding/objects.md index bb4cbe1..4af1d83 100644 --- a/manuscript/markdown/References and Rebinding/objects.md +++ b/manuscript/markdown/References and Rebinding/objects.md @@ -6,55 +6,78 @@ In JavaScript, an object[^pojo] is a map from names to values, a lot like an env [^pojo]: Tradition would have us call objects that don't contain any functions "POJOs," meaning Plain Old JavaScript Objects. - { year: 2012, month: 6, day: 14 } +{:lang="js"} +~~~~~~~~ +{ year: 2012, month: 6, day: 14 } +~~~~~~~~ Two objects created this way have differing identities, just like arrays: - { year: 2012, month: 6, day: 14 } === { year: 2012, month: 6, day: 14 } - //=> false +{:lang="js"} +~~~~~~~~ +{ year: 2012, month: 6, day: 14 } === { year: 2012, month: 6, day: 14 } + //=> false +~~~~~~~~ Objects use `[]` to access the values by name, using a string: - { year: 2012, month: 6, day: 14 }['day'] - //=> 14 +{:lang="js"} +~~~~~~~~ +{ year: 2012, month: 6, day: 14 }['day'] + //=> 14 +~~~~~~~~ Values contained within an object work just like values contained within an array: - var unique = function () { - return function () {} - }, - x = unique(), - y = unique(), - z = unique(), - o = { a: x, b: y, c: z }; +{:lang="js"} +~~~~~~~~ +var unique = function () { + return function () {} + }, + x = unique(), + y = unique(), + z = unique(), + o = { a: x, b: y, c: z }; - o['a'] === x && o['b'] === y && o['c'] === z - //=> true +o['a'] === x && o['b'] === y && o['c'] === z + //=> true +~~~~~~~~ Names needn't be alphanumeric strings. For anything else, enclose the label in quotes: - { 'first name': 'reginald', 'last name': 'lewis' }['first name'] - //=> 'reginald' +{:lang="js"} +~~~~~~~~ +{ 'first name': 'reginald', 'last name': 'lewis' }['first name'] + //=> 'reginald' +~~~~~~~~ If the name is an alphanumeric string conforming to the same rules as names of variables, there's a simplified syntax for accessing the values: - { year: 2012, month: 6, day: 14 }['day'] === - { year: 2012, month: 6, day: 14 }.day - //=> true +{:lang="js"} +~~~~~~~~ +{ year: 2012, month: 6, day: 14 }['day'] === + { year: 2012, month: 6, day: 14 }.day + //=> true +~~~~~~~~ All containers can contain any value, including functions or other containers: - var Mathematics = { - abs: function (a) { - return a < 0 ? -a : a - } - }; +{:lang="js"} +~~~~~~~~ +var Mathematics = { + abs: function (a) { + return a < 0 ? -a : a + } +}; - Mathematics.abs(-5) - //=> 5 +Mathematics.abs(-5) + //=> 5 +~~~~~~~~ Funny we should mention `Mathematics`. If you recall, JavaScript provides a global environment that contains some existing objects that have handy functions you can use. One of them is called `Math`, and it contains functions for `abs`, `max`, `min`, and many others. Since it is always available, you can use it in any environment provided you don't shadow `Math`. - Math.abs(-5) - //=> 5 - +{:lang="js"} +~~~~~~~~ +Math.abs(-5) + //=> 5 +~~~~~~~~ diff --git a/manuscript/markdown/References and Rebinding/reassignment.md b/manuscript/markdown/References and Rebinding/reassignment.md index 07c01a3..b6c1d07 100644 --- a/manuscript/markdown/References and Rebinding/reassignment.md +++ b/manuscript/markdown/References and Rebinding/reassignment.md @@ -2,47 +2,65 @@ Like most imperative programming languages, JavaScript allows you to re-assign the value of variables. The syntax is familiar to users of most popular languages: - var age = 49; - age = 50; - age - //=> 50 +{:lang="js"} +~~~~~~~~ +var age = 49; +age = 50; +age + //=> 50 +~~~~~~~~ We took the time to carefully examine what happens with bindings in environments. Let's take the time to explore what happens with reassigning values to variables. The key is to understand that we are rebinding a different value to the same name in the same environment. So let's consider what happens with a shadowed variable: - (function () { - var age = 49; - (function () { - var age = 50; - })(); - return age; - })() - //=> 49 +{:lang="js"} +~~~~~~~~ +(function () { + var age = 49; + (function () { + var age = 50; + })(); + return age; +})() + //=> 49 +~~~~~~~~ Binding `50` to age in the inner environment does not change `age` in the outer environment because the binding of `age` in the inner environment shadows the binding of `age` in the outer environment. We go from: - {age: 49, '..': global-environment} - +{:lang="js"} +~~~~~~~~ +{age: 49, '..': global-environment} +~~~~~~~~ + To: - {age: 50, '..': {age: 49, '..': global-environment}} - +{:lang="js"} +~~~~~~~~ +{age: 50, '..': {age: 49, '..': global-environment}} +~~~~~~~~ + Then back to: - {age: 49, '..': global-environment} +{:lang="js"} +~~~~~~~~ +{age: 49, '..': global-environment} +~~~~~~~~ -{pagebreak} +{pagebreak} However, if we don't shadow `age` by explicitly using `var`, reassigning it in a nested environment changes the original: - (function () { - var age = 49; - (function () { - age = 50; - })(); - return age; - })() - //=> 50 +{:lang="js"} +~~~~~~~~ +(function () { + var age = 49; + (function () { + age = 50; + })(); + return age; +})() + //=> 50 +~~~~~~~~ Like evaluating variable labels, when a binding is rebound, JavaScript searches for the binding in the current environment and then each ancestor in turn until it finds one. It then rebinds the name in that environment. @@ -52,59 +70,80 @@ Like evaluating variable labels, when a binding is rebound, JavaScript searches Now that we can reassign things, there's another important factor to consider: Some values can *mutate*. Their identities stay the same, but not their structure. Specifically, arrays and objects can mutate. Recall that you can access a value from within an array or an object using `[]`. You can reassign a value using `[]` as well: - var oneTwoThree = [1, 2, 3]; - oneTwoThree[0] = 'one'; - oneTwoThree - //=> [ 'one', 2, 3 ] +{:lang="js"} +~~~~~~~~ +var oneTwoThree = [1, 2, 3]; +oneTwoThree[0] = 'one'; +oneTwoThree + //=> [ 'one', 2, 3 ] +~~~~~~~~ You can even add a value: - var oneTwoThree = [1, 2, 3]; - oneTwoThree[3] = 'four'; - oneTwoThree - //=> [ 1, 2, 3, 'four' ] +{:lang="js"} +~~~~~~~~ +var oneTwoThree = [1, 2, 3]; +oneTwoThree[3] = 'four'; +oneTwoThree + //=> [ 1, 2, 3, 'four' ] +~~~~~~~~ You can do the same thing with both syntaxes for accessing objects: - var name = {firstName: 'Leonard', lastName: 'Braithwaite'}; - name.middleName = 'Austin' - name - //=> { firstName: 'Leonard', - # lastName: 'Braithwaite', - # middleName: 'Austin' } +{:lang="js"} +~~~~~~~~ +var name = {firstName: 'Leonard', lastName: 'Braithwaite'}; +name.middleName = 'Austin' +name + //=> { firstName: 'Leonard', + # lastName: 'Braithwaite', + # middleName: 'Austin' } +~~~~~~~~ We have established that JavaScript's semantics allow for two different bindings to refer to the same value. For example: - var allHallowsEve = [2012, 10, 31] - var halloween = allHallowsEve; - +{:lang="js"} +~~~~~~~~ +var allHallowsEve = [2012, 10, 31] +var halloween = allHallowsEve; +~~~~~~~~ + Both `halloween` and `allHallowsEve` are bound to the same array value within the local environment. And also: - var allHallowsEve = [2012, 10, 31]; - (function (halloween) { - // ... - })(allHallowsEve); +{:lang="js"} +~~~~~~~~ +var allHallowsEve = [2012, 10, 31]; +(function (halloween) { + // ... +})(allHallowsEve); +~~~~~~~~ There are two nested environments, and each one binds a name to the exact same array value. In each of these examples, we have created two *aliases* for the same value. Before we could reassign things, the most important point about this is that the identities were the same, because they were the same value. This is vital. Consider what we already know about shadowing: - var allHallowsEve = [2012, 10, 31]; - (function (halloween) { - halloween = [2013, 10, 31]; - })(allHallowsEve); - allHallowsEve - //=> [2012, 10, 31] - +{:lang="js"} +~~~~~~~~ +var allHallowsEve = [2012, 10, 31]; +(function (halloween) { + halloween = [2013, 10, 31]; +})(allHallowsEve); +allHallowsEve + //=> [2012, 10, 31] +~~~~~~~~ + The outer value of `allHallowsEve` was not changed because all we did was rebind the name `halloween` within the inner environment. However, what happens if we *mutate* the value in the inner environment? - var allHallowsEve = [2012, 10, 31]; - (function (halloween) { - halloween[0] = 2013; - })(allHallowsEve); - allHallowsEve - //=> [2013, 10, 31] - +{:lang="js"} +~~~~~~~~ +var allHallowsEve = [2012, 10, 31]; +(function (halloween) { + halloween[0] = 2013; +})(allHallowsEve); +allHallowsEve + //=> [2013, 10, 31] +~~~~~~~~ + This is different. We haven't rebound the inner name to a different variable, we've mutated the value that both bindings share. Now that we've finished with mutation and aliases, let's have a look at it. - -T> JavaScript permits the reassignment of new values to existing bindings, as well as the reassignment and assignment of new values to elements of containers such as arrays and objects. Mutating existing objects has special implications when two bindings are aliases of the same value. \ No newline at end of file + +T> JavaScript permits the reassignment of new values to existing bindings, as well as the reassignment and assignment of new values to elements of containers such as arrays and objects. Mutating existing objects has special implications when two bindings are aliases of the same value. diff --git a/manuscript/markdown/References and Rebinding/recipes/extend.md b/manuscript/markdown/References and Rebinding/recipes/extend.md index 55e06dc..e289284 100644 --- a/manuscript/markdown/References and Rebinding/recipes/extend.md +++ b/manuscript/markdown/References and Rebinding/recipes/extend.md @@ -2,102 +2,123 @@ It's very common to want to "extend" an object by adding properties to it: - var inventory = { - apples: 12, - oranges: 12 - }; - - inventory.bananas = 54; - inventory.pears = 24; +{:lang="js"} +~~~~~~~~ +var inventory = { + apples: 12, + oranges: 12 +}; + +inventory.bananas = 54; +inventory.pears = 24; +~~~~~~~~ It's also common to want to add a [shallow copy] of the properties of one object to another: [shallow copy]: https://en.wikipedia.org/wiki/Object_copy#Shallow_copy - for (var fruit in shipment) { - inventory[fruit] = shipment[fruit] - } +{:lang="js"} +~~~~~~~~ +for (var fruit in shipment) { + inventory[fruit] = shipment[fruit] +} +~~~~~~~~ Both needs can be met with this recipe for `extend`: - var extend = variadic( function (consumer, providers) { - var key, - i, - provider; - - for (i = 0; i < providers.length; ++i) { - provider = providers[i]; - for (key in provider) { - if (provider.hasOwnProperty(key)) { - consumer[key] = provider[key] - } - } +{:lang="js"} +~~~~~~~~ +var extend = variadic( function (consumer, providers) { + var key, + i, + provider; + + for (i = 0; i < providers.length; ++i) { + provider = providers[i]; + for (key in provider) { + if (provider.hasOwnProperty(key)) { + consumer[key] = provider[key] } - return consumer - }); - + } + } + return consumer +}); +~~~~~~~~ + You can copy an object by extending an empty object: - extend({}, { - apples: 12, - oranges: 12 - }) - //=> { apples: 12, oranges: 12 } +{:lang="js"} +~~~~~~~~ +extend({}, { + apples: 12, + oranges: 12 +}) + //=> { apples: 12, oranges: 12 } +~~~~~~~~ You can extend one object with another: - var inventory = { - apples: 12, - oranges: 12 - }; - - var shipment = { - bananas: 54, - pears: 24 - } - - extend(inventory, shipment) - //=> { apples: 12, - // oranges: 12, - // bananas: 54, - // pears: 24 } - +{:lang="js"} +~~~~~~~~ +var inventory = { + apples: 12, + oranges: 12 +}; + +var shipment = { + bananas: 54, + pears: 24 +} + +extend(inventory, shipment) + //=> { apples: 12, + // oranges: 12, + // bananas: 54, + // pears: 24 } +~~~~~~~~ + And when we discuss prototypes, we will use `extend` to turn this: - var Queue = function () { - this.array = []; - this.head = 0; - this.tail = -1 - }; - - Queue.prototype.pushTail = function (value) { - // ... - }; - Queue.prototype.pullHead = function () { - // ... - }; - Queue.prototype.isEmpty = function () { - // ... - } +{:lang="js"} +~~~~~~~~ +var Queue = function () { + this.array = []; + this.head = 0; + this.tail = -1 +}; + +Queue.prototype.pushTail = function (value) { + // ... +}; +Queue.prototype.pullHead = function () { + // ... +}; +Queue.prototype.isEmpty = function () { + // ... +} +~~~~~~~~ Into this: - var Queue = function () { - extend(this, { - array: [], - head: 0, - tail: -1 - }) - }; - - extend(Queue.prototype, { - pushTail: function (value) { - // ... - }, - pullHead: function () { - // ... - }, - isEmpty: function () { - // ... - } - }); \ No newline at end of file +{:lang="js"} +~~~~~~~~ +var Queue = function () { + extend(this, { + array: [], + head: 0, + tail: -1 + }) +}; + +extend(Queue.prototype, { + pushTail: function (value) { + // ... + }, + pullHead: function () { + // ... + }, + isEmpty: function () { + // ... + } +}); +~~~~~~~~ diff --git a/manuscript/markdown/References and Rebinding/recipes/flip.md b/manuscript/markdown/References and Rebinding/recipes/flip.md index 1a1450c..3f8bcbf 100644 --- a/manuscript/markdown/References and Rebinding/recipes/flip.md +++ b/manuscript/markdown/References and Rebinding/recipes/flip.md @@ -2,103 +2,136 @@ When we wrote [mapWith](#mapWith), we wrote it like this: - function mapWith (fn) { - return function (list) { - return Array.prototype.map.call(list, function (something) { - return fn.call(this, something); - }); - }; - }; +{:lang="js"} +~~~~~~~~ +function mapWith (fn) { + return function (list) { + return Array.prototype.map.call(list, function (something) { + return fn.call(this, something); + }); + }; +}; +~~~~~~~~ Let's consider the case whether we have a `map` function of our own, perhaps from the [allong.es](http://allong.es) library, perhaps from [Underscore](http://underscorejs.org). We could write our function something like this: - function mapWith (fn) { - return function (list) { - return map.call(list, fn); - }; - }; +{:lang="js"} +~~~~~~~~ +function mapWith (fn) { + return function (list) { + return map.call(list, fn); + }; +}; +~~~~~~~~ Looking at this, we see we're conflating two separate transformations. First, we're reversing the order of arguments. You can see that if we simplify it: - function mapWith (fn, list) { - return map.call(list, fn); - }; +{:lang="js"} +~~~~~~~~ +function mapWith (fn, list) { + return map.call(list, fn); +}; +~~~~~~~~ Second, we're "currying" the function so that instead of defining a function that takes two arguments, it returns a function that takes the first argument and returns a function that takes the second argument and applies them both, like this: - function mapCurried (list) { - return function (fn) { - return map(list, fn); - }; - }; +{:lang="js"} +~~~~~~~~ +function mapCurried (list) { + return function (fn) { + return map(list, fn); + }; +}; +~~~~~~~~ Let's return to the implementation that does both: - function mapWith (fn) { - return function (list) { - return map.call(list, fn); - }; - }; +{:lang="js"} +~~~~~~~~ +function mapWith (fn) { + return function (list) { + return map.call(list, fn); + }; +}; +~~~~~~~~ Now let's put a wrapper around it: - function wrapper () { - return function (fn) { - return function (list) { - return map.call(list, fn); - }; - }; +{:lang="js"} +~~~~~~~~ +function wrapper () { + return function (fn) { + return function (list) { + return map.call(list, fn); }; + }; +}; +~~~~~~~~ Abstract the parameter names: - function wrapper () { - return function (first) { - return function (second) { - return map.call(second, first); - }; - }; +{:lang="js"} +~~~~~~~~ +function wrapper () { + return function (first) { + return function (second) { + return map.call(second, first); }; + }; +}; +~~~~~~~~ And finally, extract the function as a parameter: - function wrapper (fn) { - return function (first) { - return function (second) { - return fn.call(second, first); - }; - }; +{:lang="js"} +~~~~~~~~ +function wrapper (fn) { + return function (first) { + return function (second) { + return fn.call(second, first); }; + }; +}; +~~~~~~~~ What we have now is a function that takes a function and "flips" the order of arguments around, then curries it: - function flip (fn) { - return function (first) { - return function (second) { - return fn.call(this, second, first); - }; - }; +{:lang="js"} +~~~~~~~~ +function flip (fn) { + return function (first) { + return function (second) { + return fn.call(this, second, first); }; + }; +}; +~~~~~~~~ This is gold. Consider how we define [mapWith](#mapWith) now: - var mapWith = flip(map); +{:lang="js"} +~~~~~~~~ +var mapWith = flip(map); +~~~~~~~~ Much nicer! There's one final decoration. Sometimes we'll want to flip a function but retain the flexibility to call it with both parameters at once. No problem: - function flip (fn) { - return function (first, second) { - if (arguments.length === 2) { - return fn.call(this, second, first); - } - else { - return function (second) { - return fn.call(this, second, first); - }; - }; +{:lang="js"} +~~~~~~~~ +function flip (fn) { + return function (first, second) { + if (arguments.length === 2) { + return fn.call(this, second, first); + } + else { + return function (second) { + return fn.call(this, second, first); }; }; + }; +}; +~~~~~~~~ -Now you can call `mapWith(fn, list)` or `mapWith(fn)(list)`, your choice. \ No newline at end of file +Now you can call `mapWith(fn, list)` or `mapWith(fn)(list)`, your choice. diff --git a/manuscript/markdown/References and Rebinding/recipes/map-with.md b/manuscript/markdown/References and Rebinding/recipes/map-with.md index aa69929..f738555 100644 --- a/manuscript/markdown/References and Rebinding/recipes/map-with.md +++ b/manuscript/markdown/References and Rebinding/recipes/map-with.md @@ -2,51 +2,66 @@ In recent versions of JavaScript, arrays have a `.map` method. Map takes a function as an argument, and applies it to each of the elements of the array, then returns the results in another array. For example: - [1, 2, 3, 4, 5].map(function (n) { - return n*n - }) - //=> [1, 4, 9, 16, 25] - +{:lang="js"} +~~~~~~~~ +[1, 2, 3, 4, 5].map(function (n) { + return n*n +}) + //=> [1, 4, 9, 16, 25] +~~~~~~~~ + We say that `.map` *maps* its arguments over the receiver array's elements. Or if you prefer, that it defines a mapping between its receiver and its result. Libraries like [Underscore] provide a map *function*.[^why] It usually works like this: - _.map([1, 2, 3, 4, 5], function (n) { - return n*n - }) - //=> [1, 4, 9, 16, 25] - +{:lang="js"} +~~~~~~~~ +_.map([1, 2, 3, 4, 5], function (n) { + return n*n +}) + //=> [1, 4, 9, 16, 25] +~~~~~~~~ + [^why]: Why provide a map function? well, JavaScript is an evolving language, and when you're writing code that runs in a web browser, you may want to support browsers using older versions of JavaScript that didn't provide the `.map` function. One way to do that is to "shim" the map method into the Array class, the other way is to use a map function. Most library implementations of map will default to the `.map` method if its available. This recipe isn't for `map`: It's for `mapWith`, a function that wraps around `map` and turns any other function into a mapping. In concept, `mapWith` is very simple:[^mapWith] - function mapWith (fn) { - return function (list) { - return Array.prototype.map.call(list, function (something) { - return fn.call(this, something); - }); - }; - }; +{:lang="js"} +~~~~~~~~ +function mapWith (fn) { + return function (list) { + return Array.prototype.map.call(list, function (something) { + return fn.call(this, something); + }); + }; +}; +~~~~~~~~ Here's the above code written using `mapWith`: - var squareMap = mapWith(function (n) { - return n*n; - }); - - squareMap([1, 2, 3, 4, 5]) - //=> [1, 4, 9, 16, 25] - +{:lang="js"} +~~~~~~~~ +var squareMap = mapWith(function (n) { + return n*n; +}); + +squareMap([1, 2, 3, 4, 5]) + //=> [1, 4, 9, 16, 25] +~~~~~~~~ + If we didn't use `mapWith`, we'd have written something like this: - var squareMap = function (array) { - return Array.prototype.map.call(array, function (n) { - return n*n; - }); - }; - +{:lang="js"} +~~~~~~~~ +var squareMap = function (array) { + return Array.prototype.map.call(array, function (n) { + return n*n; + }); +}; +~~~~~~~~ + And we'd do that every time we wanted to construct a method that maps an array to some result. `mapWith` is a very convenient abstraction for a very common pattern. *`mapWith` was suggested by [ludicast](http://github.com/ludicast)* - + [Underscore]: http://underscorejs.org -[^mapWith]: If we were always mapWithting arrays, we could write `list.map(fn)`. However, there are some objects that have a `.length` property and `[]` accessors that can be mapWithted but do not have a `.map` method. `mapWith` works with those objects. This points to a larger issue around the question of whether containers really ought to implement methods like `.map`. In a language like JavaScript, we are free to define objects that know about their own implementations, such as exactly how `[]` and `.length` works and then to define standalone functions that do the rest. \ No newline at end of file +[^mapWith]: If we were always mapWithting arrays, we could write `list.map(fn)`. However, there are some objects that have a `.length` property and `[]` accessors that can be mapWithted but do not have a `.map` method. `mapWith` works with those objects. This points to a larger issue around the question of whether containers really ought to implement methods like `.map`. In a language like JavaScript, we are free to define objects that know about their own implementations, such as exactly how `[]` and `.length` works and then to define standalone functions that do the rest. diff --git a/manuscript/markdown/References and Rebinding/recipes/once.md b/manuscript/markdown/References and Rebinding/recipes/once.md index c17195d..eec8d2f 100644 --- a/manuscript/markdown/References and Rebinding/recipes/once.md +++ b/manuscript/markdown/References and Rebinding/recipes/once.md @@ -2,39 +2,48 @@ `once` is an extremely helpful combinator. It ensures that a function can only be called, well, *once*. Here's the recipe: - function once (fn) { - var done = false; - - return function () { - return done ? void 0 : ((done = true), fn.apply(this, arguments)) - } - } +{:lang="js"} +~~~~~~~~ +function once (fn) { + var done = false; + + return function () { + return done ? void 0 : ((done = true), fn.apply(this, arguments)) + } +} +~~~~~~~~ Very simple! You pass it a function, and you get a function back. That function will call your function once, and thereafter will return `undefined` whenever it is called. Let's try it: - var askedOnBlindDate = once(function () { - return 'sure, why not?' - }); - - askedOnBlindDate() - //=> 'sure, why not?' - - askedOnBlindDate() - //=> undefined - - askedOnBlindDate() - //=> undefined +{:lang="js"} +~~~~~~~~ +var askedOnBlindDate = once(function () { + return 'sure, why not?' +}); -It seems some people will only try blind dating once. But you do have to be careful that you are calling the function `once` returns multiple times. If you keep calling `once`, you'll get a new function that executes once, so you'll keep calling your function: +askedOnBlindDate() + //=> 'sure, why not?' + +askedOnBlindDate() + //=> undefined - once(function () { - return 'sure, why not?' - })() - //=> 'sure, why not?' +askedOnBlindDate() + //=> undefined +~~~~~~~~ + +It seems some people will only try blind dating once. But you do have to be careful that you are calling the function `once` returns multiple times. If you keep calling `once`, you'll get a new function that executes once, so you'll keep calling your function: - once(function () { - return 'sure, why not?' - })() - //=> 'sure, why not?' +{:lang="js"} +~~~~~~~~ +once(function () { + return 'sure, why not?' +})() + //=> 'sure, why not?' + +once(function () { + return 'sure, why not?' +})() + //=> 'sure, why not?' +~~~~~~~~ This is expected, but sometimes not what we want. So we must either be careful with our code, or use a variation, the [named once](#named-once) recipe. diff --git a/manuscript/markdown/References and Rebinding/recipes/y.md b/manuscript/markdown/References and Rebinding/recipes/y.md index 54aedf6..1f435af 100644 --- a/manuscript/markdown/References and Rebinding/recipes/y.md +++ b/manuscript/markdown/References and Rebinding/recipes/y.md @@ -2,28 +2,34 @@ This is the [canonical Y Combinator][y]: - function Y (f) { - return ((function (x) { - return f(function (v) { - return x(x)(v); - }); - })(function (x) { - return f(function (v) { - return x(x)(v); - }); - })); - } +{:lang="js"} +~~~~~~~~ +function Y (f) { + return ((function (x) { + return f(function (v) { + return x(x)(v); + }); + })(function (x) { + return f(function (v) { + return x(x)(v); + }); + })); +} +~~~~~~~~ You use it like this: - var factorial = Y(function (fac) { - return function (n) { - return (n == 0 ? 1 : n * fac(n - 1)); - } - }); - - factorial(5) - //=> 120 +{:lang="js"} +~~~~~~~~ +var factorial = Y(function (fac) { + return function (n) { + return (n == 0 ? 1 : n * fac(n - 1)); + } +}); + +factorial(5) + //=> 120 +~~~~~~~~ Why? It enables you to make recursive functions without needing to bind a function to a name in an environment. This has little practical utility in JavaScript, but in combinatory logic it's essential: With fixed-point combinators it's possible to compute everything computable without binding names. @@ -33,32 +39,38 @@ There are many explanations of the Y Combinator's mechanism on the internet, but Work things out for yourself. And once you've grokked that recipe, this recipe is for a Y Combinator that is a little more idiomatic. Work it out too: - function Y (fn) { - var f = function (f) { - return function () { - return fn.apply(f, arguments) - } - }; - - return ((function (x) { - return f(function (v) { - return x(x)(v); - }); - })(function (x) { - return f(function (v) { - return x(x)(v); - }); - })); +{:lang="js"} +~~~~~~~~ +function Y (fn) { + var f = function (f) { + return function () { + return fn.apply(f, arguments) } + }; + + return ((function (x) { + return f(function (v) { + return x(x)(v); + }); + })(function (x) { + return f(function (v) { + return x(x)(v); + }); + })); +} +~~~~~~~~ You use this version like this: - var factorial = Y(function (n) { - return (n == 0 ? 1 : n * this(n - 1)); - }); - - factorial(5) +{:lang="js"} +~~~~~~~~ +var factorial = Y(function (n) { + return (n == 0 ? 1 : n * this(n - 1)); +}); + +factorial(5) +~~~~~~~~ There are certain cases involving nested recursive functions it cannot handle due to the ambiguity of `this`, and obviously it is useless as a method combination, but it is an interesting alternative to the `let` pattern. -[y]: https://en.wikipedia.org/wiki/Fixed-point_combinator#Example_in_JavaScript "Call-by-value fixed-point combinator in JavaScript" \ No newline at end of file +[y]: https://en.wikipedia.org/wiki/Fixed-point_combinator#Example_in_JavaScript "Call-by-value fixed-point combinator in JavaScript" diff --git a/manuscript/markdown/References and Rebinding/recursion.md b/manuscript/markdown/References and Rebinding/recursion.md index b4bd5e4..9157f87 100644 --- a/manuscript/markdown/References and Rebinding/recursion.md +++ b/manuscript/markdown/References and Rebinding/recursion.md @@ -2,41 +2,50 @@ We've talked about binding values in environments, and now we're talking about rebinding values and mutating values. Let's take a small digression. As we've seen, in JavaScript functions are values. So you can bind a function just like binding a string, number or array. Here's a function that tells us whether a (small and positive) number is even: - var even = function (num) { - return (num === 0) || !(even(num - 1)) - } +{:lang="js"} +~~~~~~~~ +var even = function (num) { + return (num === 0) || !(even(num - 1)) +} - even(0) - //=> true +even(0) + //=> true - even(1) - //=> false +even(1) + //=> false - even(42) - //=> true +even(42) + //=> true +~~~~~~~~ You can alias a function value: - var divisibleByTwo = even; +{:lang="js"} +~~~~~~~~ +var divisibleByTwo = even; - divisibleByTwo(0) - //=> true +divisibleByTwo(0) + //=> true - divisibleByTwo(1) - //=> false +divisibleByTwo(1) + //=> false - divisibleByTwo(42) - //=> true +divisibleByTwo(42) + //=> true +~~~~~~~~ What happens when we redefine a recursive function like `even`? Does `divisibleByTwo` still work? Let's try aliasing it and reassigning it: - even = void 0; +{:lang="js"} +~~~~~~~~ +even = void 0; - divisibleByTwo(0) - //=> true +divisibleByTwo(0) + //=> true - divisibleByTwo(1) - //=> TypeError +divisibleByTwo(1) + //=> TypeError +~~~~~~~~ What happened? Well, our new `divisibleByTwo` function wasn't really a self-contained value. When we looked at functions, we talked about "pure" functions that only access their arguments and we looked at "closures" that have free variables. Recursive functions defined like this are closures, not pure functions, because when they "call themselves," what they actually do is look themselves up by name in their enclosing environment. Thus, they depend upon a specific value (themselves) being bound in their enclosing environment. Reassign to that variable (or rebind the name, same thing), and you break their functionality. @@ -44,60 +53,69 @@ What happened? Well, our new `divisibleByTwo` function wasn't really a self-cont You recall that in [Naming Functions](#named-function-expressions), we saw that when you create a named function expression, you bind the name of the function within its body but not the environment of the function expression, meaning you can write: - var even = function myself (num) { - return (num === 0) || !(myself(num - 1)) - } +{:lang="js"} +~~~~~~~~ +var even = function myself (num) { + return (num === 0) || !(myself(num - 1)) +} - var divisibleByTwo = even; - even = void 0; +var divisibleByTwo = even; +even = void 0; - divisibleByTwo(0) - //=> true +divisibleByTwo(0) + //=> true - divisibleByTwo(1) - //=> false +divisibleByTwo(1) + //=> false - divisibleByTwo(42) - //=> true +divisibleByTwo(42) + //=> true +~~~~~~~~ This is different, because the function doesn't refer to a name bound in its enclosing environment, it refers to a name bound in its own body. It is now a pure function. In fact, you can even bind it to the exact same name in its enclosing environment and it will still work: - var even = function even (num) { - return (num === 0) || !(even(num - 1)) - } +{:lang="js"} +~~~~~~~~ +var even = function even (num) { + return (num === 0) || !(even(num - 1)) +} - var divisibleByTwo = even; - even = void 0; +var divisibleByTwo = even; +even = void 0; - divisibleByTwo(0) - //=> true +divisibleByTwo(0) + //=> true - divisibleByTwo(1) - //=> false +divisibleByTwo(1) + //=> false - divisibleByTwo(42) - //=> true +divisibleByTwo(42) + //=> true +~~~~~~~~ The `even` inside the function refers to the name bound within the function by the named function expression. It may have the same name as the `even` bound in the enclosing environment, but they are two different bindings in two different environments. Thus, rebinding the name in the enclosing environment does not break the function. You may ask, what if we rebind `even` inside of itself. Now will it break? - var even = function even (num) { - even = void 0; - return (num === 0) || !(even(num - 1)) - } +{:lang="js"} +~~~~~~~~ +var even = function even (num) { + even = void 0; + return (num === 0) || !(even(num - 1)) +} - var divisibleByTwo = even; - even = void 0; +var divisibleByTwo = even; +even = void 0; - divisibleByTwo(0) - //=> true +divisibleByTwo(0) + //=> true - divisibleByTwo(1) - //=> false +divisibleByTwo(1) + //=> false - divisibleByTwo(42) - //=> true +divisibleByTwo(42) + //=> true +~~~~~~~~ Strangely, *no it doesn't*. The name bound by a named function expression is read-only. Why do we say strangely? Because other quasi-declarations like function declarations do *not* behave like this. @@ -107,42 +125,50 @@ So, when we want to make a recursive function, the safest practice is to use a n Named function expressions have limits. Here's one such limit: You can do simple recursion, but not mutual recursion. For example: - var even = function even (num) { return (num === 0) || odd( num - 1) }; - var odd = function odd (num) { return (num > 0) && even(num - 1) }; +{:lang="js"} +~~~~~~~~ +var even = function even (num) { return (num === 0) || odd( num - 1) }; +var odd = function odd (num) { return (num > 0) && even(num - 1) }; - odd = 'unusual'; +odd = 'unusual'; - even(0) - //=> true +even(0) + //=> true - even(1) - //=> TypeError +even(1) + //=> TypeError +~~~~~~~~ Using named function expressions doesn't help us, because `even` and `odd` need to be bound in an environment accessible to each other, not just to themselves. You either have to avoid rebinding the names of these functions, or use a closure to build a [module](#modules): - var operations = (function () { - var even = function (num) { return (num === 0) || odd( num - 1) }; - var odd = function (num) { return (num > 0) && even(num - 1) }; - return { - even: even, - odd: odd - } - })(), - even = operations.even, - odd = operations.odd; +{:lang="js"} +~~~~~~~~ +var operations = (function () { + var even = function (num) { return (num === 0) || odd( num - 1) }; + var odd = function (num) { return (num > 0) && even(num - 1) }; + return { + even: even, + odd: odd + } + })(), + even = operations.even, + odd = operations.odd; +~~~~~~~~ Now you can rebind one without breaking the other, because the names outside of the closure have no effect on the bindings inside the closure: - odd = 'unusual; +{:lang="js"} +~~~~~~~~ +odd = 'unusual; - even(0) - //=> true +even(0) + //=> true - even(1) - //=> false - - even(42) - //=> true +even(1) + //=> false +even(42) + //=> true +~~~~~~~~ T> As has often been noted, refactoring *to* a pattern is more important than designing *with* a pattern. So don't rush off to write all your recursive functions this way, but familiarize yourself with the technique so that if and when you run into a subtle bug, you can recognize the problem and know how to fix it. diff --git a/manuscript/markdown/References and Rebinding/references.md b/manuscript/markdown/References and Rebinding/references.md index 3421d31..8fbe117 100644 --- a/manuscript/markdown/References and Rebinding/references.md +++ b/manuscript/markdown/References and Rebinding/references.md @@ -2,21 +2,27 @@ Consider this code: - var x = 'June 14, 1962', - y = x; - - x === y - //=> true +{:lang="js"} +~~~~~~~~ +var x = 'June 14, 1962', + y = x; + +x === y + //=> true +~~~~~~~~ This makes obvious sense, because we know that strings are a value type, so no matter what expression you use to derive the value 'June 14, 1962', you are going to get a string with the exact same identity. But what about this code? - var x = [2012, 6, 14], - y = x; - - x === y - //=> true +{:lang="js"} +~~~~~~~~ +var x = [2012, 6, 14], + y = x; + +x === y + //=> true +~~~~~~~~ Also true, even though we know that every time we evaluate an expression such as `[2012, 6, 14]`, we get a new array with a new identity. So what is happening in our environments? @@ -28,11 +34,14 @@ What this means is that when we write something like `y = x`, the name `x` is lo The same thing happens with binding a variable through a more conventional means of applying a function to arguments: - var x = [2012, 6, 14]; - - (function (y) { - return x === y - })(x) - //=> true +{:lang="js"} +~~~~~~~~ +var x = [2012, 6, 14]; + +(function (y) { + return x === y +})(x) + //=> true +~~~~~~~~ -`x` and `y` both end up bound to the exact same array, not two different arrays that look the same to our eyes. \ No newline at end of file +`x` and `y` both end up bound to the exact same array, not two different arrays that look the same to our eyes. diff --git a/manuscript/markdown/References and Rebinding/var.md b/manuscript/markdown/References and Rebinding/var.md index 73f6a7d..c41f2e7 100644 --- a/manuscript/markdown/References and Rebinding/var.md +++ b/manuscript/markdown/References and Rebinding/var.md @@ -10,70 +10,85 @@ Let's look at a few ways to use it *improperly*. JavaScript's `var` keyword is scoped to the function enclosing it. This makes sense, because bindings are made in environments, and the environments are associated with function calls. So if you write: - function foo (bar) { - var baz = bar * 2; - - if (bar > 1) { - var blitz = baz - 100; - - // ... - } - } - +{:lang="js"} +~~~~~~~~ +function foo (bar) { + var baz = bar * 2; + + if (bar > 1) { + var blitz = baz - 100; + + // ... + } +} +~~~~~~~~ + The name `blitz` is actually scoped to the function `foo`, not to the block of code in the consequent of an `if` statement. There are roughly two schools of thought. One line of reasoning goes like this: Since `blitz` is scoped to the function `foo`, you should write the code like this: - function foo (bar) { - var baz = bar * 2, - blitz; - - if (bar > 1) { - blitz = baz - 100; - - // ... - } - } +{:lang="js"} +~~~~~~~~ +function foo (bar) { + var baz = bar * 2, + blitz; + + if (bar > 1) { + blitz = baz - 100; + + // ... + } +} +~~~~~~~~ We've separated the "declaration" from the "assignment," and we've made it clear that `blitz` is scoped to the entire function. The other school of thought is that programmers are responsible for understanding how the tools work, and even if you write it the first way, other programmers reading the code ought to know how it works. So here's a question: Are both ways of writing the code equivalent? Let's set up a test case that would tell them apart. We'll try some aliasing: - var questionable = 'outer'; - - (function () { - alert(questionable); - - if (true) { - var questionable = 'inner'; - alert(questionable) - } - })() - +{:lang="js"} +~~~~~~~~ +var questionable = 'outer'; + +(function () { + alert(questionable); + + if (true) { + var questionable = 'inner'; + alert(questionable) + } +})() +~~~~~~~~ + What will this code do if we type it into a browser console? One theory is that it will alert `outer` and then `inner`, because when it evaluates the first alert, `questionable` hasn't been bound in the function's environment yet, so it will be looked up in the enclosing environment. Then an alias is bound, shadowing the outer binding, and it will alert `inner`. This theory is wrong! It actually alerts `undefined` and then `inner`. Even though we wrote the `var` statement later in the code, JavaScript acts as if we'd declared it at the top of the function. This is true even if we never execute the `var` statement: - var questionable = 'outer'; - - (function () { - return questionable; - - var questionable = 'inner' - })() - - //=> undefined - +{:lang="js"} +~~~~~~~~ +var questionable = 'outer'; + +(function () { + return questionable; + + var questionable = 'inner' +})() + + //=> undefined +~~~~~~~~ + So yes, both ways of writing the code work the same way, but only one represents the way it works directly and obviously. For this reason, we put the `var` declarations at the top of every function, always. ### for pete's sake JavaScript provides a `for` loop for your iterating pleasure and convenience. It looks a lot like the `for` loop in C: - var sum = 0; - for (var i = 1; i <= 100; i++) { - sum = sum + i - } - sum - #=> 5050 +{:lang="js"} +~~~~~~~~ +var sum = 0; +for (var i = 1; i <= 100; i++) { + sum = sum + i +} +sum + #=> 5050 +~~~~~~~~ Hopefully, you can think of a faster way to calculate this sum.[^gauss] And perhaps you have noticed that `var i = 1` is tucked away instead of being at the top as we prefer. But is this ever a problem? @@ -81,94 +96,115 @@ Hopefully, you can think of a faster way to calculate this sum.[^gauss] And perh Yes. Consider this variation: - var introductions = [], - names = ['Karl', 'Friedrich', 'Gauss']; - - for (var i = 0; i < 3; i++) { - introductions[i] = "Hello, my name is " + names[i] - } - introductions - //=> [ 'Hello, my name is Karl', - // 'Hello, my name is Friedrich', - // 'Hello, my name is Gauss' ] +{:lang="js"} +~~~~~~~~ +var introductions = [], + names = ['Karl', 'Friedrich', 'Gauss']; + +for (var i = 0; i < 3; i++) { + introductions[i] = "Hello, my name is " + names[i] +} +introductions + //=> [ 'Hello, my name is Karl', + // 'Hello, my name is Friedrich', + // 'Hello, my name is Gauss' ] +~~~~~~~~ So far, so good. Hey, remember that functions in JavaScript are values? Let's get fancy! - var introductions = [], - names = ['Karl', 'Friedrich', 'Gauss']; - - for (var i = 0; i < 3; i++) { - introductions[i] = function (soAndSo) { - return "Hello, " + soAndSo + ", my name is " + names[i] - } - } - introductions - //=> [ [Function], - // [Function], - // [Function] ] - +{:lang="js"} +~~~~~~~~ +var introductions = [], + names = ['Karl', 'Friedrich', 'Gauss']; + +for (var i = 0; i < 3; i++) { + introductions[i] = function (soAndSo) { + return "Hello, " + soAndSo + ", my name is " + names[i] + } +} +introductions + //=> [ [Function], + // [Function], + // [Function] ] +~~~~~~~~ + So far, so good. Let's try one of our functions: - introductions[1]('Raganwald') - //=> 'Hello, Raganwald, my name is undefined' - +{:lang="js"} +~~~~~~~~ +introductions[1]('Raganwald') + //=> 'Hello, Raganwald, my name is undefined' +~~~~~~~~ + What went wrong? Why didn't it give us 'Hello, Raganwald, my name is Friedrich'? The answer is that pesky `var i`. Remember that `i` is bound in the surrounding environment, so it's as if we wrote: - var introductions = [], - names = ['Karl', 'Friedrich', 'Gauss'], - i; - - for (i = 0; i < 3; i++) { - introductions[i] = function (soAndSo) { - return "Hello, " + soAndSo + ", my name is " + names[i] - } - } - introductions - -Now, at the time we created each function, `i` had a sensible value, like `0`, `1`, or `2`. But at the time we *call* one of the functions, `i` has the value `3`, which is why the loop terminated. So when the function is called, JavaScript looks `i` up in its enclosing environment (its closure, obviously), and gets the value `3`. That's not what we want at all. +{:lang="js"} +~~~~~~~~ +var introductions = [], + names = ['Karl', 'Friedrich', 'Gauss'], + i; + +for (i = 0; i < 3; i++) { + introductions[i] = function (soAndSo) { + return "Hello, " + soAndSo + ", my name is " + names[i] + } +} +introductions +~~~~~~~~ + +Now, at the time we created each function, `i` had a sensible value, like `0`, `1`, or `2`. But at the time we *call* one of the functions, `i` has the value `3`, which is why the loop terminated. So when the function is called, JavaScript looks `i` up in its enclosing environment (its closure, obviously), and gets the value `3`. That's not what we want at all. Here's how to fix it, once again with `let` as our guide: - var introductions = [], - names = ['Karl', 'Friedrich', 'Gauss']; - - for (var i = 0; i < 3; i++) { - (function (i) { - introductions[i] = function (soAndSo) { - return "Hello, " + soAndSo + ", my name is " + names[i] - } - })(i) +{:lang="js"} +~~~~~~~~ +var introductions = [], + names = ['Karl', 'Friedrich', 'Gauss']; + +for (var i = 0; i < 3; i++) { + (function (i) { + introductions[i] = function (soAndSo) { + return "Hello, " + soAndSo + ", my name is " + names[i] } - introductions[1]('Raganwald') - //=> 'Hello, Raganwald, my name is Friedrich' - + })(i) +} +introductions[1]('Raganwald') + //=> 'Hello, Raganwald, my name is Friedrich' +~~~~~~~~ + That works. What did we do? Well, we created a new function and called it immediately, and we deliberately shadowed `i` by passing it as an argument to our function, which had an argument of exactly the same name. If you dislike shadowing, this alternative also works: - var introductions = [], - names = ['Karl', 'Friedrich', 'Gauss']; - - for (var i = 0; i < 3; i++) { - (function () { - var ii = i; - introductions[ii] = function (soAndSo) { - return "Hello, " + soAndSo + ", my name is " + names[ii] - } - })() +{:lang="js"} +~~~~~~~~ +var introductions = [], + names = ['Karl', 'Friedrich', 'Gauss']; + +for (var i = 0; i < 3; i++) { + (function () { + var ii = i; + introductions[ii] = function (soAndSo) { + return "Hello, " + soAndSo + ", my name is " + names[ii] } - introductions[1]('Raganwald') - //=> 'Hello, Raganwald, my name is Friedrich' - + })() +} +introductions[1]('Raganwald') + //=> 'Hello, Raganwald, my name is Friedrich' +~~~~~~~~ + Now we're creating a new inner variable, `ii` and binding it to the value of `i`. The shadowing code seems simpler and less error-prone to us, but both work. ### nope, nope, nope, nope, nope The final caution about `var` concerns what happens if you omit to declare a variable with var, boldly writing something like: - fizzBuzz = function () { - // lots of interesting code elided - // for the sake of hiring managers - } - +{:lang="js"} +~~~~~~~~ +fizzBuzz = function () { + // lots of interesting code elided + // for the sake of hiring managers +} +~~~~~~~~ + So where is the name `fizzBuzz` bound? The answer is that if there is no enclosing `var` declaration for `fizzBuzz`, the name is bound in the *global* environment. And by global, we mean global. It is visible to every separate compilation unit. All of your npm modules. Every JavaScript snippet in a web page. Every included file. This is almost never what you want. And when you do want it, JavaScript provides alternatives such as binding to `window.fizzBuzz` in a browser, or `this.fizzBuzz` in node. In short, eschew undeclared variables. Force yourself to make a habit of using `var` all of the time, and explicitly binding variables to the `window` or `this` objects when you truly want global visibility. diff --git a/manuscript/markdown/Sequence/callbacks.md b/manuscript/markdown/Sequence/callbacks.md index 58055f0..aedccfe 100644 --- a/manuscript/markdown/Sequence/callbacks.md +++ b/manuscript/markdown/Sequence/callbacks.md @@ -4,39 +4,45 @@ Let's consider code that is written using the *callback* pattern.[^CPS] Consider [^CPS]: This is also known as [Continuation-Passing-Style](https://en.wikipedia.org/wiki/Continuation-passing_style). - function times2 (n) { - return n * 2; - } - - function plus1 (n) { - return n + 1; - } - - var four = times2(2); - var five = plus1(four); - console.log('->', five); - //=> prints "-> 5" on the console - +{:lang="js"} +~~~~~~~~ +function times2 (n) { + return n * 2; +} + +function plus1 (n) { + return n + 1; +} + +var four = times2(2); +var five = plus1(four); +console.log('->', five); +//=> prints "-> 5" on the console +~~~~~~~~ + In this code, each function does exactly one thing: It returns the calculated value. The code that calls each function is in control of what to do with the value. The calling code says "give me the value: I'll take it from there." That makes a certain amount of sense, because it's the calling code that knows what it wants done with the value, so why doesn't the calling function simply do what it wants done? There is another reasonable way to arrange things. If you were asking someone how much it would cost to buy a new tablet, you might want the tablet purchased as well. So you would tell them to figure out the price and then buy it. In other words, the person making the request also dictates what is to be done with the result. Using this convention, our code above would look like this: - function times2 (n, callback) { - callback(n * 2); - } - - function plus1 (n, callback) { - callback(n + 1); - } - - times2(2, function (four) { - plus1(four, function (five) { - console.log('->', five); - }) - }); - //=> prints "-> 5" on the console +{:lang="js"} +~~~~~~~~ +function times2 (n, callback) { + callback(n * 2); +} + +function plus1 (n, callback) { + callback(n + 1); +} + +times2(2, function (four) { + plus1(four, function (five) { + console.log('->', five); + }) +}); +//=> prints "-> 5" on the console +~~~~~~~~ Now our code is telling `times2`, "Take the result and execute this function with it" rather than "Give me the result." And what it wants done is to pass the result to `plus1`, along with instructions for what `plus1` is supposed to do with its own result. @@ -48,39 +54,51 @@ Our goal is to write something like `pipelineWithCallbacks(a, b, ..., z)`. This Here is a function that composes two functions taking a value and a callback: - function compose2 (a, b) { - return function (value, callback) { - b(value, function (resultFromB) { - a(resultFromB, callback); - }); - }; - } - +{:lang="js"} +~~~~~~~~ +function compose2 (a, b) { + return function (value, callback) { + b(value, function (resultFromB) { + a(resultFromB, callback); + }); + }; +} +~~~~~~~~ + We can use this as the basis for pipelining any number of functions: - var pipelineFunctionsWithCallbacks = variadic( function (fns) { - return - }); +{:lang="js"} +~~~~~~~~ +var pipelineFunctionsWithCallbacks = variadic( function (fns) { + return +}); +~~~~~~~~ And now we can write: - var callback = { - pipeline: function (fnA, fnB) { - return function (value, callback) { - fnA(value, function (resultFromA) { - fnB(resultFromA, callback); - }); - }; - } - } +{:lang="js"} +~~~~~~~~ +var callback = { + pipeline: function (fnA, fnB) { + return function (value, callback) { + fnA(value, function (resultFromA) { + fnB(resultFromA, callback); + }); + }; + } +} + +sequence2(callback.pipeline, times2, plus1)(4, console.log) + //=> prints "9" +~~~~~~~~ - sequence2(callback.pipeline, times2, plus1)(4, console.log) - //=> prints "9" - We now have a much more complex set of semantics implemented. As you may know, callbacks are used in some environments to handle asynchronicity. So we could use the above code to write things like: - sequence2(pipeline2withCallback, getUserRecord, updateUserRecord)(userId, displayResult); - +{:lang="js"} +~~~~~~~~ +sequence2(pipeline2withCallback, getUserRecord, updateUserRecord)(userId, displayResult); +~~~~~~~~ + And the calls to `getUserRecord`/`updateUserRecord` could invoke a remote request asynchronously. ### unifying our algorithms @@ -89,41 +107,53 @@ Now in all fairness, this is a very interesting result but it is onerous to thin Let's review: We have two `sequence` functions: - var sequence = variadic( function (chain, fns) { - return function sequence (seed) { - return reduce(fns, chain, seed); - }; - }); - +{:lang="js"} +~~~~~~~~ +var sequence = variadic( function (chain, fns) { + return function sequence (seed) { + return reduce(fns, chain, seed); + }; +}); +~~~~~~~~ + And: - var sequence2 = variadic( function (chain, fns) { - return variadic( function (args) { - return reduce(fns.slice(1), chain, fns[0]).apply(this, args); - }); - }); - +{:lang="js"} +~~~~~~~~ +var sequence2 = variadic( function (chain, fns) { + return variadic( function (args) { + return reduce(fns.slice(1), chain, fns[0]).apply(this, args); + }); +}); +~~~~~~~~ + The difference between them is that the first version "reduces" the value provided as a seed, while the second one "reduces" the list of functions into a new function and then applies the arguments provided to that function. We can unify (or "DRY") these two up. The first thing we'll need to do is make the overall algorithms have the same shape or structure. We'll do that by converting the first version to reduce the functions rather than reduce the seed value. We are given a chain function such as: - var andand = { - chain: function (value, fn) { - return value ? fn(value) : value; - } - }; - +{:lang="js"} +~~~~~~~~ +var andand = { + chain: function (value, fn) { + return value ? fn(value) : value; + } +}; +~~~~~~~~ + Nw all we want to do is return a function that pipelines two other functions while incorporating the `chain` logic: - var andand = { - chain: function (value, fn) { - return value ? fn(value) : value; - }, - pipeline: function (fnA, fnB) { - return function (value) { - - } - } - }; \ No newline at end of file +{:lang="js"} +~~~~~~~~ +var andand = { + chain: function (value, fn) { + return value ? fn(value) : value; + }, + pipeline: function (fnA, fnB) { + return function (value) { + + } + } +}; +~~~~~~~~ diff --git a/manuscript/markdown/Sequence/compose-and-pipeline.md b/manuscript/markdown/Sequence/compose-and-pipeline.md index 9ff7cbf..b1464d2 100644 --- a/manuscript/markdown/Sequence/compose-and-pipeline.md +++ b/manuscript/markdown/Sequence/compose-and-pipeline.md @@ -2,55 +2,73 @@ In [Combinators and Function Decorators](#combinators), we saw the function `compose`: - function compose (a, b) { - return function (c) { - return a(b(c)) - } - } +{:lang="js"} +~~~~~~~~ +function compose (a, b) { + return function (c) { + return a(b(c)) + } +} +~~~~~~~~ As we saw before, given: - function addOne (number) { - return number + 1 - } - - function double (number) { - return number * 2 - } +{:lang="js"} +~~~~~~~~ +function addOne (number) { + return number + 1 +} + +function double (number) { + return number * 2 +} +~~~~~~~~ Instead of: - function doubleOfAddOne (number) { - return double(addOne(number)) - } - +{:lang="js"} +~~~~~~~~ +function doubleOfAddOne (number) { + return double(addOne(number)) +} +~~~~~~~~ + We could write: - var doubleOfAddOne = compose(double, addOne); - +{:lang="js"} +~~~~~~~~ +var doubleOfAddOne = compose(double, addOne); +~~~~~~~~ + ### the semantics of compose With `compose`, we're usually making a new function. Although it works perfectly well, we don't need to write things like `compose(double, addOne)(3)` inline to get the result `8`. It's easier and clearer to write `double(addOne(3))`. On the other hand, when working with something like method decorators, it can help to write: - var setter = compose(fluent, maybe); - - // ... - - SomeClass.prototype.setUser = setter(function (user) { - this.user = user; - }); - - SomeClass.prototype.setPrivileges = setter(function (privileges) { - this.privileges = privileges; - }); - +{:lang="js"} +~~~~~~~~ +var setter = compose(fluent, maybe); + +// ... + +SomeClass.prototype.setUser = setter(function (user) { + this.user = user; +}); + +SomeClass.prototype.setPrivileges = setter(function (privileges) { + this.privileges = privileges; +}); +~~~~~~~~ + This makes it clear that `setter` adds the behaviour of both `fluent` and `maybe` to each method it decorates, and it's simpler to read `var setter = compose(fluent, maybe);` than: - function setter (fn) { - return fluent(maybe(fn)); - } +{:lang="js"} +~~~~~~~~ +function setter (fn) { + return fluent(maybe(fn)); +} +~~~~~~~~ The take-away is that `compose` is helpful when we are defining a new function that combines the effects of existing functions. @@ -60,10 +78,13 @@ The take-away is that `compose` is helpful when we are defining a new function t Sometimes it makes more sense to compose functions in data flow order, as in "The value flows through a and then through b." For this, we can use the `pipeline` function: - var pipeline = flip(compose); - - var setter = pipeline(addOne, double); - +{:lang="js"} +~~~~~~~~ +var pipeline = flip(compose); + +var setter = pipeline(addOne, double); +~~~~~~~~ + Comparing `pipeline` to `compose`, pipeline says "add one to the number and then double it." Compose says, "double the result of adding one to the number." Both do the same job, but communicate their intention in opposite ways. ![Saltspring Island Roasting Facility](images/saltspring/rollers.jpg) diff --git a/manuscript/markdown/Sequence/semantics.md b/manuscript/markdown/Sequence/semantics.md index 4166c74..5953f03 100644 --- a/manuscript/markdown/Sequence/semantics.md +++ b/manuscript/markdown/Sequence/semantics.md @@ -6,10 +6,13 @@ In JavaScript, statements separated by semicolons have well-defined semantics. W We are going to spend the entire chapter discussing the semantics of code that looks like this: - var temp1 = doSomething(seed); - var temp2 = andDoSomethingElseWith(temp1); - var temp3 = nowDoSomethingWith(temp2); - var result = doTheLastThingWith(temp3); +{:lang="js"} +~~~~~~~~ +var temp1 = doSomething(seed); +var temp2 = andDoSomethingElseWith(temp1); +var temp3 = nowDoSomethingWith(temp2); +var result = doTheLastThingWith(temp3); +~~~~~~~~ This code as written looks exactly like the `pipeline` function we saw in the previous section. A `seed` is put into the first function, and the results are pipelined through the functions until we get a result form the last function. @@ -26,35 +29,47 @@ But there is more subtlety involved. Like most other contemporary languages, `&& In other words, it's something like: - var foo_value = foo(); - - if (foo_value) { - return bar(); - } - else return foo_value; - +{:lang="js"} +~~~~~~~~ +var foo_value = foo(); + +if (foo_value) { + return bar(); +} +else return foo_value; +~~~~~~~~ + Note that `bar()` is only evaluated if `foo()` evaluates truthy. If `foo()` evaluates falsy, *the function `bar` is never called*. If you have a long chain, like this: - foo() && bar() && bash() && fizz() && buzz() && fib() && nachos() - +{:lang="js"} +~~~~~~~~ +foo() && bar() && bash() && fizz() && buzz() && fib() && nachos() +~~~~~~~~ + If `foo()` evaluates to falsy, none of teh remaining functions will be called. If `foo()` is truthy, `bar()` is then called. If `bar()` is then truthy, `bash()` is then called. If `bash()` is falsy, the remaining functions are not called. One very handy use for these semantics are when you have a series of "guards" for an action. So let's say we're about to save changes to some kind of record: - checkUserIsLoggedIn() && userHasPrivileges() && recordIsValid() && saveRecord() - +{:lang="js"} +~~~~~~~~ +checkUserIsLoggedIn() && userHasPrivileges() && recordIsValid() && saveRecord() +~~~~~~~~ + If `checkUserIsLoggedIn`, `userHasPrivileges`, or `recordIsValid` return falsy, the record will not be saved. ### || The `||` operator also has short-circuit semantics. Given `foo() || bar()`, if `foo()` returns something truthy, `bar` will *not* be called. It's somewhat equivalent to: - var foo_value = foo(); - - if (foo_value) { - return foo_value; - } - else return bar(); +{:lang="js"} +~~~~~~~~ +var foo_value = foo(); + +if (foo_value) { + return foo_value; +} +else return bar(); +~~~~~~~~ Once again we can chain several functions together, and the chain is aborted if any of the functions return truthy. This has several uses, one of which will be well-known to C programmers. @@ -62,8 +77,11 @@ Some functions return an *error value* if something goes wrong, and `0` if the f Ignoring for a moment the possibility of asynchronous behaviour, it might look like this: - openFile() || readFile() || closeFile() || writeCopy() || closeCopy() - +{:lang="js"} +~~~~~~~~ +openFile() || readFile() || closeFile() || writeCopy() || closeCopy() +~~~~~~~~ + Assuming these functions pass error codes back, if any of them returns a non-zero error code, the remaining functions will not be evaluated. ### what do && and || tell us? @@ -84,21 +102,27 @@ So how can we formalize this pattern? Let's start with something we know, `pipeline`. The implementation we gave was: `pipeline = flip(compose)`. True, but not very helpful. Let's write it out the long way: - var pipeline = variadic( function (fns) { - return function pipeline (seed) { - return reduce(fns, function chain (lastResult, fn) { return fn(lastResult); }, seed); - }; - }); +{:lang="js"} +~~~~~~~~ +var pipeline = variadic( function (fns) { + return function pipeline (seed) { + return reduce(fns, function chain (lastResult, fn) { return fn(lastResult); }, seed); + }; +}); +~~~~~~~~ The interesting thing here is the function within the `reduce` method: `function chain (value, fn) { return fn(value); }`. Notice we're naming it "chain" because that's what it does, it chains the functions together by taking the result of one and putting it into the next. Chain is the function responsible for invoking each of the functions being pipelined. Since that's what we're interested in, let's extract it as a parameter. Now that we're modifying `pipeline`, we'll also give it a new name: - var sequence = variadic( function (chain, fns) { - return function sequence (seed) { - return reduce(fns, chain, seed); - }; - }); +{:lang="js"} +~~~~~~~~ +var sequence = variadic( function (chain, fns) { + return function sequence (seed) { + return reduce(fns, chain, seed); + }; +}); +~~~~~~~~ Notice that if we wanted to, we could implement `pipeline` as `sequence(function chain (lastResult, fn) { return fn(lastResult); }, ... )`. This shows that our new function is a generalized implementation, with `pipeline` being a specific instance. @@ -106,34 +130,49 @@ Notice that if we wanted to, we could implement `pipeline` as `sequence(function We're not allowed to name variables `&&` or `||`, so we'll use words: - var andand = { - chain: function (value, fn) { - return value ? fn(value) : value; - } - }; - - var oror = { - chain: function (value, fn) { - return value ? value : fn(value); - } - }; +{:lang="js"} +~~~~~~~~ +var andand = { + chain: function (value, fn) { + return value ? fn(value) : value; + } +}; + +var oror = { + chain: function (value, fn) { + return value ? value : fn(value); + } +}; +~~~~~~~~ Now we can implement the semantics we discussed above. Instead of: - checkUserIsLoggedIn() && userHasPrivileges() && recordIsValid() && saveRecord() - +{:lang="js"} +~~~~~~~~ +checkUserIsLoggedIn() && userHasPrivileges() && recordIsValid() && saveRecord() +~~~~~~~~ + We write: - sequence(andand.chain, checkUserIsLoggedIn, userHasPrivileges, recordIsValid, saveRecord)() +{:lang="js"} +~~~~~~~~ +sequence(andand.chain, checkUserIsLoggedIn, userHasPrivileges, recordIsValid, saveRecord)() +~~~~~~~~ And instead of: - openFile() || readFile() || closeFile() || writeCopy() || closeCopy() - +{:lang="js"} +~~~~~~~~ +openFile() || readFile() || closeFile() || writeCopy() || closeCopy() +~~~~~~~~ + We write: - sequence(oror.chain, openFile, openFile, closeFile, writeCopy, closeCopy) - +{:lang="js"} +~~~~~~~~ +sequence(oror.chain, openFile, openFile, closeFile, writeCopy, closeCopy) +~~~~~~~~ + Being able to plug our chain function in lets us implement `&&` and `||` semantics in `sequence`. This raises two questions: 1. Is this *really* superior to writing things like `openFile() || readFile() || closeFile() ...`? @@ -141,4 +180,4 @@ Being able to plug our chain function in lets us implement `&&` and `||` semanti Answering the second question answers the first. `&&` and `||` semantics are very simple, so they are useful for understanding how passing in a `chain` parameter works. -In the next section, we'll look at a more substantial example of chaining functions with new semantics. We'll chain functions that take *callbacks* together. \ No newline at end of file +In the next section, we'll look at a more substantial example of chaining functions with new semantics. We'll chain functions that take *callbacks* together. diff --git a/manuscript/markdown/cheat-sheet.md b/manuscript/markdown/cheat-sheet.md index 7ea564c..6f396a5 100644 --- a/manuscript/markdown/cheat-sheet.md +++ b/manuscript/markdown/cheat-sheet.md @@ -6,74 +6,95 @@ In the recipes, you may see one or more of the following JavaScript constructs b Functions are applied with `()`. But they also have *methods* for applying them to arguments. `.call` and `.apply` are explained when we discuss [function contexts](#context), but here are some examples: - function plus (a, b) { - return a + b - } - - plus(2, 3) - //=> 5 - - plus.call(this, 2, 3) - //=> 5 - - plus.apply(this, [2, 3]) - //=> 5 +{:lang="js"} +~~~~~~~~ +function plus (a, b) { + return a + b +} + +plus(2, 3) + //=> 5 + +plus.call(this, 2, 3) + //=> 5 + +plus.apply(this, [2, 3]) + //=> 5 +~~~~~~~~ ### slice Arrays have a `.slice` method. The function can always be found at `Array.prototype.slice`. It works like this: - [1, 2, 3, 4, 5].slice(0) - //=> [1, 2, 3, 4, 5] - - [1, 2, 3, 4, 5].slice(1) - //=> [2, 3, 4, 5] - - [1, 2, 3, 4, 5].slice(1, 4) - //=> [2, 3, 4] +{:lang="js"} +~~~~~~~~ +[1, 2, 3, 4, 5].slice(0) + //=> [1, 2, 3, 4, 5] + +[1, 2, 3, 4, 5].slice(1) + //=> [2, 3, 4, 5] + +[1, 2, 3, 4, 5].slice(1, 4) + //=> [2, 3, 4] +~~~~~~~~ Note that `slice` always creates a new array, so `.slice(0)` makes a copy of an array. The [arguments](#arguments-again) pseudo-variable is not an array, but you can use `.slice` with it like this to get an array of all or some of the arguments: - Array.prototype.slice.call(arguments, 0) - //=> returns the arguments in an array. - - function butFirst () { - return Array.prototype.slice.call(arguments, 1) - } - - butFirst('a', 'b', 'c', 'd') - //=> [ 'b', 'c', 'd' ] - +{:lang="js"} +~~~~~~~~ +Array.prototype.slice.call(arguments, 0) + //=> returns the arguments in an array. + +function butFirst () { + return Array.prototype.slice.call(arguments, 1) +} + +butFirst('a', 'b', 'c', 'd') + //=> [ 'b', 'c', 'd' ] +~~~~~~~~ + For simplicity and as a small speed improvement, `slice` is usually bound to a local variable: - var __slice = Array.prototype.slice; - - function butFirst () { - return __slice.call(arguments, 1) - } - +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function butFirst () { + return __slice.call(arguments, 1) +} +~~~~~~~~ + Or even: - var __slice = Array.prototype.slice; - - function slice (list, from, to) { - return __slice.call(list, from, to) - } - - function butFirst () { - return slice(arguments, 1) - } - +{:lang="js"} +~~~~~~~~ +var __slice = Array.prototype.slice; + +function slice (list, from, to) { + return __slice.call(list, from, to) +} + +function butFirst () { + return slice(arguments, 1) +} +~~~~~~~~ + ### concat Arrays have another useful method, `.concat`. Concat returns an array created by concatenating the receiver with its argument: - [1, 2, 3].concat([2, 1]) - //=> [1, 2, 3, 2, 1] - +{:lang="js"} +~~~~~~~~ +[1, 2, 3].concat([2, 1]) + //=> [1, 2, 3, 2, 1] +~~~~~~~~ + ### function lengths Functions have a `.length` property that counts the number of arguments declared: - function (a, b, c) { return a + b + c }.length - //=> 3 \ No newline at end of file +{:lang="js"} +~~~~~~~~ +function (a, b, c) { return a + b + c }.length + //=> 3 +~~~~~~~~ diff --git a/manuscript/markdown/prelude.md b/manuscript/markdown/prelude.md index 9f193d1..6122ace 100644 --- a/manuscript/markdown/prelude.md +++ b/manuscript/markdown/prelude.md @@ -15,14 +15,20 @@ All values are expressions. Say you hand the barista a café Cubano. Yup, you ha Let's try this with something the computer understands easily: - 42 +{:lang="js"} +~~~~~~~~ +42 +~~~~~~~~ Is this an expression? A value? Neither? Or both? The answer is, this is both an expression *and* a value.[^representation] The way you can tell that it's both is very easy: When you type it into JavaScript, you get the same thing back, just like our café Cubano: - 42 - //=> 42 +{:lang="js"} +~~~~~~~~ +42 + //=> 42 +~~~~~~~~ [^representation]: Technically, it's a *representation* of a value using Base10 notation, but we needn't worry about that in this book. You and I both understand that this means "42," and so does the computer. @@ -36,8 +42,11 @@ Now the barista gives us back an espresso. And if we hand over the espresso, we Let's try this as well with something else the computer understands easily: - "JavaScript" + " " + "Allonge" - //=> "JavaScript Allonge" +{:lang="js"} +~~~~~~~~ +"JavaScript" + " " + "Allonge" + //=> "JavaScript Allonge" +~~~~~~~~ Now we see that "strings" are values, and you can make an expression out of strings and an operator `+`. Since strings are values, they are also expressions by themselves. But strings with operators are not values, they are expressions. Now we know what was missing with our "coffee grounds plus hot water" example. The coffee grounds were a value, the boiling hot water was a value, and the "plus" operator between them made the whole thing an expression that was not a value. @@ -45,32 +54,41 @@ Now we see that "strings" are values, and you can make an expression out of stri In JavaScript, we test whether two values are identical with the `===` operator, and whether they are not identical with the `!==` operator: - 2 === 2 - //=> true - - 'hello' !== 'goodbye' - //=> true - +{:lang="js"} +~~~~~~~~ +2 === 2 + //=> true + +'hello' !== 'goodbye' + //=> true +~~~~~~~~ + How does `===` work, exactly? Imagine that you're shown a cup of coffee. And then you're shown another cup of coffee. Are the two cups "identical?" In JavaScript, there are four possibilities: First, sometimes, the cups are of different kinds. One is a demitasse, the other a mug. This corresponds to comparing two things in JavaScript that have different *types*. For example, the string `"2"` is not the same thing as the number `2`. Strings and numbers are different types, so strings and numbers are never identical: - 2 === '2' - //=> false - - true !== 'true' - //=> true +{:lang="js"} +~~~~~~~~ +2 === '2' + //=> false + +true !== 'true' + //=> true +~~~~~~~~ Second, sometimes, the cups are of the same type--perhaps two espresso cups--but they have different contents. One holds a single, one a double. This corresponds to comparing two JavaScript values that have the same type but different "content." For example, the number `5` is not the same thing as the number `2`. - true === false - //=> false - - 2 !== 5 - //=> true - - 'two' === 'five' - //=> false +{:lang="js"} +~~~~~~~~ +true === false + //=> false + +2 !== 5 + //=> true + +'two' === 'five' + //=> false +~~~~~~~~ What if the cups are of the same type *and* the contents are the same? Well, JavaScript's third and fourth possibilities cover that. @@ -78,12 +96,15 @@ What if the cups are of the same type *and* the contents are the same? Well, Jav Third, some types of cups have no distinguishing marks on them. If they are the same kind of cup, and they hold the same contents, we have no way to tell the difference between them. This is the case with the strings, numbers, and booleans we have seen so far. - 2 + 2 === 4 - //=> true - - (2 + 2 === 4) === (2 !== 5) - //=> true - +{:lang="js"} +~~~~~~~~ +2 + 2 === 4 + //=> true + +(2 + 2 === 4) === (2 !== 5) + //=> true +~~~~~~~~ + Note well what is happening with these examples: Even when we obtain a string, number, or boolean as the result of evaluating an expression, it is identical to another value of the same type with the same "content." Strings, numbers, and booleans are examples of what JavaScript calls "value" or "primitive" types. We'll use both terms interchangeably. We haven't encountered the fourth possibility yet. Stretching the metaphor somewhat, some types of cups have a serial number on the bottom. So even if you have two cups of the same type, and their contents are the same, you can still distinguish between them. @@ -96,15 +117,21 @@ So what kinds of values might be the same type and have the same contents, but n An array looks like this: `[1, 2, 3]`. This is an expression, and you can combine `[]` with other expressions. Go wild with things like: - [2-1, 2, 2+1] - [1, 1+1, 1+1+1] - +{:lang="js"} +~~~~~~~~ +[2-1, 2, 2+1] +[1, 1+1, 1+1+1] +~~~~~~~~ + Notice that you are always generating arrays with the same contents. But are they identical the same way that every value of `42` is identical to every other value of `42`? Try these for yourself: - [2-1, 2, 2+1] === [1,2,3] - [1,2,3] === [1, 2, 3] - [1, 2, 3] === [1, 2, 3] - +{:lang="js"} +~~~~~~~~ +[2-1, 2, 2+1] === [1,2,3] +[1,2,3] === [1, 2, 3] +[1, 2, 3] === [1, 2, 3] +~~~~~~~~ + How about that! When you type `[1, 2, 3]` or any of its variations, you are typing an expression that generates its own *unique* array that is not identical to any other array, even if that other array also looks like `[1, 2, 3]`. It's as if JavaScript is generating new cups of coffee with serial numbers on the bottom. A> Arrays look exceedingly simple, but this word "reference" is so laden with possibilities that there's an entire chapter devoted to discussing [rebinding and references](#references). Try typing this code out: