Converting Object-Oriented Programming to Functional Programming in JavaScript

My JavaScript code looks like this:

function stringFormatter(locale) {
  return {
    getA: function() {
      return 'A' + locale;
    },
    getB: function() {
      return 'B' + locale;
    },
    getC: function() {
      return 'C' + locale;
    }
  };
}

// when page just render
let stringManager = stringFormatter('enUS');
// call it in other components 
let result = stringManager.getA();

With this Object-Oriented Programming approach, I only need to set the locale once using the stringFormatter function when the page first loads. Subsequent calls to methods getA, getB or getC will automatically adapt to the specified locale.

Now, if I want to transition to a more Functional Programming style while maintaining the same concept, here is one possible solution:

function getLocaleString(locale) {
  return function(letter) {
    return letter + locale;
  };
}

const getString = getLocaleString('enUS');
let result = getString('A');

This new approach eliminates the need to pass the locale parameter every time we call getA, getB or getC. It allows us to configure the locale value just once, without relying on Object-Oriented Programming.

Answer №1

Utilize a closure:

let getA;
let getB;
let getC;

function initializeStringFormatter (x) {
    let language = x;

    getA = function () { return 'A' + language }
    getB = function () { return 'B' + language }
    getC = function () { return 'C' + language }
}

initializeStringFormatter('enUS');

getA();

Closures have the ability to offer the same functionalities as object properties in OOP. There exists a direct correspondence between closures and objects where closures employ scope for attaching variables to functions while objects use bindings for attaching variables to methods.

It is common practice to blend OOP with FP. Even Lisp incorporates an OOP library. A typical JavaScript convention involves returning an object instead of using function names as global variables:

function initializeStringFormatter (language) {

    function a () { return 'A' + language }
    function b () { return 'B' + language }
    function c () { return 'C' + language }

    return {
        getA: a,
        getB: b,
        getC: c
    }
}

let stringManager = initializeStringFormatter('enUS');

stringManager.getA();

In JavaScript, it is frequent to witness FP developers employing objects primarily as namespaces for functions. State management can be entirely accomplished through closures without relying on object properties.

Note: In FP, languages like Lisp do not necessarily require built-in OOP support; rather, OOP is considered a design pattern or library - the standard OOP library in Lisp being CLOS: the Common Lisp Object System

The drawback of closures is that they are essentially private due to the fact that they are local variables within functions. However, this adheres to good practices seen in the OOP realm where public access to variables should be restricted. By utilizing closures, variable access is exclusively done through functions, akin to the getter/setter design pattern commonly observed in OOP implementations.

Although not completely pure FP, JavaScript still permits the modification of enclosed variables. Nevertheless, the aforementioned function adheres to pure FP standards since the locale variable remains unmodifiable.

To achieve 100% pure FP in JavaScript, one straightforward approach is to eliminate all instances of let and var in code and strictly use const. While initially feeling unusual, writing programs solely with const and constant function arguments is feasible. Languages such as JavaScript and Go provide flexibility enabling the use of variables for managing state-based logic seamlessly.

Answer №2

Ever considered using a thunk?

A thunk is a function that delays its computation until the result is actually needed, allowing for lazy evaluation of arguments. (Check out ramda's definition of a thunk)

const createThunkFromLocale = (locale) => () => (toCreate) => toCreate + locale;

let exampleThunk = createThunkFromLocale('en-US')();
console.log(exampleThunk('One'));
console.log(exampleThunk('Two'));
console.log(exampleThunk('Three'));

exampleThunk = createThunkFromLocale('vi-VN')();
console.log(exampleThunk('X'));
console.log(exampleThunk('Y'));
console.log(exampleThunk('Z'));

Answer №3

Creating state outside of a function can be achieved with ease:

let language = 'en-US';

function getLetter() {
 return 'A' + language;
}

However, the use of functions does not necessarily make it a truly 'functional' approach.

If state is manipulated outside of the function, it essentially mimics class behavior, just located externally.

In embracing functional programming, it's ideal for a function's outcome to remain unaffected by side effects.

To address this, employing a higher-order function becomes essential:

function getLetterForLanguage(language) {
  return () => {
    return 'A' + language;
  }
}

This enables calling:

const getLetter = getLetterForLanguage('en-US');

Subsequently, utilizing getLetter multiple times is plausible.

While this approach leans more towards functionality, its superiority over class implementation remains uncertain.

Answer №4

You have the option to establish a global default value that can be used as the default locale in each function.

Update: To modify the defaultLocale, define it using let; otherwise, maintain it with const.

let defaultLocale = 'enUS';

const getA = (locale = defaultLocale) => 'A' + locale;
const getB = (locale = defaultLocale) => 'B' + locale;
const getC = (locale = defaultLocale) => 'C' + locale;

let result = getA();

console.log(result);  // Output: AenUS

defaultLocale = 'fr'; // Change the default locale

console.log(getB());  // Output: Bfr


Update: If you prefer the thunk approach suggested by hgb123, you can pass the function into the thunk.

const getA = (locale) => 'A' + locale;
const getB = (locale) => 'B' + locale;
const getC = (locale) => 'C' + locale;

const thunkFormatLocale = locale => () => func => func(locale);

let thunk = thunkFormatLocale('enUS')();
console.log(thunk(getA));
console.log(thunk(getB));
console.log(thunk(getC));

thunk = thunkFormatLocale('fr')();
console.log(thunk(getA));
console.log(thunk(getB));
console.log(thunk(getC));

Answer №5

Building upon @slebetman's response, we can utilize the latest features of Javascript to streamline our closure.

const createStringFormatter = language => ({
  getA: () => 'A' + language,
  getB: () => 'B' + language,
  getC: () => 'C' + language
});

let textManager = createStringFormatter('enUS');

textManager.getA();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Perform ng-repeat on an array containing several other arrays

This is an angularjs function that retrieves specific categories. $scope.getSpecificCat = function(p_cat) { $http.get(url+'getSpecificCatJson/' + p_cat).success(function(data){ $scope.specifics = data; }).error(functi ...

How can we align the top edge of a div to the center of a circle within a separate div in a responsive manner?

I want to create 2 stacked divs: the first div contains a circular image, and the second div contains text. I want the second div to always cover half of the circle, with its upper edge positioned at the center point of the circle. This is my code: .cov ...

Modifying properties of an array of objects in React Native using JavaScript

I have a scenario where I am using Flatlist to render a couple of boxes. If the "shapes" element's "visible" key is false, the box will be blank. This visibility property is defined in state and I'm not sure if this is the correct approach. Now, ...

Creating dynamic axes and series in Ext JS 4 on the fly

I am looking to dynamically generate the Y axis based on a JSON response. For example: { "totalCount":"4", "data":[ {"asOfDate":"12-JAN-14","eventA":"575","eventB":"16","eventC":"13",...}, {"asOfDate":"13-JAN-14","eventA":"234","eventB":"46","even ...

Utilizing the "onmouseover" event to show a tooltip with JavaScript

Is there a way to create small dialogue boxes using JavaScript for user guidance when entering data into a field by hovering over them? I am new to JavaScript and not sure if my approach is correct. Here is the code I have: <html> <head> ...

Organize an array of objects based on their corresponding years

I am currently facing a challenge in grouping an array of objects by their respective years. To provide some context, I have a visually appealing horizontal timeline graph created using D3js. My goal is to group events with overlapping dates by the year t ...

Working with JSON data in AngularJS2's templates

Is there a way for me to process JSON from the template in a manner similar to the second code I provided? First code. This method works well when using .json and .map () @Component({ ..// template: `..// <li *ngFor="#user of users"> ...

Is it time to reconsider using multiple Angular filters in ng-repeat loops?

I am in the process of developing a car rental website using Angular framework. In the section where users select cars, they should be able to filter based on various attributes like 4x4 capability, automatic or manual transmission, and different categori ...

What is the proper method for adding an HTML tag <br> directly following a div element

I need to add a <br> tag after each <div>. This is my code function generateLayout() { for (i = 1; i < 101; i++) { $('#content').append('<div class=\'block block' + i + '\'>< ...

The script I added is malfunctioning when I utilize laravel/ui

Recently, I started using a template from node js during a course. While reading various posts, I learned that bootstrap utilizes jquery. It dawned on me that the script placed in the head section was causing issues when inserting scripts in other views. ...

When using Vue2, pushing a string to an array simply replaces the existing string instead of appending it

My current task involves manipulating a local data array by adding and removing strings within a method. However, I have noticed that my logic always results in the array containing only a single string passed to the updateIdArr method. Even after removin ...

Halt scrolling news feed when hovering over with the mouse

I'm currently working on a news ticker on this jsfiddle, but it's not functioning as I'd like. Here are the issues I'm facing: When I increase the width and height of all the divs, it doesn't work properly as the last divs over ...

Iterate using jQuery through all child div elements

<div id="SelectedSection" class="selected"> <div class="sec_ch" name="7"> <div class="sec_ch" name="8"> <div class="sec_ch" name="9"> <div class="sec_ch" name="11"> <div class="clear"> </div> </di ...

"JavaScript/jQuery: The pattern in the text does not align with the string

I am currently working on validating a text field with the specific data pattern of "I-MH-ABCD-ABC-1222". Below is the regular expression I have implemented, but unfortunately it is not functioning as intended. var router_added_sap = "I-MH-ABCD-ABC-1222" ...

Transforming milliseconds into a specific date using jQuery and JavaScript

My mind tends to wander, but I'll try to keep this concise - In an effort to cure my boredom, I've embarked on creating a "shoutbox", and one aspect has me a bit perplexed. I am seeking to record the time when a message is submitted, ensuring th ...

Can you explain how to implement a generic type in a function, ensuring that type meets specific criteria?

I'm looking to create a function that can clone either an object or array. It should return the same type as the argument passed in, but only objects and arrays are accepted. Does anyone know how to accomplish this task? export function clone<T&g ...

Stopping a jQuery AJAX request after receiving another response

I am facing a problem and I need some creative solutions :) Currently, I have two $.ajax calls in my code. The first call is asynchronous and takes a long time to respond (approximately 1 minute). On the other hand, the second call is synchronous (with as ...

The issue of Safari causing duplicate ajax calls to be submitted

Upon observation, it appears that Safari 5.0.5 (6533.21.1) is sending duplicate ajax calls. To illustrate this issue, I have conducted a simplified test as shown below: // jquery 1.6 include $(document).ready(function() { setTimeout(function(e) { ...

Using JavaScript to place image data on the canvas in an overlay fashion

I recently wrote the code below to generate a rectangle on a canvas: <!DOCTYPE html> <html> <body> <canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;"> Your browser does not support the HTML5 canv ...

Is it possible to customize the color of the placeholder and clear-icon in the ion-search bar without affecting

I am working with two ion-search bars and I need to customize the placeholder and clear icon color for just one of them. <ion-searchbar class="search-bar" placeholder="search"></ion-searchbar> My goal is to target a specific i ...