Ist die Verwendung der Anweisung "with" mit Proxies eine schlechte Praxis?


8

Zunächst möchte ich klarstellen, dass dies withveraltet ist und dass die Verwendung im Allgemeinen eine schlechte Praxis ist .

Meine Frage bezieht sich jedoch auf einen Sonderfall: Verwenden eines speziellen ProxyObjekts als Parameter von with.


Hintergrund

Ich arbeite an einem Projekt, bei dem ich den Zugriff auf einen Code auf den globalen Bereich beschränken muss.

Ein Ansatz könnte darin bestehen, eine Schleife mit zu verwenden eval, die konstante Variablen mit dem Wert von undefinedfür jede Eigenschaft des globalen Objekts erstellt. Dies scheint jedoch noch schlimmer zu sein als die Verwendung withund kann den Zugriff auf mit letund erstellte Variablen nicht einschränken const.

Die Idee

Die Idee ist, ein Proxyals Argument zu verwenden with, dessen ...

  • hasTrap kehrt immer zurück true, daher können keine Suchvorgänge oder Zuweisungen über die withAnweisung hinausgehen
  • getTrap funktionieren normal, außer dass sie ReferenceErrors auslösen, wenn sie versuchen, auf eine nicht vorhandene Variable (dh eine Eigenschaft) zuzugreifen.
  • set Trap funktionieren normal (oder enthalten möglicherweise eine benutzerdefinierte Logik)
  • targetObjekt hat keine [[Prototype]](dh es wurde mit erstellt Object.create(null))
  • targetObjekt hat eine @@unscopablesEigenschaft mit dem Wert eines leeren Objekts, um das Scoping jeder Eigenschaft zu ermöglichen

Also so etwas wie dieser Code:

const scope = Object.create(null)
Object.assign(scope, {
  undefined,
  console,
  String,
  Number,
  Boolean,
  Array,
  Object,
  /* etc. */
  [Symbol.unscopables]: Object.create(null)
})

const scopeProxy = new Proxy(scope, {
  get: (obj, prop) => {
    if (prop in obj)
      return obj[prop]
    else
      throw new ReferenceError(`${prop} is not defined`)
  },
  set: Reflect.set,
  has: () => true
})

with(scopeProxy) {
  //Sandboxed code
  
  foo = Number('42')
  console.log(foo) //42
  
  try{
    console.log(scopeProxy) //Inaccessible
  }catch(e){
    console.error(e) //ReferenceError: scopeProxy is not defined
  }
}

Kontras vermeiden

Auf der MDN-Seitewith sind mehrere Kontras zu der Anweisung aufgeführt , aber durch diese Verwendung werden sie jeweils entfernt.

1. Leistung

  • Das Problem:

    Das Nachschlagen von Bezeichnern, die nicht Mitglied des withParameterobjekts der Anweisung sind, ist weniger performant.

  • Vermeidung:

    Keine Suche kann über das Parameterobjekt hinausgehen.

2. Mehrdeutigkeit

  • Das Problem:

    Es ist schwer zu entscheiden, welcher Bezeichner von den gleichnamigen nachgeschlagen wird.

  • Vermeidung:

    Alle Suchvorgänge und Zuweisungen rufen die Eigenschaft des Parameterobjekts ab oder ändern sie.

3. Vorwärtskompatibilität

  • Das Problem:

    Die Eigenschaften der Parameterobjekte oder ihre Prototypen können sich in Zukunft ändern.

  • Vermeidung:

    Das Parameterobjekt ist anfangs leer und hat keinen Prototyp, daher können sich keine Eigenschaften ändern.

Frage

Der obige Code funktioniert einwandfrei, und die auf MDN aufgeführten Kontras gelten hierfür nicht.

Meine Frage lautet also:

Ist es immer noch eine schlechte Praxis, die withAnweisung zu verwenden, und wenn ja, welche Nachteile hat die Verwendung in diesem Fall?


Es hört sich so an, als ob Sie tatsächlich Code in einer Sandbox ausführen möchten. Ja, eine Sandbox verwendet möglicherweise einen Proxy in ihrer Implementierung, aber Sie sollten dies wahrscheinlich nicht selbst schreiben.
Bergi

@Bergi Ich weiß, dass ein Sandbox-Mechanismus komplizierter sein sollte als dieser, aber meine Frage ist nicht wirklich, ob diese Sandbox sicher und gut ist, sondern ob die Verwendung auf withdiese Weise schlecht ist oder nicht.
FZs

Nun, es verhindert immer noch, dass Sie den strengen Modus verwenden, und der Zugriff auf Eigenschaften über withund einen Proxy ist immer noch ziemlich langsam. Ich würde versuchen, nach einer anderen Lösung für das zugrunde liegende Problem zu suchen, aber ansonsten verwenden Sie nur withein Werkzeug, das zu tun scheint, was Sie brauchen.
Bergi

Antworten:


1

Klingt nach dem guten alten lexikalischen und dynamischen Thema. Im Allgemeinen ist der lexikalische Bereich sicherer, aber in einigen Situationen ist ein dynamischer Bereich sinnvoll, da er einige Lösungen sehr vereinfacht. Ich würde sagen, Ihr Beispiel ist einer der Fälle, in denen es nützlich sein kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.