This code sample from "Node.js Design Patterns – Third edition: Design and implement production-grade Node.js applications using"
function patchCalculator (calculator) {
// new method
calculator.add = function () {
const addend2 = calculator.getValue()
const addend1 = calculator.getValue()
const result = addend1 + addend2
calculator.putValue(result)
return result
}
// modified method
const divideOrig = calculator.divide
calculator.divide = () => {
// additional validation logic
const divisor = calculator.peekValue()
if (divisor === 0) {
throw Error('Division by 0')
}
// if valid delegates to the subject
return divideOrig.apply(calculator)
}
return calculator
}
const calculator = new StackCalculator()
const enhancedCalculator = patchCalculator(calculator)
//
and its a section talking about Object augmentation
when discussing Object decoration
.
I am just confused on what apply
is doing. Here is my guess, the patchCalculator
is applying that new method, to the original object calculator
and enhancedCalculator
are having the same implementation? is this correct?
thanks in advance for the help, i am not sure why apply
confuses me so much and the different usages of it.
2
Answers
Yes I think you are correct. MDN page for .apply
In this case, that the .apply applies the
divideOrig
method to thecalculator
object, but just in a less redundant way than the.add
method is shown, and also can be a modification of the original.divide
function, and not have to be completely rewritten (aka, redefined from scratch, as.add
is here).Let me start of that the code:
Is very deceptive to start with. Currently it looks like
calculator
andenhancedCalculator
are two different objects. HoweverpatchCalculator()
does not create a new object, but returns the passed argument (with mutated properties).This means that the above could also be written as:
This will "update" the
calculator
variable to now be the patched variant. With this deception out of the way, lets get to the answer.apply()
is used to invoke a function with a manually providedthis
argument.Under normal circumstances
this
is automatically set to the receiver of the method call. Take for exampleperson.greet()
, in this exampleperson
is the receiver of thegreet()
method invocation and is used as thethis
argument insidegreet()
.The code might look like this:
Currently the above is a function and, when called by itself
greet()
this
will beglobalThis
orundefined
in strict mode. To use this method it has to be coupled to an object withfirstName
,lastName
andage
properties.Say the
person
object is defined like this:Now we can attach the
greet
function as one of theperson
properties and invoke it.Now say we either can’t, or don’t want to mutate the
person
object. In this scenario we can useapply()
orcall()
to call the function. Providing thethis
function argument manually, rather than it being assigned based on the receiver.Relating this to your example data we can see that:
Takes the
divide
method, and assigns it to thedivideOrig
variable.We then override the
calculator.divide
property with our new method. Within this new variant ofdivide
we calldivideOrig.apply(calculator)
to get the original result back. This calls the originalcalculator.divide()
(which is now assigned todivideOrig
), and manually setscalculator
as thethis
argument.If we where to call
divideOrig()
by itself it doesn’t have a receiver (there is no object in front of it). Meaning thatthis
would be set toglobalThis
orundefined
like discussed earlier.My guess is that the original
calculator.divide()
depends on the properties/state ofcalculator
. Therefore we need to make sure thatcalculator
is set as thethis
argument.