Elegant code javascript

Is there any way I can use a prototype property in this code to make it look more DRY?

<!DOCTYPE html>
<html>
  <style type="text/css">
    #demo1, #demo2, #demo3 {
      font-family: 'Open Sans', sans-serif;
      font-size: 1.2rem;
    }
  </style>

  <body>
    <p id="demo1"></p>
    <p id="demo2"></p>
    <p id="demo3"></p>

    <script> // ensure script is called last in the HTML body

      // this is a constructor function
      function Person(first, last, age, eye) {
        this.firstName = first;
        this.lastName = last;
        this.age = age;
        this.eyeColor = eye;
      }

      // these are new instances
      var myFather = new Person("John", "Doe", 50, "blue");
      var myMother = new Person("Sally", "Rally", 48, "green");
      // this is calling the instances and assembling them into the DOM
      document.getElementById("demo1").innerHTML = "My father is " + myFather.age + ". My mother is " + myMother.age + ".";

      // this is another constructor function
      function Mutation(firstName, lastName, looks, power) {
        this.firstName = firstName;
        this.lastName= lastName;
        this.looks = looks;
        this.power = power;
      }

      // these are new instances
      var xMen1 = new Mutation ("Scott", "Summers", "handsome", "laser");
      var xMen2 = new Mutation ("Ororo", "Munroe", "beauty", "lightening and wind");
      document.getElementById("demo2").innerHTML = "Fierce as a storm, " + xMen2.firstName + "'s tornado-like powers is matched only by her astounding " + xMen2.looks + ".";
      document.getElementById("demo3").innerHTML = "Leader of the Xmen, " + xMen1.firstName + "'s power is shown by his unforgiving " + xMen1.power + " filled gaze.";
    </script>
  </body>
</html>

Hi @gschudel I’m not sure how all the logic ties up but you can definitely create a prototype for person:

var Person = function(first, last, age, eye) 
{
	this.firstName = first;
	this.lastName = last;
	this.age = age;
	this.eyeColor = eye;
}

Person.prototype = 
{
	mutate : function(firstName, lastName, looks, power) 
	{
		this.firstName = firstName;
		this.lastName= lastName;
		this.looks = looks;
		this.power = power;		
	}
};

If you dig in a bit online you can also find out how to create prototype classical inheritance, which may come in handy for what you may be trying to achieve e.g. Mutant extends Person

http://javascript.crockford.com/inheritance.html

You can make Mutation inherit from Person.

  function Mutation(firstName, lastName, looks, power) {
    // call super constructor
    Person.call(this, firstName, lastName); // no eye color or age?

    this.looks = looks;
    this.power = power;
  }

  // inherit person's methods (if there are any)
  Mutation.prototype = Object.create(Person.prototype);

But since ES6, this is easier to do with the class syntax.

  class Person {
    constructor(first, last, age, eye) {
        this.firstName = first;
        this.lastName = last;
        this.age = age;
        this.eyeColor = eye;
    }
  }

  class Mutation extends Person {
    constructor(firstName, lastName, looks, power) {
        // call super constructor
        super(firstName, lastName); // no eye color or age?

        this.looks = looks;
        this.power = power;
    }
  }
2 Likes

Prototypes are useful if you have some shared logic among your object instances, such as

Person.prototype.getName = function () {
  return this.firstname + ' ' + this.lastname
}

But for mere property assignments (like in your code) that wouldn’t make much sense as such properties are usually specific to the object instances anyway – there’s no shared firstname property, say.

If however the object instantiation itself requires some further logic, a mixin might be an option; for example something like

const nameMixin = function (firstname, lastname) {
  this.firstname = firstname
  this.lastname = lastname
  this.fullname = firstname + ' ' + lastname
}

const Person = function (firstname, lastname, age, eyeColor) {
  this.age = age
  this.eyeColor = eyeColor
  nameMixin.call(this, firstname, lastname)
}

(This example is of course contrived as a getter for fullname would be much more appropriate here – which would be possible from a mixin too.)

Another approach would be the use of factory functions for specific aspects of your objects:

const name = (
  firstname, 
  lastname
) => ({ 
  firstname, 
  lastname,
  fullname: firstname + ' ' + lastname
})

const person = (
  age, 
  eyeColor, 
  health = 100
) => ({ 
  age, 
  eyeColor,
  health
})

const mutant = (
  looks, 
  power, 
  healingFactor = 0
) => ({ 
  looks, 
  power,
  healingFactor
})

Which you would then combine like

const myFather = Object.assign(
  name('John', 'Doe'),
  person(42, 'blue')
)

const xMen1 = Object.assign(
  name('Scott', 'Summers'),
  mutant('handsome', 'laser')
)

One might then abstract away the actual composition in another factory like

const createPerson = (
  firstname, 
  lastname, 
  age, 
  eyeColor
) => Object.assign(
  name(firstname, lastname),
  person(age, eyeColor)
)

const createMutant = (
  firstname,
  lastname,
  looks,
  power
) => Object.assign(
  name(firstname, lastname),
  mutant(looks, power)
)

const myMother = createPerson(/* ... */)
const xMen2 = createMutant(/* ... */)

So as long as there are no shared methods among the object instances, there’s no need for prototypical inheritance – quite the contrary, it is often advocated to prefer composition over inheritance.

2 Likes

Yeah but to that I always think to myself they’re just tools and as such they have their place and you can take advantage of both. There are pretty powerful architectural solutions that use both in combination

To expand a bit on that … while you can have a property in the prototype (remember that methods–in JavaScript–are just properties that happen to have the function type) this property can not be modified from an object instance. If you try that JS will create a ‘local’ property for it (because the prototype chain lookup is read-only). If the property happens to be an object, you can still modify the object, though (since objects are passed by reference).

Just FYI, using Object.assign to mix objects together is more like inheritance than not, and it isn’t object composition at all. This has become an issue in the community lately, unfortunately, because there’s a blog and a YouTube video made by entertaining people, but entertaining != accurate, and they get their information1 wrong2.

2 Likes

Yes you’re absolutely right… thinking about it, that’d be quite a stretch. ^^ Thanks for pointing that out! So JFTR, actual composition might look more like this in JS (please correct me if I’m wrong):

const createMutant = (
  firstname,
  lastname,
  looks,
  power
) => ({
  name: name(firstname, lastname),
  mutant: mutant(looks, power)
})

Anyway I suppose my actual point was that prototypical inheritance isn’t always the answer to DRY… and as for the linked quote, I do admit that I had Eric Elliot in mind. :-P

2 Likes

Inheritance is exactly what I am learning right now coupled with this! Thanks for the suggestion

This is a good example of what I was looking for. I was hoping to have Mutation receive information from Person (similiar to the metaphor of the X-men I was using…geek out alert).

will I still need the call and new instances at the bottom

var xMen1 = new Mutation ("Scott", "Summers", "handsome", "laser");
var xMen2 = new Mutation ("Ororo", "Munroe", "beauty", "lightening and wind");


document.getElementById("demo2").innerHTML = "Fierce as a storm, " + xMen2.firstName + "'s tornado-like powers is matched only by her astounding " + xMen2.looks + ".";

document.getElementById("demo3").innerHTML = "Leader of the Xmen, " + xMen1.firstName + "'s power is shown by his unforgiving " + xMen1.power + " filled gaze.";

I’ve reviewed ES6, I just want to rewind and understand the power of Objects better first.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.