It's both and more.
V8 is a VirtualMachine basically which has an interpreter and multiple compilers and optimizers.
The JS code will be compiled directly into machine code. An optimizer is trying to find the most aggressive optimization(s). In each of those steps, the output gets stored in a cache. The code will execute and on failure, a previous optimized or compiled code will be executed from the cache instead. V8 can re-order steps when certain code fragments are identified and known to perform well. The compiler and optimizer are guarded by type checkers to pick to most efficient type. Int8 for small numbers for example, not the wasting doubles. Only in the case of all cache steps can't be executed and/or the types of variables can't be determined then the code gets interpreted.
Edit: JS engines like V8 (Node.js) SpiderMonkey and ChakraCore are interpreters (for compatibility reasons), compilers (JIT and AOT), optimizers. V8 and SpiderMonkey have pre-compiled machine code (AOT) to replace identified and known fractions of code with high-performance machine code after AST creation and before even pushing it to JIT compiler. Such AOT code is for loops and some expressions. Optimizers can inline functions like it's known from C compilers also done after AST and before pushing to JIT.