When writing software, code reuse is the practice of writing generalized code that can be relied upon to address a variety of similar goals. Whenever you notice that two parts of your code have similarities, there’s an opportunity to factor out similar aspects of it into some reusable library code to avoid writing repeating codes.

Let’s assume that we need to create a program for keeping track of how far along the street each car is and the name of one of the drivers is “Amy.” (* FYI: What I explain in this blog post is a ridiculously oversimplified version of the program). To keep track of how far along the street Amy’s car is, her object could include a location property and that property will point to a number reflecting her position. (The number is an object as well just like nearly every value in JavaScript.)

[run.js]
var amy = { location : 1 };

When the program tries to move Amy’s car further along the street, we could show the change on the next line.

[run.js]
var amy = { location : 1 };
amy.location ++;

Also, let’s create a second car object.

[run.js]
var amy = { location : 1 };
amy.location ++;
var ben = { location : 9 };
ben.location ++;

In the code above, the .location property is repeated. So, we could write a function that does the work for us.

[library code] 
var move = function(car) { car.location ++ };
[run.js]
var amy = { location : 1 };
move(amy);
var ben = { location : 9 };
move(ben);

The reason that it’s valuable to factor a repeated code into a function is that you can make all edits you want to apply in one place. Without a function, you’d be forced to edit all places that old logic exist and could easily miss editing one of the many places you repeated the old logic.

Also, the code that adds a .location property to a new car object is repeated.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
return obj;
};

var move = function(car) { car.location ++ };
[run.js]
var amy = carlike({ }, 1);
move(amy);
var ben = carlike({ }, 9);
move(ben);

When a function accepts an object and augments it with some extra properties, the function is called a decorator. It’s common to use adjectives as names for decorators.

We can rename our decorators “move(amy) & move(ben)” to clearly shows that the decorators have the move capability is an aspect of each car by using a “method calling syntax” instead of a “function calling syntax.”

[library code] 
var carlike = function(obj, location) {
obj.location = location;
return obj;
};
var move = function(car) { car.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

When a function is designed to operate on a specific object, “method calling syntax” allows us to express the object’s significance in that function in a more effective way. To provide an access to this function via “dot notation” on Amy & Ben, we’ll add a move property to each car as they’re being built and assign the function to that property.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function(car) { car.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

We’re no longer planning on passing a car in as an argument to this function. Instead, we’ll see the target object appearing to the left of the dot when the function is called. JavsScript supports the automatically-bound this argument, which refers to the object on the left of the call-time dot. Rather than providing an input variable for the argument, we can use this.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function( ) { this.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

[A quick recap of this] : this behaves almost exactly like a parameter. Rather than being bound to a value supplied in between call-time parentheses, this is bound to the object on the left of the call-time dot.

[example.js]
var obj1 = {}, obj2 = {}
obj1.example = function(arg1) {
console.log(this, arg1);
};
obj1.example(obj2); // obj1, obj2
/* obj1.example(obj2); */
obj1 // this parameter binding
. // call-time dot
() // call-time parentheses
obj2 // 1st parameter binding

In this code, it is like the following:

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function( ) { this.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();
/* amy.move(); */
amy // this parameter binding
. // call-time dot
() // call-time parentheses
// no positional parameter binding

We’ve now made this function a method of every car-like object. In addition to clearer invocation syntax, the method provides “encapsulation” of behavior. But, we’re no longer planning on passing a car in as an argument to this function. Instead, we’ll see the target object appearing to the left of the dot when the function is called. That’s good for conserving memory. But, there’s a downside. Whenever you use a “shared function” like this, you can’t reply on it always knowing which object to use as its this argument.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function( ) { this.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();
setTimeout(ben.move, 1000);
// runs with the this argument bound to the global object, where there is no global .location property.

It works just like passing the original move function would have (before we refactored move to be a method). Like car, this is a parameter, and setTimeout has no way of knowing which value to pass in.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function(car) { car.location ++ };
[run.js]
var amy = carlike({ }, 1);
move(amy);
var ben = carlike({ }, 9);
move(ben);
setTimeout(ben.move, 1000);
// runs with the "car" argument bound to undefined, where there is no global .location property.

Let’s move the method’s code directly into the decorator function and remove the global variable declaration entirely.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { this.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

Our new code feels easier to read than before when move was defined outside the car-like function. All the code is on one level of indentation and we’re using the same syntax to assign the .location & .move properties. But, even though our code is all together in one place, we’ve paid a price for that simplicity. In the new code, every time we run the car-like function, we create a new function to add as the .move property of the input object.

COPYRIGHT © 2018 CodeStates

The old version of the code created only one function object for all car-like objects to point their .move property to. In addition to avoiding duplicated code, this pattern avoided duplicating in-memory function objects.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = move;
return obj;
};
var move = function( ) { this.location ++ };
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();
COPYRIGHT © 2018 CodeStates

People are often confused why this code would bring a new function into existence for every call of “car-like.” You can improve your intuition around this point by examining a more obvious situation that is completely equivalent.

[maker.js]
var makeAnObject = function() {
return { example : 'property' };
}

This simple “maker function” builds & returns new object.

[maker.js]
var makeAnObject = function() {
return { example : 'property' };
}
var obj1 = makeAnObject();
var obj2 = makeAnObject();
log(ob1 === obj2);

After calling it twice & storing the results, would you expect the two objects it produces to be different objects or the exact same object in memory? In other words, would they pass a strict comparison of identity?

log(obj1 === obj2); // false

Even though the same line of code generated both objects, they are different objects taking up different spaces in memory. Making changes to one will not affect the other.

[maker.js]
var makeAnObject = function() {
return (){};
}
var obj1 = makeAnObject();
var obj2 = makeAnObject();
log(ob1 === obj2);

What if the maker function had returned a function object instead of a plain object? Would you expect the two results to be the exact same object, or would they be two different objects?

log(obj1 === obj2); // false

Just like the previous version of the code, which generated two different plain objects, this new code will generate different function objects every time makeAnObject gets run.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { this.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

So here, every time the interpreter visits the middle line of the car-like decorator, a new function will be generated in regard of the car’s .move method. Depending on how many cars we make, this could take a lot of memory.

But, there is a slight advantage to putting the .move function inside the body of the car-like decorator function. Because a new .move function is being created every time, each with an access to a unique closure scope, we don’t need to reply on the keyword this anymore.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { obj.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

Instead of referring to this argument, which gets bounded to a new value whenever .move is invoked, we could refer to the closure variable obj, which will always refer to one particular car object.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { obj.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();
setTimeout(ben.move, 1000);
// runs with the obj variable bound to the ben object

In addition to avoiding all confusion with this argument, now our .move method can never lose binding to their target object when passed as callbacks or called in other ways.

[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { this.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();
setTimeout(ben.move, 1000);
// runs with the this variable bound to the global object, where there is no global .location property.
COPYRIGHT © 2018 CodeStates
[library code] 
var carlike = function(obj, location) {
obj.location = location;
obj.move = function( ) { obj.location ++ };
return obj;
};
[run.js]
var amy = carlike({ }, 1);
amy.move();
var ben = carlike({ }, 9);
ben.move();

[Conclusion] : The example explained in this blog post is so simple that we’re passing an empty object. Generally, you’d use a decorator to add some functionality to an object that had some other functionality in it already.

[Note] This blog post is written based on a lecture from CodeStates.

Thanks for reading! 💕 If you like this blog post, please clap👏

--

--