A thorough discussion of JavaScript functional programming _javascript techniques

Source: Internet
Author: User
Tags anonymous benchmark closure gcd setinterval
Sometimes, an elegant implementation is a function. Not a method. Not a class. is not a frame. Just functions. 
                      -John Carmack, chief programmer of the Game "Doom"

Functional programming is all about how to break a problem into a series of functions. In general, functions are chained together, nested, and transmitted back and forth, and are considered first class citizens. If you've ever used a framework such as jquery or node.js, you should have used some of these techniques, but you didn't realize it.

We started with a little embarrassment from JavaScript.

Suppose we need a list of values that are assigned to ordinary objects. These objects may contain anything: data, 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]

This code can be used but not stable. Any code can change a values object without the accumulate () function. And if we forget to give values an array [], this code doesn't work at all.

But if the variable is declared inside the function, he will not be changed by any trick 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]

No way! Only the value of the object that was last passed in is returned.

We might be able to solve this problem by nesting a function inside the first function.

var valueaccumulator = function (obj) {
 var values = []
 var accumulate = function () {
  Values.push (obj.value );
 };
 Accumulate ();
 return values;
};

But the problem still exists, and we can't access the accumulate function and values variable now.

What we need is a self calling function

Self-invoking functions and closures

What if we could return a function expression that could return an array of values in turn? Variables declared within a function can be accessed by all code within the function, including the self invocation function.

By using a self calling function, the front of the embarrassment disappears.

var valueaccumulator = function () {
 var values = [];
 var accumulate = function (obj) {
  if (obj) {
   values.push (obj.value);
   return values;
  } else {return
   values;
  }
 };
 return accumulate;
};
This is allows us to doing this:
var accumulator = Valueaccumulator ();
Accumulator (OBJ1);
Accumulator (OBJ2);
Console.log (accumulator ());
Output: [Obj1.value, Obj2.value]
valueaccumulator =->
 values = []
 (obj)->
  Values.push Obj.value If obj
  values

These are all about scopes. Variable values are visible in the internal function accumulate (), even when the external code is calling the function. This is called closure.

A closure in JavaScript is a function that can access the parent scope, even if the parent function has finished executing.

Closures are a feature of all functional languages. The traditional imperative language has no closures.

Higher order functions

A self invocation function is actually a form of a higher order function. Higher-order functions are either input with other functions, or functions that return a function as output.

Higher-order functions are not common in traditional programming. When an imperative programmer uses loops to iterate over an algebraic group, a functional programmer uses a completely different implementation approach. With higher-order functions, each element of an array can be applied to a function and a new array is returned.

This is the central idea of functional programming. Higher-order functions have the ability to pass logic like objects to functions.

In JavaScript, a function is treated as a first-class citizen, which is the language of a classical function such as scheme and Haskell. This may sound odd, but it actually means that the function is treated as a basic type, just like numbers and objects. If numbers and objects can be passed back and forth, then the function can also.

to actually see. Now use the Valueaccumulator () function of the previous section in conjunction with higher-order functions:
Use foreach () to traverse an array and call the callback function on each of its elements Accumulator2
var Accumulator2 = Valueaccumulator ();
var objects = [Obj1, Obj2, obj3]; This array can be very large
Objects.foreach (ACCUMULATOR2);
Console.log (Accumulator2 ());

Pure function

The results returned by a pure function only relate to the parameters passed in. External variables and global states are not used here, and there are no side effects. In other words, you can't change the variables that are passed in as input. Therefore, only values returned by pure functions can be used in a program.

Use a mathematical function to give a simple example. MATH.SQRT (4) will always return 2, without using any hidden information, such as settings or States, without any side effects.

Pure function is the real deduction of mathematical "function", which is the relationship between input and output. Their ideas are simple and easy to reuse. Because pure functions are completely independent, they are more appropriate to be used again and again.

For example, compare the impure function with the pure function.

To print information 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 functions do the same thing
var printsomewhere = function (str, height, width) {
 var elem = document.createelement ("div"); c13/>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) + + "px",
WINDOW.INNERWIDTH/2) + + "px");

Non-pure functions rely on the state of the Window object to compute the width and height, and self-sufficient pure functions require these values to be passed in as arguments. It actually allows the information to be printed anywhere, which makes the function more useful.

A non-pure function appears to be an easier choice because it implements the Append element within itself, not the return element. A pure function that returns a value Printsomewhere () will perform better with other functional programming techniques.

var messages = [' Hi ', ' Hello ', ' Sup ', ' Hey ', ' Hola '];
Messages.map (function (s, i) {return
 Printsomewhere (s, * i *, * i *);
}). ForEach (function (Element) {
 document.body.appendChild (element);
});

When a function is pure, that is, not dependent on the state and the environment, we do not have to control when it is actually calculated. The lazy evaluation at the back will talk about this.

anonymous functions

Another benefit of having a function as a first class object is an anonymous function.

As the name implies, an anonymous function is a function without a name. More than that. It allows for the ability to define temporary logic in the field. Often the advantage of this is convenience: If a function is used only once, there is no need to waste a variable name on it.

Here are some examples of anonymous functions:

Standard way to write anonymous functions
() {return
 ' Hello World '
};

Anonymous functions can be assigned to variable
var anon = function (x, y) {return
 x + y
};

Anonymous functions are used in place of named callback functions, which are a more common use of anonymous functions
setinterval (function () {
 console.log (new Date (). GetTime ())
}, 1000 );
output:1413249010672, 1413249010673, 1413249010674, ...

If it is not included in an anonymous function, he will be executed immediately,
//and return a undefined as a callback function:
setinterval (Console.log) (New Date (). GetTime ()), 1000)
//output:1413249010671

Here is an example of how anonymous and High-order functions work together

function Powersof (x) {return
 function (y) {
  //This is a 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 returned here does not need to be named, it can be used anywhere outside the powersof () function, which is the anonymous function.

Do you remember that function of the accumulator? It can be overridden with an anonymous function

var
 obj1 = {value:1},
 obj2 = {Value:2},
 obj3 = {Value:3};
 
var values = (function () {
 //anonymous function
 var values = [];
 return function (obj) {
  //There is an anonymous function!
  if (obj) {
   values.push (obj.value); return
   values;
  } else {return
   values;
}}) (); Let it execute
console.log (VALUES (obj1));//Returns: [Obj.value]
Console.log (VALUES (OBJ2));//Returns: [ Obj.value, Obj2.value]
obj1 = {Value:1}
obj2 = {Value:2}
obj3 = {Value:3}

values = Do->
   valuelist = []
 (obj)->
  valuelist.push obj.value if obj
  valuelist
console.log (Values Obj1 )); # Returns: [Obj.value]
Console.log (VALUES (OBJ2)); # Returns: [Obj.value, Obj2.value]

That's great! A High-order anonymous pure function. How can we be so lucky? Actually, there's more than that, and there's a self executing structure in it, (function () {...}) ();。 The parentheses behind the function allow the function to be executed immediately. In the example above, the value assigned to the outside values is the result of the function execution.

Anonymous functions are not just grammatical sugars, they are avatars of lambda calculus. Please listen to me. The lambda calculus has been around long before computer and computer languages were invented. It's just a mathematical concept of a function. Unusually, although it defines only three expressions: variable references, function calls, and anonymous functions, it is found to be Turing complete. Today, the lambda calculus is at the heart of all functional languages, including JavaScript.
For this reason, anonymous functions are often referred to as lambda expressions.

Anonymous functions also have the disadvantage that they are difficult to identify in the call stack, which can cause some difficulties in debugging. Be careful to use anonymous functions.

Method Chain

In JavaScript, it is common to chain methods together. If you've ever used jquery, you should have used this technique. It is sometimes called the "builder model".

This technique is used to simplify the code that multiple functions apply to an object in turn.

Each function takes one row to invoke, rather ...
arr = [1, 2, 3, 4];
arr1 = Arr.reverse ();
ARR2 = Arr1.concat ([5, 6]);
ARR3 = Arr2.map (math.sqrt);

...... String them together in one line
console.log ([1, 2, 3, 4].reverse (). Concat ([5, 6]). Map (MATH.SQRT));
Parentheses may indicate what is going on
console.log (([1, 2, 3, 4]). Reverse ()). Concat ([5, 6]). Map (MATH.SQRT));

This is only valid if the function is a method owned by the target object. If you want to create your own function, for example, to zip the two arrays together, you must declare it as a member of the Array.prototype object. Take a look at the following code fragment:
Array.prototype.zip = function (arr2) {
// ...
}

So we can write the following.
Arr.zip ([11,12,13,14). Map (function (n) {return n*2});
Output:2, 22, 4, 24, 6, 26, 8, 28

Recursion

Recursion should be the most famous functional programming technique. is a function that calls itself.

When the function calls itself, sometimes strange things happen. It behaves as a loop, executes the same code multiple times, and is also a stack of functions.

Recursive functions must be used with great care to avoid infinite loops (this should be called infinite recursion). Just like a loop, you must have a stop condition. This is called the base case.

Here's an example.

var foo = function (n) {
 if (N < 0) {
  //Benchmark case return
  ' Hello ';
 } else {
  //recursive case return
  foo (n-1) ;
 }
}
Console.log (foo (5));

The code in the original text is incorrect, the function call of the recursive case is missing return, which results in the function being executed in the end. Here has been corrected.

Recursion and loops can be converted to each other. But recursive algorithms are often more appropriate, even necessary, because in some cases the loop is laborious.

An obvious example is traversing the tree.

var getleafs = function (node) {
 if (node.childNodes.length = 0) {
  //base case return
  Node.innertext
 } E LSE {
  //recursive case: Return
  node.childNodes.map (GETLEAFS);
 }
}

Divide

Recursion is not just an interesting way to replace the for and while loops. There is an algorithm called Divide and conquer, which recursively splits the problem into smaller cases until it is small to be resolved.

Historically, a Euclidean algorithm was used to find the largest common denominator of two numbers.

function gcd (A, b) {
 if (b = = 0) {
  //Benchmark case (governance) return
  A;
 } else {
  //recursive case (sub) return
  gcd (b, A% b);
 }

Console.log (gcd (12,8));
Console.log (GCD (100,20));

GCB = (A, b)-> if B is 0 then a else GCB (b, a% b)

In theory, divide and conquer is great, but is it useful in reality? Of course! Using JavaScript to sort the array of functions is not very good, it replaces the original arrays, which means that the data is not constant, and it is not reliable and flexible. Through divide and conquer, we can do better.

The full implementation code is about 40 lines, and here only pseudocode is displayed:

var mergesort = function (arr) {
 if (Arr.length < 2) {
  //Datum case: An array of only 0 or 1 elements is not sorted return
  items;
 } else {
  //Recursive case: Split the array, sort, combine
  var middle = math.floor (ARR.LENGTH/2);
  Cent
  var left = mergesort (Arr.slice (0, middle));
  var right = MergeSort (Arr.slice (middle));
  Governance
  //merge is an auxiliary function that returns a new array that merges two arrays into a return
  merge (left, right);
 }

A better example of sorting with divide and conquer is the fast row, which uses JavaScript with only 13 lines of code. Please refer to my previous blog "Elegant Functional programming language"

Lazy evaluation

Lazy evaluation, also known as a non rigorous evaluation, is invoked on demand and deferred execution, which is particularly useful for functional programming, which is the evaluation policy that evaluates the result of a function until it is needed. For example, a line of code is x = Func (), and the return value obtained by calling this func () function is assigned to X. But what x equals is not important at first, until X is needed. The call to Func () is an inert evaluation when you need to use X.

This strategy allows for significant performance enhancements, especially when using method chains and arrays of these functional programmers ' favorite program flow technologies. An exciting advantage of lazy evaluation is that it makes infinite sequences possible. Nothing needs to be really computed until it is really unable to continue the delay. It can be this way:

Idealized JavaScript pseudo code:
var infinatenums = range (1 to infinity);
var tenprimes = Infinatenums.getprimenumbers (). (10);

This opens the door to many possibilities, such as asynchronous execution, parallel computing, and combination, which is only a few.

However, there is also the problem that JavaScript itself does not support lazy evaluation, meaning that there is a library of functions that allow JavaScript to simulate lazy evaluation, which is the subject of chapter three.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.