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 SubscribeIn RepLogApp
, when we try to call this.helper.calculateTotalWeight
, for some reason, it doesn't think this is a function!
... 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); |
But down below, we can plainly see: calculateTotalWeight
is a function! What the heck is going on?
To find out, in initialize
, let's log a few things: console.log(this.helper)
and then Object.keys(this.helper)
:
... lines 1 - 2 | |
(function(window, $) { | |
window.RepLogApp = { | |
initialize: function ($wrapper) { | |
... line 6 | |
this.helper = new Helper(this.$wrapper); | |
console.log(this.helper, Object.keys(this.helper)); | |
... lines 9 - 18 | |
}, | |
... lines 20 - 54 | |
}; | |
... lines 56 - 71 | |
})(window, jQuery); |
The Object.keys
method is an easy way to print the properties and methods inside an object.
Do the same thing for Helper
and Object.keys(Helper)
:
... lines 1 - 2 | |
(function(window, $) { | |
window.RepLogApp = { | |
initialize: function ($wrapper) { | |
... line 6 | |
this.helper = new Helper(this.$wrapper); | |
console.log(this.helper, Object.keys(this.helper)); | |
console.log(Helper, Object.keys(Helper)); | |
... lines 10 - 18 | |
}, | |
... lines 20 - 54 | |
}; | |
... lines 56 - 71 | |
})(window, jQuery); |
Let's look at what the difference is between our instance of the Helper
object and the Helper
object itself.
Ok, find your browser, refresh, and check this out! There's the helper
instance object, but check out the methods and properties on it: it has $wrapper
. Wait, so when we create a new Helper()
, that instance object does have the $wrapper
property... but somehow it does not have a calculateTotalWeight
method!
That's why we're getting the error. The question is why? Below, where we printed the upper-case "H" Helper
object, it prints out as a function, but in its keys, it does have one called calculateTotalWeight
. Oooh, mystery!
This can be very confusing. So follow this next part closely and all the way to the end.
At this point, the calculateTotalWeight
function is effectively still static. The only way that we can call that method is by saying Helper.calculateTotalWeight
- by calling the method on the original, static object. We cannot call this method on the instantiated instance: we can't say this.helper.calculateTotalWeight()
. It just doesn't work!
To fix this, instead of adding the method via Helper.calculateTotalWeight
, we need to say Helper.prototype.calculateTotalWeight
:
... lines 1 - 2 | |
(function(window, $) { | |
... lines 4 - 63 | |
Helper.prototype.calculateTotalWeight = function() { | |
... lines 65 - 70 | |
}; | |
})(window, jQuery); |
That weird little trick fixes everything. To test it easily, back up in initialize()
, let's try calling this.helper.calculateTotalWeight()
:
... lines 1 - 2 | |
(function(window, $) { | |
window.RepLogApp = { | |
initialize: function ($wrapper) { | |
... line 6 | |
this.helper = new Helper(this.$wrapper); | |
console.log(this.helper, Object.keys(this.helper)); | |
console.log(Helper, Object.keys(Helper)); | |
console.log(this.helper.calculateTotalWeight()); | |
... lines 11 - 19 | |
}, | |
... lines 21 - 55 | |
}; | |
... lines 57 - 72 | |
})(window, jQuery); |
This did not work before, but refresh! 157.5 - it works now!
The short explanation is that when you create objects that need to be instantiated, you need to add its properties and methods to this special prototype
key.
Once you've done that and create a new Helper
, magically, anything on the prototype, like calculateTotalWeight
, becomes part of that object.
But, that superficial explanation is crap! Let's find out how this really works!
I did a check -
`
var Helper = function ($text) {
this.$text = $text;
console.log(this.$text); // --> return : Object { 0: p.text, length: 1, prevObject: Object(1) }
};
BUT
Helper.prototype.btnAppear = function () {
this.$text.fadeToggle(); // --> return : TypeError: this.$text is undefined
};
`
Hmm, so this.$text
inside of Helper.btnAppear
is undefined? It makes no sense. What happens if you do this
var Helper = function ($text) {
this.$text = 'hi';
};
Helper.prototype.btnAppear = function () {
console.log(this.$text);
};
Does it still says undefined? If that's the case I'm not sure what's going on, probably the browser where you are running your JS does not support the prototype
? Anyways, if you can't find the solution just declare the "btnAppear" function inside the Helper constructor
var Helper = function ($text) {
...
this.btnAppear = function() {
// do your thing.
}
};
Cheers!
OMG, that must be the clearest explanation of JS prototypes I've ever seen! You're the best! (as always)
// 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
}
}
Hi, I have a problem using my Helper.prototype.btnAppear function -> "TypeError: this.$text is undefined" .
window.Appear = {
initialize:function () {
this.$btn = $('.btn');
this.$text = $('.text');
this.helper = new Helper(this.$text);
this.$btn.on('click', this.helper.btnAppear);
},
};
var Helper = function ($text) {
this.$text = $text;
};
Helper.prototype.btnAppear = function () {
this.$text.fadeToggle(); // --> this.$text create an error, when the function is called by the Appear object event
};
Could someone please tell me if something is wrong here ?
Thank's a lot!