DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • TypeScript: Useful Features
  • Tracking Bugs Made Easy With React.js Bug Tracking Tools
  • What Is useContext in React?
  • Mastering React: Common Interview Questions and Answers

Trending

  • Continuous Integration vs. Continuous Deployment
  • Development of Custom Web Applications Within SAP Business Technology Platform
  • New Free Tool From Contrast Security Makes API Security Testing Fast and Easy
  • Build a Digital Collectibles Portal Using Flow and Cadence (Part 1)
  1. DZone
  2. Coding
  3. JavaScript
  4. Programming Paradigms: Comparing Functional and Object-Orientated Styles in JavaScript

Programming Paradigms: Comparing Functional and Object-Orientated Styles in JavaScript

If you're a JavaScript developer, deciding between functional and object-oriented programming for a project may seem daunting. See what one dev has to say on each.

Alex Ogier user avatar by
Alex Ogier
·
Jul. 06, 17 · Tutorial
Like (11)
Save
Tweet
Share
8.88K Views

Join the DZone community and get the full member experience.

Join For Free

Recently, a friend of mine who is studying programming (in particular, programming paradigms) came to me with a question regarding implementing a game of TicTacToe in JavaScript for an online course. While the question related to some tricky behavior involving some nested arrays, it made me think about how I would best approach structuring a simple game myself without the various constrictions and methodologies I use within the frameworks I typically use.

In this post, I experiment implementing TicTacToe in functional and object-orientated styles and compare the difficulties and benefits I experienced when writing the game. While I wasn’t satisfied with either implementation, I do feel like I learned a bit more about working with vanilla JavaScript as well as some of the difficulties caused by the language design.

An Exploration of Programming Paradigms in JavaScript

The first version of the game I implemented was the object-oriented style (view the code and play it here on codepen).

To start, I created several classes for the various objects in the game:

  • A player class which stored some information about who controlled the player and which piece (‘X’ or ‘O’) they had.
  • A board class which saved the nine cells of the game.
  • A square class which stored those who may have naught or crossed it, along with handling some interaction.

I then wrapped the core logic of the game in a game class (this handled the various phases of the game):

function Player(piece, ai) { 
  this.piece = piece;
  this.controller = ai ? 'ai' : 'human';
}
function Board() { 
  this.squares = [[null, null, null],[null, null, null],[null, null, null]];
  for(var x = 0; x <= 2; x++) {
    for(var y = 0; y <= 2; y++) {
      this.squares[x][y] = new Square(document.querySelector('.js-x' + x + 'y' + y), this);
    }
  } 
  this.lastClicked = null;
}
function Game() { 
  this.board = new Board();
  this.players = [new Player('O', true), new Player('X', false)];
  this.currentTurn = 0;
}

Some initial difficulties I ran into when implementing the game this way was determining how to manage the player interaction. As JavaScript interactions are typically handled via a function triggered by the runtime when a user action occurs, I had trouble reconciling this with the game loop I wanted to implement. I ended up attaching event handlers on each of the squares and letting the square object handle the click. This notified the board object, and the board buffered the input in its lastClicked field.

I felt that the right way to go was to let the object clicked manage the response.

If I were to refactor the code, I would probably create a buffering system which would attach certain actions to a buffer handled by the player object rather than the board. Fortunately, TicTacToe is a simple game, so this isn’t strictly necessary.

As I was implementing the game flow via the main game loop, actions were independent of this loop, which means iterating this loop had to be done with a setTimeout which either repeated an input check or continued to the next turn of the game.

This design felt like it wasn’t suited to JavaScript’s execution model as what would be an asynchronous thread.sleep within a loop turned into a chain of recursive asynchronous method calls.

Game.prototype.ProcessTurn = function() { 
  var currentPlayer = this.players[this.currentTurn % 2]; 
  var t = this;
  setTimeout(function() { 
    var currentMove = currentPlayer.GetMove(t.board);
    var moveSuccess = t.board.ApplyMove(currentMove);
    if(moveSuccess) {
      t.Run();
    } else {
      t.ProcessTurn();
    }
  }, 100);
}

I felt the separation of concerns programming in an object-oriented style made reasoning about parts of the code quite easy and I was happy with the architecture, though I have predominantly used Marionette Backbone so that could be put down to familiarity.

One thing that was frustrating (and may have been due to over abstraction) was some boilerplate constructors and function declarations I ended up writing. I could have avoided this had I forgone best practices, and just accessed objects three references deep, buy I felt that in a larger scale project this would have become untenable.

The second implementation was written in a functional style (view the code and play it here).

One of the main advantages of functional programming is the lack of shared state and determinism. A key term is “pure function” which is a function which has no side effects, this means it doesn’t modify any program state, the returned result being the only new/modified information. “Pure function” makes reasoning about the code easier as you can be sure that no existing state changes when making a function call.

When writing this implementation, I saw some potential advantages to the object-orientated style as well as some messiness, especially when trying to maintain a reasonable amount of functional purity yet having to deal with DOM manipulation.

A critical decision I made at the start when comparing the programming paradigms was to put the entire game state within a structure (JavaScript object) as TicTacToe contains minimal state. By doing this, I could then clone and return an updated structure each time I performed a mutating operation.

Unfortunately, I still had to deal with a global reference (a pointer to the current state), and due to the asynchronous nature of event handling in JavaScript, I had to copy the references to the DOM elements that comprised the squares of the TicTacToe board each time I cloned the game state.
I removed the game loop in this implementation and triggered all events by a user click, which then fires off the chain of functions including a subsequent move by the computer player and restarts the game.

function clickListener(x, y) {
  ...
  state = processTurn(state, {x: x, y: y});
  state = checkEndgame(state);
  ..
}

The advantage of a functional style meant that I could quickly build simple functions which all took the game state and did one thing with little to no side effects.

For instance, checking whether the game is a draw:

function checkDraw(gameState) {
  return gameState.turn > 8;
}

And checking the endgame:

function checkEndgame(gameState) {
  var running = true;
  if (checkWinner(gameState)) {
  ...
  return newGameState;
}

This pattern would certainly help if I wanted to create a better opponent AI than the current (random pick) one, as it would be relatively easy to plug in an algorithm which evaluated the board state recursively (into future generations) while not modifying any existing states.

Overall, the functional implementation of the game, while including a lot of repetition, especially when cloning the game state,  looks like so:

function checkEndgame(gameState) {
  var running = true;
  if (checkWinner(gameState)) {
  ...
  return newGameState;
}

This method allowed me to manage the game flow and avoid complex data interactions. If I was going to do this on a larger scale, helper methods could reduce the amount of manual cloning, and a module system could easily allow me to divide up functions into logical groupings.

Final Thoughts

In the end, the amount of code for each implementation was almost identical (about 170 lines – including spaces) with non-logical constructors or cloning code taking up between 20% to 30% of each.

Regarding suitability within JavaScript, I believe functional styles work well with asynchronous operations and event callbacks. Regarding DOM interaction, the encapsulation of elements in a JavaScript object is responsible for updating its rendered state and propagating any interactions.

Overall, this exercise made me appreciate that while JavaScript might not be the most elegant of languages in any one respect, the multi-paradigm nature of the language does allow for a range of styles to be used independently to produce maintainable code. Some languages make it easy to write in some programming paradigms, but not others. While I’d say my preferred style for this project was the object-orientated style, this was probably due to the simplicity of abstraction it gave me. The relatively small size of the project didn’t reveal some of the actual benefits functional styles may have bought.

JavaScript

Published at DZone with permission of Alex Ogier, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • TypeScript: Useful Features
  • Tracking Bugs Made Easy With React.js Bug Tracking Tools
  • What Is useContext in React?
  • Mastering React: Common Interview Questions and Answers

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: