JavaScript Design Patterns: Prototype

Continuing on to the second of the four JavaScript design patterns my manager has asked me to learn, we now move into the ‘prototype’ pattern. I can’t make heads or tails of anything about it. It’s 3pm Tuesday. It took me 21 hours to learn the ‘module’ pattern. Let’s see if I can do better than that.

1 PROTOTYPE DESIGN PATTERN

Devan Patel – 4 JavaScript Design Patterns You Should Know

“Any JavaScript developer has either seen the keyword prototype, confused by the prototypical inheritance, or implemented prototypes in their code. The Prototype design pattern relies on the JavaScript prototypical inheritance. The prototype model is used mainly for creating objects in performance-intensive situations.”

1.1 JAVASCRIPT PROTOTYPICAL INHERITANCE

1.1.1 THE PROBLEM WITH CLASSICAL INHERITANCE

Aadit M Shah – Why Prototypal Inheritance Matters

Many people say classical inheritance is bad, but most don’t know the reasons why. Classical inheritance is not bad. Other languages use it. When I see the term “classical” I think of it as meaning “traditional”—but that’s not what we’re talking about here. What we mean is more like class-ical inheritance. And similarly, prototypical inheritance is better thought of as prototype-ical inheritance. In both cases, we’re discussing the sort of things or ways by which inheritance is achieved: classes or prototypes.

Quora: Object-Oriented Programming – what is classical inheritance?

Yukihiro Matsumoto: Ruby Inheritance

In Ruby, which uses classical inheritance it’d be like this:

class Mammal
  def growHair
    puts "I'm growing hair!"
  end
end

class Dog < Mammal
  def speak
    puts "Bark!"
  end
end

So, there’s a mammal class, and a dog class, and the dog class inherits everything from the mammal class, adding its own dog-specific properties also. Got it.

In JavaScript, any function can be used as a constructor.

1.1.1.1 CONSTRUCTORS

Toby Ho – JavaScript Constructors and Prototypes

I know, I’ve already figured out what a constructor function is. But if it was so simple, it wouldn’t continue to arise as a subject. There’s a lot I’m just not comfortable with.

1.1.1.1.1 JAVASCRIPT CONSTRUCTORS AND PROTOTYPES

Toby Ho – JavaScript Constructors and Prototypes

Everything always returns to “pretty much everything is an object”—which is easy to remember, and difficult to understand the meaning of. Functions are initially taught as “you bundle up a bunch of code and give it a name so you don’t need to repeat it”—but bits of code can have different functionality. So it could be a bit of code like

function popUp() {
  // code to make something pop up
} 

that can be used sitewide for a hundred different pop ups, and only written once. Or it could be an object constructor:

function Dog(){
    this.color = "brown";
}

that can be used to create a hundred brown dogs without needing to specify the color of each one. That’s how it’s similar. You create a brown dog like this:

spot = new Dog()

you can test this with:

spot instanceof Dog; // true

So now you’ll wonder—well, if they look the same but they’re used differently, then what happens if I use them in the wrong places? This is how I feel about most women’s clothing—it all looks the same, just loops of cloth—how do you know if it’s a skirt or a tube-top? In JavaScript, I think it’s done with capitalization. dog vs Dog. But let’s say you do this:

Dog()

It returns undefined. Okay, that makes sense because there’s nothing going on in there that we should see. But what about this?

function Dog(){
  this.color = "brown"
  console.log("i'm a dog!")
}

Dog()

Now it prints i’m a dog! like a regular function. So something’s going on. But what about the color property? If you go to the console and type:

window.color

It returns brown—because this.color must refer to something. It refers to its parent. In the case of spot it refers to spot’s color. If we’re just calling it on its own, then the parent is window—AKA the global object. This means there’s a new global variable, right? Right. Which means that we essentially just said var color = brown. To test this:

color // brown

And what if you already had a global variable called color? Now it’d be overwritten. There’s a trick to making sure that if you accidentally run a constructor function like a regular function that it’ll just create a new object rather than apply properties to the window object. But instead I’m just going to try to remember which things are which and use them as intended. Because that’s the foundation of civilization. Accordingly, I’ll skip ahead in this article to the next section that isn’t merely testing the limits of social norms.

1.1.1.1.1.1 METHODS

Toby Ho – JavaScript Constructors and Prototypes

Functions are also another thing besides being just functions and constructors. They’re also methods.

function Dog(){
  this.color = "brown"
  this.speak = function(){
    console.log("i'm a dog!")
  }
}

spot = new Dog();
spot.speak()

Now there’s a function inside this other function. Okay, so enough about that, and now: the reason we’re here…

1.1.1.1.1.2 INHERITANCE AND THE PROTOTYPE

Toby Ho – JavaScript Constructors and Prototypes

So, back to classical inheritance once more. I don’t know if it’s possible to inherit from more than one class, but here’s the same example I gave above:

class Mammal
  def growHair
    puts “I’m growing hair!”
  enda
end

class Dog Dog.prototype – > Mammal.prototype – > Animal.prototype – > Object.prototype

So, in short, Spot has the properties of Object, which you didn’t even know existed, but that’s what objects are an instance of. Further, if you add properties to any of these, spot will gain those properties too. Let’s try it.

function Animal(){
  Animal.prototype.hasLungs = true;
}

function Mammal(){
  Mammal.prototype.hairy = true;
}

function Dog(){
  this.color = "brown";
  this.speak = function(){
    console.log("I'm a dog!")
  };
}

Mammal.prototype = new Animal();
Dog.prototype = new Mammal();
spot = new Dog();

spot.speak()

console.log ( spot.hairy ? "I'm hairy!" : "Actually, I'm a fish.");
console.log ( spot.hasLungs ? "I've lungs!" : "Actually, I'm a mineral.");

Object.prototype.isSomebodysUncle = true;

spot.isSomebodysUncle // true

And now Spot is somebody’s uncle, which is coming from four degrees up the chain. And lastly, here’s where hasOwnProperty gives us some helpful information.

'color' in spot; //true
'hairy' in spot; // true

spot.hasOwnProperty('color'); // true
spot.hasOwnProperty('hairy'); // false

See, Spot has both the properties color and hairy but only one of them is Spot’s own property (since Spot’s a dog)—color—while hairy is a property of Mammal. Time for some coffee and then it’s time to get into things I really don’t understand.

1.1.1.1.1.3 SETTING METHODS ON THE PROTOTYPE

Toby Ho – JavaScript Constructors and Prototypes

Okay, so back to the first example:

function Dog(name){
  this.name = name;
  this.speak = function(){
    console.log("I'm a dog!")
  };
}

This is how I’d write it. The article says this is the better way:

function Dog(name){
  this.name = name;
}

Dog.prototype.speak = function(){
  console.log("I'm a dog!")
};

It’s more memory efficient because the function is only created once—on the prototype—and is inherited across all instances of Dog. If it’s on Dog itself, then every new instance of Dog ends up with a copy of the function. Okay.

1.1.1.1.1.4 APPLY AND CALL

Toby Ho – JavaScript Constructors and Prototypes

Awesome. I’ve been waiting for this because in the previous article (that we’ll return to after finishing this one) I was reading started talking about “you can’t apply something to this object” and I figured it meant “apply” in the Windows 95 sense at first, and then decided I didn’t know enough to read the article and stepped backward a few paragraphs. I have no idea what Apply or Call means.

function Dog(name){
  this.name = name;
}

Dog.prototype.speak = function(){
  console.log("I'm a dog named " + this.name + "!")
};

spot = new Dog("Spot");
spot.speak(); // I'm a dog named Spot!

snoopy = new Dog("Spot");
snoopy.speak(); // I'm a dog named Snoopy!

So, you take a function, attach it to an object, and now it’s a method. So you can do spot.speak() and it runs the function called speak that’s part of Dog. And when you say “this” it refers to the object it’s attached to. So this.name becomes “Spot” when attached to spot, and if it’s attached to Snoopy it’s “Snoopy”.

Article says “not quite.”

Rather, this isn’t bound to any object until you call the function. So it’s just sitting there on Dog as this.name until you call spot.speak() at which point this is bound to spot. I’m using the term bind like I know what it means. People say it often. Here it’s being used interchangeably with ‘attach to’.

####### 1.1.1.1.1.4.1 BINDING

Wikipedia: Binding

I can’t find a post that goes into basics of the term. Rather, they discuss the ‘bind’ function in JavaScript or other complicated sorts of things. So here’s three definitions that make sense from Wikipedia:

  • Data Binding: the technique of connecting two data elements together. (I don’t know what a data element is, but it sounds like it makes sense)
  • Name Binding: the association of code or data with an identifier in a programming language (in which case the identifier is this and the data is the instance of Dog
  • Late Binding: name binding which is resolved at run-time rather than in pre-execution time (which I think is what we’re looking at here, where this remains this until we call spot.speak() at which point this binds to spot.
1.1.1.1.1.5 APPLY AND CALL, CONTINUED…

Okay, so you can bind a function to an object yourself.

function Dog(name){
  this.name = name;
}

speak = function(){
  console.log("I'm a dog named " + this.name + "!")
};

spot = new Dog("Spot");

speak.apply(spot); // I’m a dog named Spot!

In this example, rather than adding speak as a method on the Dog prototype, now it’s just a function. Which means it can be run on its own—but what would this.name apply to in this case? this would be the parent object—window and it turns out that its name is my name. I don’t know why that is, but it’s interesting anyway. So…

speak()// I’m a dog named Stephen!
this // Window
this.name // “Stephen”

Back to the example, I make a dog named Spot, and it has no speak method anymore. There’s an error if you try it. But speak.apply(spot) returns I’m a dog named Spot! – and also, the object still does not have the speak method. ‘apply’ is a method belonging to the Function prototype. Because everything’s an object, including Functions. So you can bind any object to any function. Also, you can pass parameters as an array. So…

function Dog(name){
  this.name = name;
}

speak = function(){
  console.log("I'm a dog named " + this.name + "!")
};

playsWith = function(otherDog1, otherDog2){
  console.log(this.name + " plays with " + otherDog1.name + " and " + otherDog2.name + ".")
}

spot = new Dog("Spot");
snoopy = new Dog("Snoopy");
olyeller = new Dog("Ol' Yeller");

playsWith.apply(spot,[snoopy, olyeller]); // Spot plays with Snoopy and Ol' Yeller.
playsWith.call(spot,snoopy,olyeller); // Spot plays with Snoopy and Ol' Yeller.

Yeah, and you can do that last bit too. See the call function—it works the same except you don’t need to pass the arguments in as an array, but rather just one after the next. I’m sure there’s more to it than that.

1.1.1.1.1.6 THE ‘NEW’ METHOD

Toby Ho – JavaScript Constructors and Prototypes

So what is .apply useful for? He provides an example of how Math.max takes a variable number of arguments, but you can’t pass them in as an array. But with .apply you can. So…

Math.max(1,2,3) // 3

numbers = [1,2,3]

Math.max(numbers); // NaN

Math.max.apply(Math, numbers) // 3

// and here's some other things that don't work

Math.max.apply(Math) // -infinity
Math.max.apply(numbers) // -infinity

So, yes, interesting, but how would I have known to pass Math in first? Who knows. The article then goes into more detail showing how flexible JavaScript is, how you can make new ways to make new objects…not relevant to what we’re trying to achieve right now.

1.1.2 THE PROBLEM WITH CLASSICAL INHERITANCE, CONTINUED…

Aadit M Shah – Why Prototypal Inheritance Matters

Okay, so anything function can be used as a constructor—and we need to use them as intended, the skirt/tube-top analogy again. Here’s the thing: on a constructor, you use it by using the new keyword. On a regular function, you just call it. But if you just call a constructor, it doesn’t construct a new object. And if you use the new keyword on a regular function, it doesn’t do the things inside the function. At least that’s what the article says. When I try it out…

function Dog(){
  this.color = "brown";
  console.log("I'm a dog.");
}

dog = new Dog(); // I'm a dog!

Dog() // I'm a dog!

function goToBed(){
  console.log("I'm going!")
}

go = new goToBed() // I'm going!
go.name = "huey"
go // Object { name: "huey" }

Looks like Dog() and goToBed() are happy to function the opposite of their intended ways. So…? Article says,

“However, this breaks functional features in JavaScript since new is a keyword, not a function. Hence functional features can’t be used in conjunction with object instantiation.”

What am I missing?

Now both this article and the one I was referencing just prior to this section are discussing the same thing: using .apply on a constructor. Both say you can’t do apply on a constructor, but wouldn’t it be nice if instead of have the new keyword we had a .new method. And THEN we could. The point being that the new keyword makes it so we can’t do function-y things like .apply

Okay, so, both articles show that this works…

function Person(firstname,lastname){
  this.firstName = firstname;
  this.lastName = lastname;
  console.log("My name is " + firstname + " " + lastname);
}

person = new Person("stephen","frost")

But that this doesn’t:

person2 = new Person.apply(null,["stephen","frost"]); 
// Person.apply is not a constructor

Because it thinks that I’m not trying to make a new Person object, but rather a new Person.apply object. Both articles suggest the following solution, which is not built in.

person2 = Person.new.apply(null,["stephen","frost"]);

Now, from Toby Ho:

Function.prototype.new = function () {
  var args = arguments
  var constructor = this
  function Fake() {
    constructor.apply(this, args)
  }
  Fake.prototype = constructor.prototype
  return new Fake
}

And a different solution, from Aadit M Shah.

Function.prototype.new = function () {
  function functor() {
    return constructor.apply(this, args);
  }
  var args = Array.prototype.slice.call(arguments);
  functor.prototype = this.prototype;
  var constructor = this;
  return new functor;
};
// you can re-order the lines and the two are very similar (‘functor’ is ‘Fake’)

Function.prototype.new = function () {
  var args = Array.prototype.slice.call(arguments);
  var constructor = this; 
  function functor() {
    return constructor.apply(this, args);
  }
  functor.prototype = this.prototype;
  return new functor;
};

Since both these articles are deeply concerned about solving this problem, should I be? This article uses it to prove a point and then move on. The other article’s point, as noted above, is to say ‘JS sure is flexible!’ – this article’s point is to say that in JavaScript you can instantiate new objects in a roundabout way thanks to the language’s flexibility, something only possible thanks to prototypal/prototypical inheritance.

Okay, so this is how it works.

// we're adding a method to the Function prototype
Function.prototype.new = function () {

  // new var for arguments bc it won't be used until Fake(), which would have
  // its own arguments object
  var args = arguments

  // the constructor would refer to the instance of the current object,
  // and since this is meant to be used by constructor functions, the current
  // object would be a constructor function! 
  var constructor = this

  // create a placeholder function we can call without 'new' keyword
  function Fake() {

    // take the constructor and send into it the arguments in the way that
    // was not possible by saying var instance = new Object.apply(args)
    // so that now we're doing it without the 'new' keyword
    constructor.apply(this, args)
  }

  // set the Fake prototype to the inherit from the constructor prototype
  Fake.prototype = constructor.prototype

  // and return the new Fake object, which now shares the same properties as 
  // whatever function was sent into it, but also has the arguments passed in
  return new Fake
}

function Stephen(fname,lname) {
  this.fname = fname;
  this.lname = lname;
}

stephen = Stephen.new("stephen","frost")
console.log(stephen.fname + " " + stephen.lname);

Great! Now I deserve to give myself a pat on the back. The article continues by listing some differences between classical and prototypal inheritance.

  • Classes cannot change at run time; prototypes can
  • Classes may or may not inherit from multiple sources; prototypes can
  • Classes are complicated; prototypes are not

Okay.

1.1.3 STOP USING THE NEW KEYWORD

Aadit M Shah – Why Prototypal Inheritance Matters

The new keyword was meant to appeal to developers of classical languages, but a) it didn’t work, and b) it obscured the prototypal inheritance.

StackOverflow: Is JavaScript’s New Keyword Considered Harmful, Part 2

It’s mostly a mindset, although I have no experience in a classical language, so I only think of new in terms of creating an object based on a blueprint. But this SO post suggests that objects should be understood as being created based on another object as the blueprint.

1.1.4 UNDERSTANDING PROTOTYPAL INHERITANCE

Aadit M Shah – Why Prototypal Inheritance Matters

1.1.4.1 EX NIHILO OBJECT CREATION

Aadit M Shah – Why Prototypal Inheritance Matters

var object = Object.create(null);

This creates an object that has no prototype—it’s a clone of null. It creates an object out of nothing.

1.1.4.2 CLONING AN EXISTING OBJECT

var dog = {
  humanAge: function(){
    return this.age * 7;
  }
}

var spot = Object.create(dog)

spot.age = 3;

spot.humanAge() // 21

In this example, dog is an object literal. Which, if I still remember correctly, is when we create an object by basically spelling it out. The object spot is created by copying the dog object. I feel like we’re regressing. Most of all because an object literal is actually a shortcut for creating dog like this:

var dog = Object.create(Object.prototype);

dog.humanAge = function(){
    return this.age * 7;
  }

1.1.4.3 EXTENDING A NEWLY CREATED OBJECT

Aadit M Shah – Why Prototypal Inheritance Matters

What we’re looking at now is that there are aspects of this that could be made more concise, particularly if we’re creating a whole lot of dogs.

var dog = {
  humanAge: function () {
    return this.age * 7;
  }
}
var spot = Object.create(dog)
spot.age = 3;
spot.humanAge() // 21

So, here’s how we could change it: we’d add a constructor function inside the dog object.

var dog = {
  create: function(age){
    var self = Object.create(this);
    self.age = age;
    return self;
  },
  humanAge: function () {
    return this.age * 7;
  }
}

var spot = dog.create(3)

console.log(spot.humanAge()); // 21

Not more concise for one dog, but for many dogs becomes extremely efficient, and begins reminding me much more of how Ruby looks. dog = Dog.new(3)

1.1.4.4 CONSTRUCTORS VS PROTOTYPES

Aadit M Shah – Why Prototypal Inheritance Matters

I’m just going to show the two patterns side by side real quick:

// PROTOTYPAL PATTERN

var dog = {
  create: function (age) {
    var self = Object.create(this);
    self.age = age;
    return self;
  },
  humanAge: function () {
    return this.age * 7;
  }
}
var spot = dog.create(3)
console.log(spot.humanAge()); // 21

// CONSTRUCTOR PATTERN

var Dog2 = function (age) {
  this.humanAge = function () {
    return age * 7
  }
}
var snoopy = new Dog2(5)
console.log(snoopy.humanAge());

The constructor pattern—the popular one—looks so much simpler, and, of course, it’s what all the tutorials talk about. So why not?

  • Constructor can’t use function features with ‘new’ keyword, but Prototypal can with ‘create’ function
  • Constructor function leads to bugs and global vars if you forget to use ‘new’ keyword with it; but ‘create’ is just a function, so it always works as expected
  • The author writes more on the confusing nature of ‘new’ keyword here:

StackOverflow: Inheritance of Variable Properties

And being completely aware that he throws around ‘it’s confusing’ without much explanation, the author now digs deeper into that assertion. Here’s an example of prototypal inheritance using prototypes:

var bigDog = Object.create(dog);

bigDog.create = function(age){
  return dog.create.call(this,Math.round((age + (age * .25))));
}

var marmaduke = bigDog.create(3);
marmaduke.humanAge(); // 28 

So, we create a clone of dog called bigDog, and then we override its create function. Using that, we create a bigDog object and calculate its humanAge. And here’s how we do it with a constructor pattern:

function SmallDog(age){
  Dog2.call(this,Math.round(age - (age * .25)));
}

SmallDog.prototype = Object.create(Dog2.prototype);

SmallDog.prototype.constructor = SmallDog;

var snoopy = new SmallDog(3);
snoopy.humanAge() // 14 

I have no idea what’s going on here. We create SmallDog(), which contains functions from Dog2 because in the next line we make the SmallDog.prototype a copy of the Dog2 prototype. And then the following line we go back and remind SmallDog that it’s a SmallDog and not a Dog2. I’m still confused. The article explains the difference in thinking of “objects inheriting from other objects” vs “constructors inheriting from other constructors”—and I can see the difficulty there is that now we get back to that fuzzy map of each object having a shadow prototype pulling the strings:

proto-and-prototype

1.1.4.5 COMBINING OBJECT CREATION AND EXTENSION

Aadit M Shah – Why Prototypal Inheritance Matters

The article now looks at this bit of the code:

var bigDog = Object.create(dog);

bigDog.create = function(age){
  return dog.create.call(this,Math.round((age + (age * .25))));
}

First, we’re creating a clone of dog, and then we’re overriding its .create function. The article suggests that it’d be better if we could combine these two. How would this work? He compares it to the way that an object literal automatically clones the Object.prototype and then adds new properties. Adding new properties in that way is called ‘extending’ and it can be done with a function.

// add function .extend on Object.prototype
// it takes 'extension' as an argument
Object.prototype.extend = function (extension) {
  var hasOwnProperty = Object.hasOwnProperty;

  // object = a clone of the current object?
  var object = Object.create(this);

  for (var property in extension)

    // this is like saying: extension.hasOwnProperty('property')
    // so if the current object has this property already
    if (hasOwnProperty.call(extension, property) 
        // or if the object that's being extended has the property
        // but it hasn't been set
        || typeof object[property] === 'undefined')

          // add the new property to the clone
          object[property] = extension[property];
  // return the clone, making the cloned object replaced by the clone
  return object;
};

The resulting code object literal appearance of the code…

var bigDog = dog.extend({
  create: function(age){
    return dog.create.call(this, Math.round((age + (age * 0.25))));
  }
});

And a new bigDog would be created the same as before. Now, we can additionally edit the create function of dog.

// WAS

var dog = {
  create: function (age) {
    var self = Object.create(this);
    self.age = age;
    return self; 
  }

// BECOMES

var dog = {
  create: function (age) {
    return this.extend({
      age: age;
    })
  }

So, what are we left with after we clean everything up?

var dog = {
  create: function (age) {
    return this.extend({
      age: age
    });
  },
  humanAge: function () {
    console.log(this.age * 7);
  },
  speak: function () {
    console.log('bark!');
  }
}

var bigDog = dog.extend({
  create: function(age){
    return dog.create.call(this, Math.round((age + (age * 0.25))));
  }
});

var smallDog = dog.extend({
  create: function(age){
    return dog.create.call(this, Math.round((age - (age * 0.25))));
  }
})


var spot = dog.create(3)
spot.humanAge(); // 21

var snoopy = smallDog.create(3);
snoopy.humanAge() // 14

var marmaduke = bigDog.create(3);
marmaduke.humanAge(); // 28

Of course, I’m leaving out the heavily commented .extend function from up above, but everything else is here and looking pretty nice. Could I do this by memory now? NO! You know what else? The word ‘prototype’ isn’t anywhere here. All I can do is keep pushing forward to see what happens when I get to the other end. Because I live my life as a bunch of systems that are immutable at run-time.

1.1.4.6 TWO METHODS OF PROTOTYPAL INHERITANCE

Aadit M Shah – Why Prototypal Inheritance Matters

The article says that in the above examples we’re inheriting from two objects: the objected being extended, and the object extending it. In our case, that would be…dog and bigDog? I’m not quite sure where or how. It looks to me like this is where we’d get dog:

create: function(age){
    return dog.create.call(this, Math.round((age + (age * 0.25))));
  }
and this is where we’d get bigDog

var object = Object.create(this);

In one, it’s delegation. In the other, it’s concatenative. Good luck. I’m going to take the dog out (really) and maybe have a good cry.

1.1.4.6.1 DELEGATION OR DIFFERENTIAL INHERITANCE

Aadit M Shah – Why Prototypal Inheritance Matters

Most JavaScript programmers are familiar with differential inheritance, but not me. Per Wikipedia, the principle is that most objects are derived from other, more general objects, and only differ slightly, while otherwise maintaining internal pointers to other objects they differ from.

  • An animal is a thing.
  • A mammal is an animal with hair, but is otherwise like other animals generally.
  • A dog is a mammal that barks, but is otherwise like other mammals generally.

The article continues that every JS object has an internal pointer, [[proto]], pointing to the prototype of the object. If the object was created ex nihilo, it points to null. So, from any object, there’s a chain of internal [[proto]] pointers that eventually end in null.

When you try to access a property of an object, it first checks the object itself. If not there, then it checks its prototype. If not there, it begins traveling up the prototype chain until it either finds the property, or the chain ends in null, in which case it returns undefined.

If this was a function, it would look like this:

// pass an object in, and also a property you're looking for
function get(object, property) {

  // if the object itself does not have that property
  if (!Object.hasOwnProperty.call(object, property)) {

    // prototype = the object's prototype
    var prototype = Object.getPrototypeOf(object);

    // if the prototype isn't null, begin this function again, using
    // prototype and the property as the arguments
    if (prototype) return get(prototype, property);

    // otherwise, if the prototype is null, return the current object
    // and the property's value, which may end up being 'undefined'
  } else return object[property];
}

get(marmaduke,"speak")

/*
function () {
    console.log('bark!');
  }
*/
1.1.4.6.2 CLONING OR CONCATENATIVE INHERITANCE

Aadit M Shah – Why Prototypal Inheritance Matters

Cloning / Concatenative Inheritance is when you copy the properties of one object to another. I believe that’s what many of the above examples use, in which we’re saying that one object should contain all the properties of another object. For instance, via the article’s extend function:

Object.prototype.extend = function (extension) {
  var hasOwnProperty = Object.hasOwnProperty;

  // object = an instance of an emoty object?
  var object = Object.create(this);

  for (var property in extension)

    // this is like saying: extension.hasOwnProperty('property')
    // so if the current object has this property already
    if (hasOwnProperty.call(extension, property) 
        // or if the object that's being extended has the property
        // but it hasn't been set
        || typeof object[property] === 'undefined')

          // add the new property to the clone
          object[property] = extension[property];
  // return the clone, which is the new object
  return object;
};

The author argues that cloning/concatenation counts as inheritance, despite one important factor: any changes made to the parent do not automatically update the clones. Here are the differences as he sees them:

  • In delegation, changes are automatically reflected on children; in concatenation, they’re not.
  • In delegation, accessing properties may be slower than in concatenation because properties may be far up the chain
  • In delegation, objects can only delegate to a single prototype (i.e., objects can only have a single parent), while in concatenation, they can have many

1.1.4.7 INHERITING FROM MULTIPLE PROTOTYPES

Aadit M Shah – Why Prototypal Inheritance Matters

Okay, so, after that teaser, how do we inherit from multiple prototypes? The article shows an edited version of the .extend function from earlier:

// add function .extend on Object.prototype
// but it no longer takes 'extension' as an argument so it probably
// takes multiple extensions
Object.prototype.extend = function () {
  var hasOwnProperty = Object.hasOwnProperty;

  // object = an instance of an empty object?
  var object = Object.create(this);

  // figure out how many extensions are passed in
  var length = arguments.length;

  // this is also new
  var index = length;

  // while index is > 0
  while (index) {

    // set extension to arguments length - index, and decrement index by 1
    var extension = arguments[length - (index--)];

    for (var property in extension)

      // this is like saying: extension.hasOwnProperty('property')
      // so if the current object has this property already
      if (hasOwnProperty.call(extension, property) 
          // or if the object that's being extended has the property
          // but it hasn't been set
          || typeof object[property] === 'undefined')

            // add the new property to the clone
            object[property] = extension[property];
  }
  // return the clone, which is the new object
  return object;
};

And how does that look in use? smallDog is already inheriting from Dog via cloning/delegation, so if smallDog is also going to inherit from president, then it will have to be via concatenation.

var smallDog = dog.extend(president,vp,{
  create: function(age){
    return dog.create.call(this, Math.round((age - (age * 0.25))));
  }
})

var president = {
  tweet: function(){
    console.log("Lok at me. Im tweetting.");
  }
}

var vp = {
  notweet: function(){
    console.log("Im not allowd to twett.");
  }
}

var snoopy = smallDog.create(3);
snoopy.humanAge() // 14

snoopy.tweet(); // Lok at me. Im tweetting.
snoopy.notweet(); // Im not allowd to twett

1.1.4.8 BLUEPRINTS FOR MIXINS

Aadit M Shah – Why Prototypal Inheritance Matters

The president prototype in the above example has no create function because a president object should not be created directly. It should be used as a prototype for other prototypes—which is called a ‘mixin’. A mixin extends an object by giving it new, reusable behaviors.

Beyond this, I’ve been completely unable to wrap my head around the examples discussed here. Let’s look at them one at a time:

I don’t know what is a/an: event, emitter, event emitter, listener.

1.1.4.8.1 INTRODUCTION TO EVENTS

Peter-Paul Koch: Introduction to Events

JavaScript was created to add interactivity to web pages, so it almost always relies on ‘events’ for the scripts to work: that is, the user does something and the page reacts to it.

So, JavaScript needs a way to figure out when the user does something.

When something happens, whether it’s the user clicking, or the page finishing loading, it’s an event.

JavaScript can detect some events, often by using ‘event handlers’. Event handlers is a thing that waits for an event to take place, and then handles it by executing some JavaScript. I’ve heard this term for a long time, but never understood what it meant. But at this very moment, I do understand. I do! An event handler is like an elephant handler. The elephant handler waits for the elephant to charge, and then he’s like “don’t worry guys, I’ll handle it” and then he shoots the elephant with a sedative. Elephant handled. The event handler hangs out on a button, and when the user clicks the button, it’s like “don’t worry guys, I’ll handle it” and then he executes the buttonPushed function.

The earliest way of handling events was inline, using onclick="console.log('you clicked me')" and works in all browsers. Since then, it’s been decided that you shouldn’t do this because we’re separating JavaScript from HTML, so nowadays, events handlers can be set through the javascript itself, rather than inline.

The order that things happen is like this: the event handler script does stuff, then the default action takes place. So if you click a submit button, first the event handler does something (like validate a form), and then the form is submitted. Let’s say we don’t want the form to be submitted by default—because instead we want the form validator to dictate whether or not that’s going to happen—you’d write return false; at the end, which means ‘don’t do the default action.’ It’s also possible to be like return trueOrFalse(); which would go to a function that returns a true or false.

1.1.4.8.1.1 THIS

In inline event handling, this refers to the object that the event is handled by. For instance, if clicking a button produces the event, then the button is the this. So you can do things like return doSomething(this) and pass the element into the function.

1.1.4.8.1.2 TRADITIONAL EVENT REGISTRATION

To register an event, you’d do something like this: element.onclick = doSomething;
In which, indeed, there are no parens at the end of the function name. If you put the parens, then it wouldn’t register the function, it would register the result of the function.

1.1.4.8.2 EMIT

I can find almost nothing to help explain about emitting events. At the most basic, which is about all I could dig up, emitting events means you’re getting your code to create a custom event. So, instead of having a handler waiting for onclick, it could be waiting for mycustomevent. So, you click a div, and it would do something like this.emit('myCustomEvent', {apple: 'red'}) and then somewhere else, the listener would be like
$('div’).on('myCustomEvent', function(){alert("hello!")});

How exactly to get this sort of functionality, I’m not sure—as it seems to rely on other libraries.

1.1.4.8.3 LISTENER

StackOverflow: What’s the difference between Event Listeners & Handlers in Java?

I googled for Listener vs Handler because the terms seemed interchangeable. I wasn’t the only one who thought so, and the top result explains that they’re basically interchangeable, except that listener may indicate an object subscriber to a source, using the Observer pattern. I haven’t learned that, and I’m just going to tuck that aside as irrelevant right now.

1.1.4.9 BLUEPRINTS FOR MIXINS, CONTINUED…

This is the example code, but I’m still getting really stuck—to the point that I’m not sure that understanding how it works is necessary for the larger point. That may be just an excuse. But this article is basic enough that code like this is totally beyond the sort of things it breaks down with detailed explanation in other sections. I’m moving on.

var eventEmitter = {
  // so you could use this like eventEmitter.on('goToSleep', '#button') ?
  on: function (event, listener) {
    // if typeof event is not undefined (it could be string or boolean, for instance)
    if (typeof this[event] !== 'undefined')

      this[event].push(listener);
     else this[event] = [
      listener
    ];
  },
  emit: function (event) {
    if (typeof this[event] !== 'undefined') {
      var listeners = this[event];
      var length = listeners.length,
      index = length;
      var args = Array.prototype.slice.call(arguments, 1);
      while (index) {
        var listener = listeners[length - (index--)];
        listener.apply(this, args);
      }
    }
  }
};

1.1.4.10 FIXING THE INSTANCEOF OPERATOR

Aadit M Shah – Why Prototypal Inheritance Matters

This has been a real nice try, but I can’t make it through the article—every example is just chock full of things I don’t understand the functionality of. The real question is now whether I understand the original sentences I was trying to understand in the first place.

2 PROTOTYPE DESIGN PATTERN, CONTINUED

“Devan Patel – 4 JavaScript Design Patterns You Should Know”

“Any JavaScript developer has either seen the keyword prototype, confused by the prototypical inheritance, or implemented prototypes in their code. The Prototype design pattern relies on the JavaScript prototypical inheritance. The prototype model is used mainly for creating objects in performance-intensive situations.”

Got it.

“The objects created are clones (shallow clones) of the original object that are passed around. One use case of the prototype pattern is performing an extensive database operation to create an object used for other parts of the application. If another process needs to use this object, instead of having to perform this substantial database operation, it would be advantageous to clone the previously created object.”

So, a nice use for the prototype pattern is let’s say you need to create an object that requires a large database operation, so it takes a lot of work for the script to run. And let’s say you need to create a second object just like it. It’d be faster to just clone the first object than it would to run the whole operation again.

Unfortunately, I don’t see how this example would achieve anything like that. I don’t see any cloning, etc.:

var TeslaModelS = function() {
  this.numWheels    = 4;
  this.manufacturer = 'Tesla';
  this.make         = 'Model S';
}

TeslaModelS.prototype = {
  go: function() {
    // Rotate wheels
  },
  stop: function() {
    // Apply brake pads
  }
}

So, onward to try and find something that can explain things better. Or at all.

Addy Osmani: Learning JavaScript Design Patterns

“[The Prototype pattern] creates objects based on a template of an existing object through cloning.”

EASY.

It’s worth mentioning that there’s no “template” object—it’s just that we’re creating copies of some existing object. There’s a very good chance that this is the pattern:

var cat = {
  noise: "Meow"
}

var catty = Object.create(cat);
catty.noise // meow

I guess that’s it then.

3 MY CODE: FROM ‘NEW’ TO OBJECT.CREATE

The takeaway for me here is to first do away with all object creation via new. So here’s what we’ve got:
[I pasted the wrong thing and now it’s lost]

Becomes:

  var randomDate = {
    year: createNumber(6, 2010),
    month: createNumber(12, 1),
    day: createNumber(29, 1)
  }

And this:

var source = new RandomDate()
var date = new Date(source.year, source.month, source.day)

Becomes:

var source = Object.create(randomDate)
var date = new Date(source.year, source.month, source.day)

The problem here is that I get the same date random every single time, instead of a whole bunch of random dates. My guess is that when the browser reads var randomDate the first time, it runs the createNumber, assigns the results to the year, month, and day vars, and then those are the numbers that it uses going forward—rather than when I call new on it, how it runs the createNumber functions each time.

So, first I wonder if I wrap each one in a function if that’ll change it.

var randomDate = {
year: function(){
return createNumber(6, 2010)
    },
    month: function(){
        return createNumber(12, 1)
    },
    day: function(){
        return createNumber(29, 1)
    }
}

var source = Object.create(randomDate)
var date = new Date(source.year(), source.month(), source.day())

Okay, so that works. Of course, there’s another new down there on that last line, but it’s creating a new date object, and since that’s built-in to JavaScript, I don’t want to touch it. Next:

function Visit(id) {
    this.id = id;
    this.date;
    this.indication;
    this.pain;
  }

// …

var visit = new Visit(i);

Becomes…

  var visitBlueprint = {
  id: function (id) {
    return id;
  }
}

var visit = Object.create(visitBlueprint);
visit.id = visit.id(i);

And similarly,

var followup = Object.create(visitBlueprint);
followup.id = followup.id(i + ((indexPain) / 10

Now, getting more complicated, because this one using something new I’d learned called chaining:

for (var i=0; i < products.length; i++){
        new Product(products[i])
            .addDraggableAttribute()
            .addDragstartListener();
    }

// …

var Product = function(product){
        this.addDraggableAttribute = function() {
            product.setAttribute("draggable","true");
            return this;
        }

        this.addDragstartListener = function() {
            product.addEventListener("dragstart", function(ev){ 
                new DragstartEvent(ev,product)
                    .setEffectAllowedToCopy()
                    .setDataToTextFromId()
            }, false);
            return this;
        }
    }

Becomes

for (var i=0; i < products.length; i++){     
        Object.create(productBlueprint)
            .addDraggableAttribute(products[i])
            .addDragstartListener(products[i]);
    }
// …
var productBlueprint = {
  addDraggableAttribute: function (product) {
    product.setAttribute('draggable', 'true');
    return this;
  },
  addDragstartListener: function (product) {
    product.addEventListener('dragstart', function (ev) {
      new DragstartEvent(ev, product).setEffectAllowedToCopy().setDataToTextFromId()
    }, false);
    return this;
  }
}

K, that works…and here’s the next one:

var productBlueprint = {
  addDraggableAttribute: function (product) {
    product.setAttribute('draggable', 'true');
    return this;
  },
  addDragstartListener: function (product) {
    product.addEventListener('dragstart', function (ev) {
      new DragstartEvent(ev, product).setEffectAllowedToCopy().setDataToTextFromId()
    }, false);
    return this;
  }
}

// …

var DragstartEvent = function (event, product) {
  this.setEffectAllowedToCopy = function () {
    event.dataTransfer.effectAllowed = 'copy';
    return this;
  }
  this.setDataToTextFromId = function () {
    event.dataTransfer.setData('Text', product.getAttribute('id'));
    return this;
  }
}

BECOMES

var productBlueprint = {
  addDraggableAttribute: function (product) {
    product.setAttribute('draggable', 'true');
    return this;
  },
  addDragstartListener: function (product) {
    product.addEventListener('dragstart', function (ev) {
      Object.create(dragstartEvent).setEffectAllowedToCopy(ev).setDataToTextFromId(ev, product)
    }, false);
    return this;
  }
}

// ...

var dragstartEvent = {
  setEffectAllowedToCopy: function (event) {
    event.dataTransfer.effectAllowed = 'copy';
    return this;
  },
  setDataToTextFromId: function (event, product) {
    event.dataTransfer.setData('Text', product.getAttribute('id')); // we're transferring the item's id as "Text"
    return this;
  }
}

Finally, I’m a bit scared of this one: (142)

var updateCurrentUserStorage = function (name) {
  localStorage.setItem('user', JSON.stringify(new User(name)));
  updateCurrentUserUI(name);
}

// … (32)

function User(name) {
  this.name = name
}

So, they become:

var updateCurrentUserStorage = function (name) {
  var newUser = Object.create(userBlueprint)
  newUser.name = newUser.name(name);
  localStorage.setItem('user', JSON.stringify(newUser));

  updateCurrentUserUI(name);
}
// …

var userBlueprint = {
  name: function (name) {
    return name
  }
}

4 MY CODE: FROM OBJECT.CREATE TO CREATE

I guess this is the part where the goal is to switch from saying Object.create(something) to something.create() – presumably for simplicity, and also for the sake of being able to extend later.

var randomDate = {
year: function(){
return createNumber(6, 2010)
    },
    month: function(){
        return createNumber(12, 1)
    },
    day: function(){
        return createNumber(29, 1)
    }
}
//…
  function createInitialVisitDate() {
    var source = Object.create(randomDate)
    var date = new Date(source.year(), source.month(), source.day())
    return date
  };

BECOMES

var randomDate = {
    create: function(){
        var self = Object.create(this);
        self.year = createNumber(6, 2010);
        self.month = createNumber(12, 1);
        self.day = createNumber(29, 1);
        se
        return self;
    }
  }
//…
  function createInitialVisitDate() {
    var source = randomDate.create();
    var date = new Date(source.year, source.month, source.day);
    return date
  };

And look, I can even make it more concise than that:

var randomDate = {
    create: function(){
        var self = Object.create(this);
        self.year = createNumber(6, 2010);
        self.month = createNumber(12, 1);
        self.day = createNumber(29, 1);
        self.date = new Date(self.year, self.month, self.day)
        return self.date;
    }
  }

  function createInitialVisitDate() {
    return randomDate.create();
  };


And now this section:

var visitBlueprint = {
    id: function(id){
        return id;
    }
  }
//…
var visit = Object.create(visitBlueprint);
      visit.id = visit.id(i);
//…
var followup = Object.create(visitBlueprint);
followup.id = followup.id(i + ((indexPain) / 10))

BECOMES

var visitBlueprint = {
    create: function(id){
        var self = Object.create(this);
        self.id = id;

        return self;
    }
  }
//…
var visit = visitBlueprint.create(i);
//…
var followup = visitBlueprint.create(i + ((indexPain) / 10));

And also, I think I can move these into the create function:

visit.date = createInitialVisitDate();
      visit.indication = createIndication();
      visit.pain = createNumber(10, 1);

So now the function is:

var visitBlueprint = {
    create: function(id){
        var self = Object.create(this);
        self.id = id;
        self.date = createInitialVisitDate();
        self.indication = createIndication();
        self.pain = createNumber(10, 1);

        return self;
    }
  }

And we can remove that from where it just clutters things up in the build function.

var userBlueprint = {

    name: function(name){
        return name
    }
}
//…
var newUser = Object.create(userBlueprint)
newUser.name = newUser.name(name);

BECOMES

var userBlueprint = {
    create: function(name){
        self = Object.create(this);
        self.name = name;
        return self;
    }
}
// …
var newUser = userBlueprint.create(name);

NEXT

var dragstartEvent = {
        create.
        setEffectAllowedToCopy: function(event) {
            event.dataTransfer.effectAllowed = 'copy';
            return this;
        },
        setDataToTextFromId: function(event,product) {
            event.dataTransfer.setData("Text", product.getAttribute("id")); // we're transferring the item's id as "Text"
            return this;
        }
    }
// …
Object.create(dragstartEvent)
                    .setEffectAllowedToCopy(ev)
                    .setDataToTextFromId(ev,product)

BECOMES

var dragstartEvent = {
        create: function(){
            self = Object.create(this);
            return self;
        },
        setEffectAllowedToCopy: function(event) {
            event.dataTransfer.effectAllowed = 'copy';
            return this;
        },
        setDataToTextFromId: function(event,product) {
            event.dataTransfer.setData("Text", product.getAttribute("id")); // we're transferring the item's id as "Text"
            return this;
        }
    }
//…
dragstartEvent.create()
                    .setEffectAllowedToCopy(ev)
                    .setDataToTextFromId(ev,product)

NEXT

Object.create(productBlueprint)
//…

becomes

productBlueprint.create()

and of course inside product blueprint…

var productBlueprint = {
        create: function(){
            self = Object.create(this);
            return self;
        },

5 ONE LAST THING: BREAKING JQUERY

Extending the Object.prototype will break jQuery.

StackOverflow: Prototyping Object in JavaScript Breaks jQuery

So, Object.prototype.extend will not work if you’re also using jQuery.

// create new object, and extend
Object.prototype.extend = function(){
    var hasOwnProperty = Object.hasOwnProperty;
    var object = Object.create(this);
    var length = arguments.length;
    var index = length;
    while (index) {
        var extension = arguments[length - (index--)];
        for (var property in extension){
            if ( hasOwnProperty.call(extension, property) || (typeof object[property] === 'undefined') ){
                object[property] = extension[property]
            }
        }
    }

    return object;
};
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s