I'd like to avoid memory leaks, so I'm wondering if $watches are automatically removed when a scope is $destroy'd, or what should I do to clean up?
As $destroy() docs says it:
Removal implies that calls to $digest() will no longer propagate to the current scope and its children.
This means that your $watches are not going to be run anymore, once your scope is $destroyed, because they can only be processed by the $digest cycle, which is cancelled when the scope is $destroyed.