bookmate game
en
Bøger
Dan Mantyla

Functional Programming in JavaScript

  • .har citeretfor 6 år siden
    Lazy evaluation, also known as non-strict evaluation, call-by-need and deffered execution, is an evaluation strategy that waits until the value is needed to compute the result of a function and is particularly useful for functional programming. It's clear that a line of code that states x = func() is calling for x to be assigned to the returned value by func(). But what x actually equates to does not matter until it is needed. Waiting to call func() until x is needed is known as lazy evaluation.
  • .har citeretfor 6 år siden
    Pure functions

    Pure functions return a value computed using only the inputs passed to it. Outside variables and global states may not be used and there may be no side effects. In other words, it must not mutate the variables passed to it for input. Therefore, pure functions are only used for their returned value.
    A simple example of this is a math function. The Math.sqrt(4) function will always return 2, does not use any hidden information such as settings or state, and will never inflict any side effects.
    Pure functions are the true interpretation of the mathematical term for 'function', a relation between inputs and an output. They are simple to think about and are readily re-usable. Because they are totally independent, pure functions are more capable of being used again and again.
    To illustrate this, compare the following non-pure function to the pure one.
    // function that prints a message to the center of the screen
    var printCenter = function(str) {
    var elem = document.createElement("div");
    elem.textContent = str;
    elem.style.position = 'absolute';
    elem.style.top = window.innerHeight/2+"px";
    elem.style.left = window.innerWidth/2+"px";
    document.body.appendChild(elem);
    };
    printCenter('hello world');
    // pure function that accomplishes the same thing
    var printSomewhere = function(str, height, width) {
    var elem = document.createElement("div");
    elem.textContent = str;
    elem.style.position = 'absolute';
    elem.style.top = height;
    elem.style.left = width;
    return elem;
    };
    document.body.appendChild(printSomewhere('hello world', window.innerHeight/2)+10+"px",window.innerWidth/2)+10+"px")
    );
    While the non-pure function relies on the state of the window object to compute the height and width, the pure, self-sufficient function instead asks that those values be passed in. What this actually does is allow the message to be printed anywhere, and this makes the function much more versatile.
    And while the non-pure function may seem like the easier option because it performs the appending itself instead of returning an element, the pure function printSomewhere() and its returned value play better with other functional programming design techniques.
    var messages = ['Hi', 'Hello', 'Sup', 'Hey', 'Hola'];
    messages.map(function(s,i){
    return printSomewhere(s, 100*i*10, 100*i*10);
    }).forEach(function(element) {
    document.body.appendChild(element);
    });
  • .har citeretfor 6 år siden
    Anonymous functions

    Another benefit of treating functions as first-class objects is the advent of anonymous functions.
    As the name might imply, anonymous functions are functions without names. But they are more than that. What they allow is the ability to define ad-hoc logic, on-the-spot and as needed. Usually, it's for the benefit of convenience; if the function is only referred to once, then a variable name doesn't need to be wasted on it.
    Some examples of anonymous functions are as follows:
    // The standard way to write anonymous functions
    function(){return "hello world"};
    // Anonymous function assigned to variable
    var anon = function(x,y){return x+y};
    // Anonymous function used in place of a named callback function,
    // this is one of the more common uses of anonymous functions.
    setInterval(function(){console.log(new Date().getTime())}, 1000);
    // Output: 1413249010672, 1413249010673, 1413249010674, ...
    // Without wrapping it in an anonymous function, it immediately // execute once and then return undefined as the callback:
    setInterval(console.log(new Date().getTime()), 1000)
    // Output: 1413249010671
    A more involved example of anonymous functions used within higher-order functions:
    function powersOf(x) {
    return function(y) {
    // this is an anonymous function!
    return Math.pow(x,y);
    };
    }
    powerOfTwo = powersOf(2);
    console.log(powerOfTwo(1)); // 2
    console.log(powerOfTwo(2)); // 4
    console.log(powerOfTwo(3)); // 8
    powerOfThree = powersOf(3);
    console.log(powerOfThree(3)); // 9
    console.log(powerOfThree(10)); // 59049
    The function that is returned doesn't need to be named; it can't be used anywhere outside the powersOf() function, and so it is an anonymous function.
    Remember our accumulator function? It can be re-written using anonymous functions.
    var
    obj1 = {value: 1},
    obj2 = {value: 2},
    obj3 = {value: 3};
    var values = (function() {
    // anonymous function
    var values = [];
    return function(obj) {
    // another anonymous function!
    if (obj) {
    values.push(obj.value);
    return values;
    }
    else {
    return values;
    }
    }
    })(); // make it self-executing
    console.log(values(obj1)); // Returns: [obj.value]
    console.log(values(obj2)); // Returns: [obj.value, obj2.value]
    Right on! A pure, high-order, anonymous function. How did we ever get so lucky? Actually, it's more than that. It's also self-executing as indicated by the structure, (function(){...})();. The pair of parentheses following the anonymous function causes the function to be called right away. In the above example, the values instance is assigned to the output of the self-executing function call.
    Note
    Anonymous functions are more than just syntactical sugar. They are the embodiment of Lambda calculus. Stay with me on this… Lambda calculus was invented long before computers or computer languages. It was just a mathematical notion for reasoning about functions. Remarkably, it was discovered that—despite the fact that it only defines three kinds of expressions: variable references, function calls, and anonymous functions—it was Turing-complete. Today, Lambda calculus lies at the core of all functional languages if you know how to find it, including JavaScript.
    For this reason, anonymous functions are often called lambda expressions.
    One drawback to anonymous functions remains. They're difficult to identify in call stacks, which makes debugging trickier. They should be used sparingly.
  • .har citeretfor 6 år siden
    Let's start with a little JavaScript dilemma.
    Say we need to compile a list of values that are assigned to generic objects. The objects could be anything: dates, HTML objects, and so on.
    var
    obj1 = {value: 1},
    obj2 = {value: 2},
    obj3 = {value: 3};
    var values = [];
    function accumulate(obj) {
    values.push(obj.value);
    }
    accumulate(obj1);
    accumulate(obj2);
    console.log(values); // Output: [obj1.value, obj2.value]
    It works but it's volatile. Any code can modify the values object without calling the accumulate() function. And if we forget to assign the empty set, [], to the values instance then the code will not work at all.
    But if the variable is declared inside the function, it can't be mutated by any rogue lines of code.
    function accumulate2(obj) {
    var values = [];
    values.push(obj.value);
    return values;
    }
    console.log(accumulate2(obj1)); // Returns: [obj1.value]
    console.log(accumulate2(obj2)); // Returns: [obj2.value]
    console.log(accumulate2(obj3)); // Returns: [obj3.value]
    It does not work! Only the value of the object last passed in is returned.
    We could possibly solve this with a nested function inside the first function.
    var ValueAccumulator = function(obj) {
    var values = []
    var accumulate = function() {
    values.push(obj.value);
    };
    accumulate();
    return values;
    };
    But it's the same issue, and now we cannot reach the accumulate function or the values variable.
    What we need is a self-invoking function.
    Self-invoking functions and closures

    What if we could return a function expression that in-turn returns the values array? Variables declared in a function are available to any code within the function, including self-invoking functions.
    By using a self-invoking function, our dilemma is solved.
    var ValueAccumulator = function() {
    var values = [];
    var accumulate = function(obj) {
    if (obj) {
    values.push(obj.value);
    return values;
    }
    else {
    return values;
    }
    };
    return accumulate;
    };
    //This allows us to do this:
    var accumulator = ValueAccumulator();
    accumulator(obj1);
    accumulator(obj2);
    console.log(accumulator());
    // Output: [obj1.value, obj2.value]
    It's all about variable scoping. The values variable is available to the inner accumulate() function, even when code outside the scope calls the functions. This is called a closure.
    Note
    Closures in JavaScript are functions that have access to the parent scope, even when the parent function has closed.
    Closures are a feature of all functional languages. Traditional imperative languages do not allow them.
  • .har citeretfor 6 år siden
    Higher-order functions

    Self-invoking functions are actually a form of higher-order functions. Higher-order functions are functions that either take another function as the input or return a function as the output.
    Higher-order functions are not common in traditional programming. While an imperative programmer might use a loop to iterate an array, a functional programmer would take another approach entirely. By using a higher-order function, the array can be worked on by applying that function to each item in the array to create a new array.
    This is the central idea of the functional programming paradigm. What higher-order functions allow is the ability to pass logic to other functions, just like objects.
    Functions are treated as first-class citizens in JavaScript, a distinction JavaScript shares with Scheme, Haskell, and the other classic functional languages. This may sound bizarre, but all this really means is that functions are treated as primitives, just like numbers and objects. If numbers and objects can be passed around, so can functions.
    To see this in action, let's use a higher-order function with our ValueAccumulator() function from the previous section:
    // using forEach() to iterate through an array and call a
    // callback function, accumulator, for each item
    var accumulator2 = ValueAccumulator();
    var objects = [obj1, obj2, obj3]; // could be huge array of objects
    objects.forEach(accumulator2);
    console.log(accumulator2());
  • .har citeretfor 6 år siden
    That being said, it is true that JavaScript is not a pure functional language. What's lacking is lazy evaluation and built-in immutable data. This is because most interpreters are call-by-name and not call-by-need. JavaScript also isn't very good with recursion due to the way it handles tail calls. However, all of these issues can be mitigated with a little bit of attention. Non-strict evaluation, required for infinite sequences and lazy evaluation, can be achieved with a library called Lazy.js. Immutable data can be achieved simply by programming technique, but this requires more programmer discipline rather than relying on the language to take care of it. And recursive tail call elimination can be achieved with a method called Trampolining. These issues will be addressed in Chapter 6, Advanced Topics & Pitfalls in JavaScript.
    Many debates have been waged over whether or not JavaScript is a functional language, an object-oriented language, both, or neither. And this won't be the last debate.
  • .har citeretfor 6 år siden
    JavaScript is a functional language, all the way. Its functions are first-class and can be nested, it has closures and compositions, and it allows for currying and monads. All of these are key to functional programming
  • .har citeretfor 6 år siden
    European Computer Manufacturers Association Script(ECMAScript), JavaScript's formal and standardized specifications for implementation, states the following in specification 4.2.1:
    "ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather, supports constructors which create objects. In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behavior. In ECMAScript, the state and methods are carried by objects, and structure, behavior and state are all inherited."
  • .har citeretfor 6 år siden
    Coupling is the amount of dependency between modules in a program. Because the functional programmer works to write first-class, higher-order, pure functions that are completely independent of each other with no side effects on global variables, coupling is greatly reduced. Certainly, functions will unavoidably rely on each other. But modifying one function will not change another, so long as the one-to-one mapping of inputs to outputs remains correct.
  • .har citeretfor 6 år siden
    The key to identifying functions that can be written in a more functional way is to look for loops and temporary variables, such as words and count instances in the preceding example. We can usually do away with both temporary variables and loops by replacing them with higher-order functions, which we will explore later in this chapter.
fb2epub
Træk og slip dine filer (ikke mere end 5 ad gangen)