I've found folloing sample in which As if Service
class has prototype
property.
①Service is not instansiated, why Service = new Service()
didn't need ?
②console.log(Service.prototype
returned {}
,what is this object ? What is the merit to manipulate this?
beforeEach(async ()=>{
jest.spyOn(Service.prototype, 'execute')
})
I am totally novice to this concept, if someone has opinion will you please let me know. Thanks
CodePudding user response:
What this does is that whenever .execute(...)
is called on any Service
instance, your spying thingy would fire.
It doesn't need new Service()
because it's not working on a single service instance, it is working on the class itself.
It works by replacing Service.prototype.execute
with a wrapper function that logs the call and then calls the original function, something like this:
const oldExecute = Service.prototype.execute
Service.prototype.execute = function (...args) {
console.log('execute called!', args)
return oldExecute.call(this, ...args)
}
Class methods are nothing other than properties on the class' prototype, so if you have class Test { hello (x) { return x * 2 } }
and you do console.log(Test.prototype.hello)
, it will log the method hello (x) { return x * 2 }
, and if you do testInstance.hello(123)
it essentially then calls Test.prototype.hello.call(testInstance, 123)
, i.e. it calls the function Test.prototype.hello
with testInstance
as this
.
And the {}
that you see is not everything there is, because the methods are by default non-enumerable. You should look at it in a debugger that also shows non-enumerable properties. Then you will see the class' methods there:
This is also the reason why when referring to the "full name" of a class method, often ClassName.prototype.methodName
is used, because that's actually how you would get that method itself without referring to a particular class instance (to make use of it, you would have to call it with its this
set to some instance, though, like what I did with .call
above). For example, the slice
function that exists on Array
(when you write [1, 2, 3].slice(1)
for instance) is referred to as Array.prototype.slice
(in other languages this would be called Array::slice
, Array#slice
or the like). Sometimes you will also see people talking about Array.slice
but that's technically not correct because slice
is not a static method and doesn't exists on Array
itself (unlike, for example, Array.from
).
Further reading:
- MDN: Object prototypes
- MDN: Classes in JavaScript
- MDN: Docs for
Function.prototype.call
(to understand the examples) - Jest docs for
spyOn