Consider an implementation for a LinkedList or a Tree. You might not want to expose next, last, children, or parent as a property, but instead make it a property accessor that checks a static WeakMap for the existence of such relationships. This way, your implementation allows the relationships to maintain weak connections.
HTML elements are a good way of explaining this. Let's say you partially implement an HTML element:
const HTMLElement = (() => {
  const children = new WeakMap()
  class Node {
    constructor () {
      children.set(this, [])
    }
    get children () {
      return children.get(this)
    }
    appendChild (element) {
      children.get(this).push(element)
      return element
    }
    removeChild (element) {
      const elements = children.get(this)
      elements.splice(elements.indexOf(element), 1)
      return element
    }
  }
  return class HTMLElement extends Node {
    constructor (tag) {
      super()
      this.tagName = tag.toUpperCase()
    }
    toString () {
      return `<${this.tagName}>${children.get(this).join('')}</${this.tagName}>`
    }
  }
})()
{
  const p = new HTMLElement('p')
  p.appendChild(new HTMLElement('a'))
  p.appendChild(new HTMLElement('span'))
  console.log(`${p}`)
}
// a and span get GC'd when reference to p is lost
console.log(typeof p)
 
 
If children was a Map, you'd have a memory leak when the reference to p is lost, because the others would still have strong references since HTMLElement is still accessible.