If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOk ok, it's finally time to talk about the JavaScript elephant in the room: prototypical inheritance. This means, real JavaScript objects that we can instantiate!
But first, let's do just a little bit of reorganization on Helper
- it'll make our next step easier to understand.
Instead of putting all of my functions directly inside my object immediately, I'll just say var Helper = {}
:
... lines 1 - 2 | |
(function(window, $) { | |
... lines 4 - 54 | |
/** | |
* A "private" object | |
*/ | |
var Helper = {}; | |
... lines 59 - 71 | |
})(window, jQuery); |
Then set the Helper.initialize
key to a function, and Helper.calculateTotalWeight
equal to its function:
... lines 1 - 2 | |
(function(window, $) { | |
... lines 4 - 54 | |
/** | |
* A "private" object | |
*/ | |
var Helper = {}; | |
Helper.initialize = function ($wrapper) { | |
... line 61 | |
}; | |
Helper.calculateTotalWeight = function() { | |
... lines 64 - 69 | |
}; | |
})(window, jQuery); |
This didn't change anything: it's just a different way of putting keys onto an object.
Ok, in JavaScript, everything is an object, and this is quite different than PHP. Obviously, Helper
is an object. But we already saw earlier that functions are also objects. This means when we say this.handleRepLogDelete
- which references a function - we can call some method on it called bind()
.
Heck, even strings are objects: we'll see that in a moment. The only downside with our Helper
or RepLogApp
objects so far is that they are effectively static.
Why? Because, there can only ever be one Helper
object. If I had two areas on my page, and I wanted to calculate the total weight in each, we'd be in trouble! If we called initialize()
a second time for the second area, it would override the original $wrapper
property. It acts like a static object. And that's what we need to fix: I want to be able to instantiate objects... just like we do in PHP with the new
keyword. This will let us create two Helper instances, each with their own $wrapper
property.
How do we do that? Instead of setting Helper
to {}
, set it to a function. Let's set Helper
to what was our initialize()
method:
... lines 1 - 2 | |
(function(window, $) { | |
... lines 4 - 54 | |
/** | |
* A "private" object | |
*/ | |
var Helper = function ($wrapper) { | |
this.$wrapper = $wrapper; | |
}; | |
... lines 61 - 69 | |
})(window, jQuery); |
Huh. So now, Helper
is a function... But remember that functions are objects, so it's totally valid to add properties or methods to it.
Why would set our object to a function? Because now we are allowed to say this.helper = new Helper($wrapper)
:
... lines 1 - 2 | |
(function(window, $) { | |
window.RepLogApp = { | |
initialize: function ($wrapper) { | |
this.$wrapper = $wrapper; | |
this.helper = new Helper(this.$wrapper); | |
... lines 8 - 16 | |
}, | |
... lines 18 - 52 | |
}; | |
... lines 54 - 69 | |
})(window, jQuery); |
JavaScript does have the new keyword just like PHP! And you can use it once Helper
is actually a function. This returns a new instance of Helper
, which we set on a property.
In PHP, when you say new Helper()
, PHP calls the constructor on your object, if you have one. The same happens here, the function is the constructor. At this point, we could create multiple Helper instances, each with their own $wrapper
.
Now, instead of using Helper
in a static kind of way, we use its instance: this.helper
:
... lines 1 - 2 | |
(function(window, $) { | |
window.RepLogApp = { | |
... lines 5 - 17 | |
updateTotalWeightLifted: function () { | |
this.$wrapper.find('.js-total-weight').html( | |
this.helper.calculateTotalWeight() | |
); | |
}, | |
... lines 23 - 52 | |
}; | |
... lines 54 - 69 | |
})(window, jQuery); |
Before we keep celebrating, let's try this. Go back, refresh, and delete one of our items! Huh, it worked... but the total didn't update. And, we have an error:
Uncaught TypeError: this.helper.calculateTotalWeight is not a function
That's odd! Why does it think our Helper doesn't have that key? The answer is all about the prototype.
Hey Abdul,
Haha, yeah, a tricky question :) Actually, it won't make any difference, but since we set that variable on a property, it makes sense to use the property further to be consistent.
Cheers!
Why You didn't add calculateTotalWeight function inside the constructor?
like:
var Helper = function($wrapper) {
this.$wrapper = $wrapper
this.calculateTotalWeight = function(){
var totalWeight = 0;
this.$wrapper.find('tbody tr').each(function() {
totalWeight += $(this).data('weight');
});
return totalWeight;
};
};
Hey Andrew!
In this case is not needed, because at this point we only update total weight whenever a row is deleted or added not when initializing the application.
Have a nice day
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.1.*", // v3.1.10
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.1
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
"symfony/swiftmailer-bundle": "^2.3", // v2.4.0
"symfony/monolog-bundle": "^2.8", // 2.12.0
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.1
"symfony/phpunit-bridge": "^3.0" // v3.1.6
}
}
just curious if we passed ($wrapper) instead of (this.$wrapper) in
this.helper = new Helper(this.$wrapper);
would it make any difference.