Static Class Methods
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
Go back and look at the new get _selectors()
method:
// ... 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()
:
// ... 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
:
// ... 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:
// ... 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:
// ... 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:
// ... 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')
:
// ... 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.
you guys need to get your video download numbering sorted ;)