2017-02-10 3 views
7

Lassen Sie uns sagen, dass wir eine Funktion haben, die wie folgt aussieht:Gibt es eine einfache Methode, eine globale Var in eine lokale Variable zu verwandeln?

const fn =() => x; 

Diese Funktion sollte den Wert von x zurück, wo x im globalen Bereich zur Verfügung steht. Zunächst ist dies undefined aber wenn wir x definieren:

const x = 42; 

Dann können wir fn erwarten 42 zurückzukehren.

Jetzt wollen wir fn als eine Zeichenfolge rendern. In JavaScript haben wir toString für diesen Zweck. Lassen Sie uns jedoch auch sagen, dass wir schließlich fn in einem neuen Kontext ausführen möchten (d. H. Unter Verwendung von eval), und daher sollten alle von ihr verwendeten globalen Referenzen entweder vor oder während unseres Aufrufs an toString verinnerlicht werden.

Wie können wir x eine lokale Variable machen, deren Wert den globalen Wert von x zu der Zeit widerspiegelt, die wir fn in eine Zeichenfolge konvertieren? Angenommen, wir können nicht wissen x heißt x. Das heißt, wir können davon ausgehen, dass die Variablen in demselben Modul enthalten sind.

+0

Wie 'fn.toString() => \ '(... args) => {const x = $ {JSON.stringify (window.x)} ; return $ {this.toString()} (... Argumente); } \ ';'? Oder wenn Sie nur den Rückgabewert der Funktion erfassen wollen: 'fn.toString =() => \'() => $ {JSON.stringify (this())} \ ';'. –

+1

Wie viel Aufwand möchten Sie damit machen? Es ist einfach, wenn Sie wissen, welche Variablen erfasst werden sollen. Es ist auch einfach, wenn Sie nur die Ausgabe erfassen (wenn die Funktion keine Eingabe macht und keine Nebenwirkungen hat, dann reicht es aus). Wenn Sie die Funktion wirklich ausführen wollen, während Sie die Werte ihrer freien Variablen festlegen, aber Sie den Namen der freien Variablen nicht kennen, müssen Sie zuerst die Namen finden. Sie können dies tun, indem Sie die Zeichenfolgendarstellung der Funktion analysieren und ein Tool wie https://github.com/estools/escope verwenden, um freie Variablen zu finden. –

+0

@FelixKling irgendwann wird es eine ordentliche Mühe wert sein. Vorläufig können wir die Variablen in einzelnen Funktionen duplizieren - das ist kein netter Ansatz, aber es tut, was wir brauchen. Natürlich, wenn es eine bessere Methode gibt, würden wir sie gerne einsetzen! – maxcountryman

Antwort

0

Wir nicht xx benannt wissen kann. Dies ist das zentrale Stück dieses Puzzles und wird daher in der ursprünglichen Frage hervorgehoben. Es wäre zwar schön, wenn wir eine einfachere Lösung hätten, aber es scheint eine richtige Antwort zu sein, die sich auf die Implementierung einer Art Parser oder AST-Traversierung bezieht.

Warum ist das notwendig? Während wir annehmen können, dass x in einem Modul als global (es ist notwendigerweise zwischen Funktionen geteilt) lebt, können wir nicht davon ausgehen, dass es einen bekannten Namen hat. Also brauchen wir einen Weg, um x (oder alle Globals wirklich) aus unserem Modul zu extrahieren und es dann als Kontext bereitzustellen, wenn wir schließlich eval.

N.B .: Bereitstellung bekannter Variablen als Kontext ist trivial. Einige Antworten hier scheinen anzunehmen, dass dies ein schwieriges Problem ist, aber in der Tat ist es ziemlich einfach mit eval; Einfach den Kontext als String voranstellen.

Also, was ist dann die richtige Antwort hier? Wenn wir einen AST verwenden würden (Acorn könnte zum Beispiel ein praktikabler Startpunkt sein), könnten wir das Modul untersuchen und programmatisch alle darin enthaltenen Globals extrahieren. Dazu gehören x oder jede andere Variable, die zwischen unseren Funktionen geteilt werden kann; Wir können sogar die Funktionen untersuchen, um festzustellen, welche Variablen für ihre Ausführung notwendig sind.

Wieder war die Hoffnung, diese Frage ursprünglich zu stellen, eine einfachere Lösung zu destillieren oder den Stand der Technik aufzudecken, der an unsere Bedürfnisse angepasst werden könnte.Letztendlich geht meine Antwort und die Antwort, die ich akzeptiere, auf die nicht-triviale Aufgabe des Parsens und Extrahierens von Globalen aus einem JavaScript-Modul zurück; Es scheint keinen einfachen Weg zu geben. Ich denke, das ist eine faire Antwort, wenn nicht eine praktische, die wir heute umsetzen können. (Wir werden jedoch später darauf eingehen, wenn unser Projekt wächst.)

-1

meinst du das? einzige Antwort kann Code veröffentlichen, so dass ich Antwort

var x = 42 
 

 
function fn() { 
 
    return x 
 
} 
 

 

 
(() => { 
 
    var x = 56 
 
    var localFn = eval('"use strict";(' + fn.toString()+')') 
 
    console.log(localFn) 
 
    console.log(localFn()) 
 
})()

  • warum localFn umbenennen, wenn Sie var fn=xx in diesem Bereich die äußere fn verwenden nie existiert!
  • in nodejs? referenzieren nodejs vm
  • passierender Kontext? Sie können nicht js Kontext es sei denn, Sie halten Ihren eigenen Rahmen wie AngularJS
-1

Wenn Sie bereits „geht es“ von eval() auszuführen fn() im neuen Kontext, warum dann nicht definieren die Funktion speichern selbst mit eval()?

eval('const fn =() => ' + x + ';')

1

Wenn Sie Sperre bestimmte Variablen wollen, während Funktion Zeichenfolge konvertieren, müssen Sie die Variablen entlang der Zeichenfolge Funktion übergeben.

Es könnte wie folgt implementiert werden (geschrieben mit Typen - Typoskript Notation)

const prepareForEval = 
    (fn: Function, variablesToLock: { [varName: string]: any }): string => { 
    const stringifiedVariables = Object.keys(variablesToLock) 
     .map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`); 

    return stringifiedVariables.join("") + fn.toString(); 
    } 

es dann so Jede eval erklären Zeichenfolgevariablen und funtion anstelle

const stringifiedFunction = prepareForEval(someFunction, { x: x, y: y }) 

// you can even simplify declaration of object, in ES6 you simply write 
const stringifiedFunction = prepareForEval(someFunction, { x, y }) 
// all variables you write into curly braces will be stringified 
// and therefor "locked" in time you call prepareForEval() 

verwenden, wo es wurde ausgeführt. Dies könnte ein Problem sein, Sie könnten eine Variable neu deklarieren, unbekannter Wert, Sie müssen den Namen der stringifizierten Funktion kennen, um sie aufrufen zu können, oder es kann einen Fehler erzeugen, wenn Sie die bereits deklarierte const Variable deklarieren.

dieses Problem zu überwinden, müssen Sie die Zeichenfolge Funktion implementieren als immediatelly anonyme Funktion mit einem eigenen Bereich ausgeführt werden, wie

const prepareForEval = 
    (fn: Function, variablesToLock: { [varName: string]: any }): string => { 
    const stringifiedVariables = Object.keys(variablesToLock) 
     .map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`); 

    return ` 
     var ${fn.name} = (function() { 
     ${stringifiedVariables.join("")} 
     return ${fn.toString()}; 
    )(); 
    `; 
    } 

diese Modifikation Funktion und Variablen in separatem Umfang erklären wird und dann wird es diese Funktion zuzuweisen zu fn.name Konstante. Die Variablen werden den Bereich nicht polieren, wo Sie eval, es wird nur neue fn.name Variable deklarieren und diese neue Variable wird auf Deserialized-Funktion festgelegt werden.

0

können Sie verwenden OR Operator || aktuellen Wert von x-fn.toString() Anruf

const fn =() => x; 
 
const x = 42; 
 
const _fn = `${fn.toString()} || ${x}`; 
 
console.log(_fn, eval(_fn)());

+0

Versuchen Sie noch einmal, die Frage zu lesen: Sie kennen den Namen von 'x' nicht zum Zeitpunkt der Serialisierung der Funktion Probe funktioniert nicht. Eine korrekte Antwort wird fast sicher eine Art AST-Traversal implementieren. – maxcountryman

+0

@maxcountryman Was meinst du mit "Du kennst nicht den Namen von x"? Sie können '.toString()' zuerst aufrufen und dann den "Namen" des Variablenbezeichners nach der Anweisung 'return' an' fn' 'const_fn = \ $ {fn.toString()} || abrufen $ {window [fn.toString(). split (/ \ s /). pop()]}. Die ursprüngliche Frage enthält nicht den Begriff "AST-Traversal" oder beschreiben Anforderung als einschließlich _ "sicher implementieren eine Art von AST-Traversal" _ – guest271314

+0

Ich wäre fasziniert zu lesen, wie Sie "x" aus einem Modul zu extrahieren, ohne zu wissen 'x' heißt so? Ich kann mir vorstellen, dass ein Weg dies über einen AST ist, aber vielleicht gibt es einfachere Methoden für JS/Node. Leider geht Ihre Antwort nicht auf das Kernproblem des tatsächlichen Extrahierens von 'x' ein und bettet es einfach in eine Zeichenkette ein, sobald sie scheinbar extrahiert wurde. (Dies ist trivial und etwas, was wir bereits für andere Kontexte tun.) – maxcountryman

0

Globale Variablen verketten können mit Verschlüssen aus lokalen (privat) werden. w3Schools

function myFunction() { 
    var a = 4; 
    return a * a; 
} 
0

Dank guest271314, sehe ich jetzt, was Sie wollen.

Dies ist sein Code, nur wenig verbessert:

const stringifiedFn = ` 
    (function() {   
     const _a = (${fn.toString()})(); 
     return _a !== undefined ? _a : ${JSON.stringify(fn())}; 
    })(); 
`; 

dieser Code fn in Kontext ausgeführt wird, in dem Sie eval, und wenn fn in diesem Zusammenhang undefined gibt es die Ausgabe von fn in Zusammenhang gibt, wo es gestrichen wurde.

Alle Kredit geht an guest271314

Verwandte Themen