Beginner JavaScript OOP vs Functional

2020-05-15 13:38发布

问题:

I'm just starting to research different programming styles (OOP, functional, procedural).

I'm learning JavaScript and starting into underscore.js and came along this small section in the docs. The docs say that underscore.js can be used in a Object-Oriented or Functional style and that both of these result in the same thing.

_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });

I don't understand which one is functional and which one is OOP, and I don't understand why, even after some research into these programming paradigms.

回答1:

There's no correct definition for what is and isn't "functional", but generally functional languages have an emphasis on simplicity where data and functions are concerned.

Most functional programming languages don't have concepts of classes and methods belonging to objects. Functions operate on well-defined data structures, rather than belonging to the data structures.

The first style _.map is a function in the _ namespace. It's a standalone function and you could return it or pass it to another function as an argument.

function compose(f, g) {
  return function(data) {
    return f(g(data));
  }
}

const flatMap = compose(_.flatten, _.map);

It's not possible to do the same for the second style, because the method instance is intrinsically tied to the data used to construct the object. So I'd say that the first form is more functional.

In either case, the general functional programming style is that data should be the final argument to the function, making it easier to curry or partially apply the earlier arguments. Lodash/fp and ramda address this by having the following signature for map.

_.map(func, data);

If the function is curried, you can create specific versions of the function by only passing the first argument.

const double = x => x * 2;
const mapDouble = _.map(double);

mapDouble([1, 2, 3]);
// => [2, 4, 6]


回答2:

Programming Paradigm

Object Oriented Programming (OOP) and Functionnal Programming (FP) are programming paradigms. Roughly speaking, following a programming paradigm is writing code compliant with a specific set of rules. For example, organizing the code into units would be called OOP, avoiding side effects would be called FP.

Each programming paradigm is made of specific features, however your favorite language does not have to provide all of the features to fall into one paradigm. In fact, OOP can live without inheritance or encapsulation, thus we can say that JavaScript (JS) is an OOP language with inheritance and without encapsulation.

Now that you have a little understanding of what a programming paradigm is (hopefully), let's have a quick look at the very basics of OOP and FP.

Object Oriented Programming

In OOP, an object is a box containing informations and operations that are supposed to refer to the same concept. Informations are often known as "attributes", and operations are often known as "methods". Attributes allow to keep track of the state of the object, and methods allow to manipulate the state of the object.

In JS you can send a message to an object in order to execute a particular method. The below code shows how to invoke a method in JS. The "point" object has two attributes, "x" and "y", and a method called "translate". The "translate" method updates the coordinates of "point" based on the given vector.

point = {
  x: 10, y: 10,
  translate: function (vector) {
    this.x += vector.x;
    this.y += vector.y;
  }
};

point.x; // 10
point.translate({ x: 10, y: 0 });
point.x; // 20

There are not many features involved in such a simple case. In OOP the code is often divided up into classes, and typically supports inheritance and polymorphism. But I won't go into further details since I'm afraid I'm already outside the scope of your question.

Functional Programming

In FP, the code is essentially a combination of functions. Moreover, the data is immutable, which leads to writing programs with no side effects. In functional code, a function is not able to change the outside world, and the output value depends only on the given arguments. This allows to keep strong control over the program flow.

Actually JS can be used as a FP language as long as you take care of side effects, there is no builtin mechanism for that. The following code is an example of such a programming style. The "zipWith" function comes from the Haskell world. It merges two lists using the given function, as it happens, add(point[i], vector[i]).

zipWith = function (f, as, bs) {
  if (as.length == 0) return [];
  if (bs.length == 0) return [];
  return [f(as[0], bs[0])].concat(
    zipWith(f, as.slice(1), bs.slice(1))
  );
};

add = function (a, b) {
  return a + b;
};

translate = function (point, vector) {
  return zipWith(add, point, vector);
};

point = [10, 10];
point[0]; // 10
point = translate(point, [10, 0]);
point[0]; // 20

This definition is very superficial though. Haskell for example, which is a pure functional language, implements many more concepts such as functions composition, functors, currying, monads, etc.

Conclusion

Actually, OOP and FP are two different concepts that have nothing in common, I would even say that there is nothing to compare. Thus, I believe that what you have read from Underscore.js docs is a misuse of language.

You should not study programming paradigms in the scope of this library. Indeed, the way you write code with Underscore.js makes it similar to OOP and FP, but it is only a matter of appearence. Hence, there is nothing really exciting under the hood :-)


Refer to Wikipedia for indepth reading.

  • https://en.wikipedia.org/wiki/Programming_paradigm
  • https://en.wikipedia.org/wiki/Object-oriented_programming
  • https://en.wikipedia.org/wiki/Functional_programming


回答3:

Functional: You pass an object to the function and do stuff

_.map([1, 2, 3], function(n){ return n * 2; });

OOP: You call function on the object and do stuff

_([1, 2, 3]).map(function(n){ return n * 2; });

In both examples [1,2,3] (array) is an object.

Example OOP reference: http://underscorejs.org/#times



回答4:

FP

In FP, a function takes inputs and produces output with the guarantee that the same inputs will yield the same outputs. In order to do this, a function must always have parameters for the values it operates on and cannot rely on state. Ie, if a function relies on state, and that state changes, the output of the function could be different. FP avoids this at all costs.

We'll show a minimum implementation of map in FP and OOP. In this FP example below, notice how map operates only on local variables and does not rely on state -

const _ = {
                 //