All dynamic languages implement prototypes, although they call it classes. Prototypes rely on duck typing and tests for the most part. They also have co-routines which make them distinct from classes.
1. Tables for namespaces, reflection, interfaces, modules and multimethods.
2. Closures for observer callbacks, collection iteration and as a simpler template pattern.
3. Easy mixins, plugins with concatenative inheritance.
4. Co-routines for state machines and async
Because tables are such a flexible data structure you can also implement polymorphism by just associating it with closures and modify it at runtime i.e, a draw method for any shape can be added to a table of closures.
With type hints, jit, workers and shared buffers in javascript it seems like prototypes can be reasonably performant. Typescript’s gradual typing it seems you can also get static checking.