I'm looking for a detailed answer on this.
What I already Know
So I have some understanding about the call stack and callbacks, and that the browser add functionality through web APIs which add callbacks through the event loop. I also read somewhere about the JS engine having an API.
What I want to understand
Thanks!
From a (C++ etc.) application development perspective, JavaScript engines are embeddable libraries; and a browser is one such embedder. Any library defines a public interface through which it can be used -- its Application Programming Interface (or API for short). There is no standard for what a JS engine's API should look like; each engine defines its own, and evolves it as necessary over time. V8's is here.
The core functionality of a JS engine's API is to allow the embedder to provide objects and functions to the JavaScript environment that are backed by the embedder's own C++ implementations. Essentially, this defines a mapping, sometimes also called "bindings". For example, the embedder can say "I want there to be a document
object, and it should appear to have a property .location
that's backed by my getter function DocumentLocationGetter() {...}
, and it should (appear to) have a method .createElement()
that's backed by my other function DocumentCreateElement(...) {...}
", and so on.
And that's the answer to both of your questions: the browser exposes certain functions to JavaScript that can then be called from there. The browser decides what to do when such a function is called (e.g.: add or remove a DOM node, change a CSS property, store an event handler in some element's event handlers list, ...). Of course the browser/embedder can also call into the JS engine, for example when invoking an event handler, it can tell the engine "please execute function button1_clicked
now".
For more details, see e.g. v8.dev/docs/embed.