Buy Access to Course
09.

Static Class Methods

Share this awesome video!

|

Keep on Learning!

Go back and look at the new get _selectors() method:

208 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
// ... lines 5 - 30
get _selectors() {
return {
newRepForm: '.js-new-rep-log-form'
}
}
// ... lines 36 - 175
}
// ... lines 177 - 206
})(window, jQuery, Routing, swal);

Interesting: PhpStorm is highlighting it like something is wrong! If you hover over it, it says:

Method can be static.

In the first episode, we talked about how when you add your methods to the prototype, it's like creating non-static methods on PHP classes:

Greeter = function (greeting) {
    this.greeting = greeting;
}

Greeter.prototype.sayHi = function () {
    console.log(this.greeting);
}

In other words, when you create new instances of your object, each method has access to its own instance properties:

greeter = new Greeter('YO!');
greeter.sayHi(); // YO!

I also said that if you decided not to put a method on the prototype, that is legal, but it effectively becomes static:

Greeter = function (greeting) {
    // ...
}

Greeter.sayHi = function () {
    console.log('YO!');
}

Greeter.sayHi(); // YO!

If that didn't make a lot of sense then, it's okay. Because with the new class syntax, it's much easier to think about!

PhpStorm is suggesting that this method could be static for one simple reason: the method doesn't use the this variable. That's the same as in PHP: if a method doesn't use the this variable, it could be made static if we wanted.

It's probably fine either way, but let's make this static! Add static before get _selectors():

208 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
// ... lines 5 - 30
static get _selectors() {
return {
newRepForm: '.js-new-rep-log-form'
}
}
// ... lines 36 - 175
}
// ... lines 177 - 206
})(window, jQuery, Routing, swal);

And as soon as we do that, we can't say this._selectors anymore. Instead, we need to say RepLogApp._selectors:

208 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
// ... lines 5 - 134
_mapErrorsToForm(errorData) {
// ... line 136
const $form = this.$wrapper.find(RepLogApp._selectors.newRepForm);
// ... lines 138 - 151
}
// ... lines 153 - 175
}
// ... lines 177 - 206
})(window, jQuery, Routing, swal);

And that makes sense: in PHP, we do the same thing: we use the class name to reference items statically. Let's change that in a few other places:

208 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
class RepLogApp {
constructor($wrapper) {
// ... lines 6 - 20
this.$wrapper.on(
'submit',
RepLogApp._selectors.newRepForm,
this.handleNewFormSubmit.bind(this)
);
}
// ... lines 27 - 134
_mapErrorsToForm(errorData) {
// ... line 136
const $form = this.$wrapper.find(RepLogApp._selectors.newRepForm);
// ... lines 138 - 151
}
_removeFormErrors() {
const $form = this.$wrapper.find(RepLogApp._selectors.newRepForm);
// ... lines 156 - 157
}
_clearForm() {
// ... lines 161 - 162
const $form = this.$wrapper.find(RepLogApp._selectors.newRepForm);
// ... line 164
}
// ... lines 166 - 175
}
// ... lines 177 - 206
})(window, jQuery, Routing, swal);

Perfect!

Time to try things! Refresh! Yes! No errors!

Let's see one more example: scroll all the way down to the Helper class. Create a new method: static _calculateWeight() with an $elements argument:

214 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
// ... lines 4 - 177
/**
* A "private" object
*/
class Helper {
// ... lines 182 - 201
static _calculateWeights($elements) {
// ... lines 203 - 208
}
}
// ... lines 211 - 212
})(window, jQuery, Routing, swal);

This will be a new static utility method whose job is to loop over whatever elements I pass, look for their weight data attribute, and then return the total weight:

214 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
// ... lines 4 - 177
/**
* A "private" object
*/
class Helper {
// ... lines 182 - 201
static _calculateWeights($elements) {
let totalWeight = 0;
$elements.each((index, element) => {
totalWeight += $(element).data('weight');
});
return totalWeight;
}
}
// ... lines 211 - 212
})(window, jQuery, Routing, swal);

We don't really need to make this change, but it's valid.

Now, in calculateTotalWeight(), just say: return Helper - because we need to reference the static method by its class name Helper._calculateTotalWeight() and pass it the elements: this.$wrapper.find('tbody tr'):

214 lines | web/assets/js/RepLogApp.js
// ... lines 1 - 2
(function(window, $, Routing, swal) {
// ... lines 4 - 177
/**
* A "private" object
*/
class Helper {
// ... lines 182 - 185
calculateTotalWeight() {
return Helper._calculateWeights(
this.$wrapper.find('tbody tr')
);
}
// ... lines 191 - 209
}
// ... lines 211 - 212
})(window, jQuery, Routing, swal);

Coolio! Try that out! And we still see the correct total.

And that is 10 times easier to understand as a PHP developer! Sure, JavaScript still has prototypical inheritance behind the scenes... but most of the time, we won't know or care.