2010-01-25 2 views
51

Nehmen wir an, ich habe var a = function() { return 1; }. Ist es möglich, a zu ändern, so dass a()2 zurückgibt? Vielleicht durch Bearbeiten einer Eigenschaft des Objekts a, seit every function is an object?Können Sie eine JavaScript-Funktion nach der Deklaration ändern?

Update: Wow, danke für alle Antworten. Ich fürchte jedoch, ich wollte nicht einfach eine Variable neu zuweisen, sondern eine bestehende Funktion bearbeiten. Ich denke an die Linien, wie Sie partial functions in Scala kombinieren können, um eine neue PartialFunction zu erstellen. Ich bin daran interessiert, etwas Ähnliches in Javascript zu schreiben und dachte, dass die vorhandene Funktion möglicherweise aktualisiert werden könnte, anstatt ein völlig neues Function Objekt zu erstellen.

+0

Warum würden Sie nicht nur eine neue Funktion ein zuweisen, 2 zurückgegeben? –

+7

Da dies die Verweise auf die anderweitig gespeicherte Funktion nicht aktualisieren würde. –

Antwort

24
var a = function() { return 1; } 
alert(a()) // 1 
a = function() { return 2; } 
alert(a()) // 2 

technisch verlieren Sie eine Funktionsdefinition und ersetzen sie durch eine andere.

0

Absolut. Ordnen Sie ihm einfach eine neue Funktion zu.

+1

Dies führt nicht zu einem Downvote. Es mag knapp sein, aber es ist absolut richtig. –

+3

Es ist nicht korrekt, das würde die Verweise auf die Funktion woanders nicht aktualisieren. –

0

Können Sie es nicht später noch einmal definieren? Wenn Sie die Änderung wollen versuchen, neu zu definieren es genauso:

a = function() { return 2; } 
0

Wenn Sie Javascript debuggen und wollen sehen, wie Änderungen am Code der Seite betrifft, können Sie diese Firefox-Erweiterung verwenden, um anzuzeigen/ändern Javascripts:

Execute JS Firefox-Erweiterung: https://addons.mozilla.org/en-US/firefox/addon/1729

40

Sie können alle Arten von fun stuff mit Javascript tun, einschließlich der Neudefinition Funktionen:

var a = function(){ return 1; } 

alert(a()); //1 

// keep a reference 
var old = a; 

// redefine 
a = function(){ 
    // call the original function with any arguments specified, storing the result 
    var originalResult = old.apply(old, arguments); 
    // add one 
    return originalResult + 1; 
}; 
alert(a()); //2 

Voila.

Edit: Aktualisiert dies in einem Szenario verrückter zeigen:

var test = new String("123"); 
console.log(test.toString()); // logs 123 
console.log(test.substring(0)); // logs 123 
String.prototype.substring = function(){ return "hahanope"; } 
console.log(test.substring(0)); // logs hahanope 

Sie können hier sehen, dass, obwohl „test“ zuerst definiert ist, und wir neu definieren String() danach gilt die Änderung noch.

Seitennotiz: Sie sollten wirklich Ihre Architektur überdenken, wenn Sie dies tun ... Sie werden den Mist aus irgendeinem schlechten Entwickler 5 Jahre später verwirren, wenn er sich eine Funktionsdefinition ansieht, die ist soll 1 zurückzukehren, scheint aber immer 2 zurückkehren ....

+1

+1 für die Neudefinition in Bezug auf die ursprüngliche Funktion. –

+9

Dies definiert die Funktion nicht neu. Er weist der Variablen, die auf die alte Bezug nahm, eine völlig neue Funktion zu. Verweise auf die ursprüngliche Funktion an anderer Stelle werden nicht aktualisiert. – hashchange

+2

@hashchange: touche. Die Tatsache, dass es einfach eine "var" -Referenz ist, ist hier unerheblich. Wenn Sie beispielsweise eine Kernfunktion in Ihrer Anwendung ändern möchten, ändern Sie einfach den Prototyp. Das Konzept gilt weiterhin. – jvenema

13

Wie wäre es damit, ohne die Funktion neu zu definieren, mit:

var a = function() { return arguments.callee.value || 1; }; 
alert(a()); // => 1 
a.value = 2; 
alert(a()); // => 2 
+1

Dies sollte die akzeptierte Antwort sein! – mgol

+0

Stimmen Sie überein, wenn Sie es jedes Mal neu definieren, überschreiben Sie alte Referenzen, ohne andere vorhandene Referenzen zu ändern. – Karol

+1

Dies ist die einzige richtige Antwort hier im Moment - die einzige, die tatsächlich die Funktion beeinflusst, und nicht eine neue zu erstellen. –

35

Früher habe ich so etwas eine vorhandene Funktion, deren Erklärung ändern war nicht zugänglich für mich:

// declare function foo 
var foo = function (a) { alert(a); }; 

// modify function foo 
foo = new Function (
    "a", 
    foo.toSource() 
    .replace("alert(a)", "alert('function modified - ' + a)") 
    .replace(/^function[^{]+{/i,"") // remove everything up to and including the first curly bracket 
    .replace(/}[^}]*$/i, "") // remove last curly bracket and everything after<br> 
); 

Anstelle von toSource() könnten Sie wahrscheinlich toString() verwenden, um eine Zeichenfolge abzurufen, die die Deklaration der Funktion enthält. Einige Aufrufe von replace(), um die Zeichenfolge für die Verwendung mit dem Funktionskonstruktor vorzubereiten und die Quelle der Funktion zu ändern.

+0

Interessant. Keine Möglichkeit, es ohne Streicher zu machen? Bummel. – pr1001

+4

ToSource funktioniert nicht auf IE und Chrome. Und das ist einfacher, den Funktionscode zu erhalten: 'foo.toString(). Match (/ {([\ s \ S] *)} /) [1]'. Auch bei @ prm1001 sollte man dies als richtige Antwort akzeptieren. Es hat mir auch geholfen – jscripter

+0

danke hmundt und Bubu Daba. Korrekt, anscheinend müssen Zeichenfolgen verwendet werden. – Tyler

10

Ich bleibe bei Jvenema Lösung, in der ich nicht die globale Variable "alt" mag.Es scheint besser die alte Funktion innerhalb der neuen zu halten:

function a() { return 1; } 

// redefine 
a = (function(){ 
    var _a = a; 
    return function() { 
    // You may reuse the original function ... 
    // Typical case: Conditionally use old/new behaviour 
    var originalResult = _a.apply(this, arguments); 
    // ... and modify the logic in any way 
    return originalResult + 1; 
    } 
})(); 
a() // --> gives 2 
0

Dies ist ein anschauliches Beispiel basierend auf einem Steuertimepicker eworld.ui www.eworldui.net

einen Timepicker Mit eworld.ui wo JavaScript ist von außen nicht erreichbar, Sie können keine js zu diesen Steuerelementen finden. Wie können Sie dem Timepicker ein onchange-Event hinzufügen?

Es gibt eine js Funktion, die aufgerufen wird, wenn Sie Select eine Zeit zwischen allen Optionen, die die Steuerung Ihnen bieten. Diese Funktion ist: TimePicker_Up_SelectTime

Zuerst müssen Sie den Code innerhalb dieser Funktion kopieren.

auswerten ... quikwatch ... TimePicker_Up_SelectTime.toString()

function TimePicker_Up_SelectTime(tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) { 
    document.getElementById(tbName).value = selTime; 
    if(lblName != '') 
    document.getElementById(lblName).innerHTML = selTime; 
    document.getElementById(divName).style.visibility = 'hidden'; 
    if(enableHide) 
    TimePicker_Up_ShowHideDDL('visible'); 
    if(customFunc != "") 
    eval(customFunc + "('" + selTime + "', '" + tbName + "');"); 
    eval(postbackFunc + "();"); 

}

Jetzt

den Code verwenden, die Sie vor den gleichen Quellcode neu zuweisen gespeichert haben, aber hinzufügen, was Sie wollen ..

TimePicker_Up_SelectTime = Funktion (tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) { document.getElementById (tbName) .value = selTime; if (lblName! = '') document.getElementById (lblName) .innerHTML = selTime; document.getElementById (divName) .style.visibility = 'versteckt'; if (enableHide) TimePicker_Up_ShowHideDDL ('visible'); if (customFunc! = "") eval (customFunc + "('" + selTime + "', '" + tbName + "');"); eval (postbackFunc + "();");

Ich habe meine Funktion der Funktion hinzugefügt, so kann ich jetzt ein onchange-Ereignis simulieren, wenn ich eine Zeit auswählen.

RaiseChange (...) könnte sein, was immer Sie wollen.

10

Sie möchten also den Code einer Funktion direkt an Ort und Stelle ändern und nicht einfach eine andere Funktion einer vorhandenen Variablen neu zuweisen.

Ich hasse es zu sagen, aber soweit ich es herausgefunden habe - und ich habe es versucht - kann es nicht getan werden. Richtig, eine Funktion ist ein Objekt und als solches hat sie Methoden und Eigenschaften, die auf dem Objekt selbst optimiert und überschrieben werden können. Leider gehört der Funktionskörper nicht dazu. Es ist keiner öffentlichen Eigenschaft zugeordnet.

Die documentation on MDN listet die Eigenschaften und Methoden des Funktionsobjekts auf. Keines von ihnen gibt uns die Möglichkeit, den Funktionskörper von außen zu manipulieren.

Das ist, weil according to the spec der Funktionskörper in der internen [[Code]] Eigenschaft des Funktionsobjekts gespeichert ist, auf die nicht direkt zugegriffen werden kann.

+0

Ich habe mich angestrengt und konnte es auch nicht bekommen. Sogar Proxy lässt Sie nicht wirklich in die ursprüngliche Funktion einhaken, da Sie abfangen müssen, den neuen Proxy anzurufen. –

3

Alle möglichen Lösungen bleiben bei einem "Funktionswrapping-Ansatz". The most reliable amongst them seems to be the one of rplantiko.

Solche Funktion Verpackung kann leicht abstrahiert werden. Das Konzept/Muster selbst könnte "Method Modification" genannt werden. Ihre Implementierung gehört eindeutig zu Function.prototype. Es wäre schön zu einem Tag von Standard prototypal Methode Modifikatoren wie before, after, around, afterThrowing und afterFinally gesichert werden.

Wie bei dem oben erwähnten Beispiel durch rplantiko ...

function a() { return 1; } 

// redefine 
a = (function() { 
    var _a = a; 
    return function() { 
    // You may reuse the original function ... 
    // Typical case: Conditionally use old/new behaviour 
    var originalResult = _a.apply(this, arguments); 
    // ... and modify the logic in any way 
    return originalResult + 1; 
    }; 
})(); 

a(); // --> gives 2 

... und die Verwendung von [around] machen, würde der Code verwandeln ...

function a() { return 1; } 

console.log("a : ", a); 
console.log("a() : ", a()); 


a = a.around(function (proceed, interceptor, args) { 
    return (proceed() + 1); 
}); 

console.log("a : ", a); 
console.log("a() : ", a()); 
+0

Dies war im Grunde, was die Funktion 'wrap()' des Prototype-Frameworks ermöglichte/noch immer bietet. http://prototypejs.org/doc/latest/language/Function/prototype/wrap/ Ich weiß nicht, ob es in der aktuellen ESx-Version ein Feature dieser Art gibt – rplantiko

-1
    function get_func(func) {//from StackOverflow 
         //var args = Array.prototype.slice.call(arguments); 
         var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 
         var ARGUMENT_NAMES = /([^\s,]+)/g; 
         var fnStr = func.toString().replace(STRIP_COMMENTS, ''); 
         var args = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); 
         //var code = func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]; 
         var code =fnStr.slice(fnStr.indexOf("{") + 1, fnStr.lastIndexOf("}")); 
         var func_full_name=fnStr.substr(0, fnStr.indexOf('{')); 
         var func_name=fnStr.slice(9, fnStr.indexOf('(')); 
         if(args === null){ args = []; } 
         return { func_name:func_name,func_full_name:func_full_name, code:code, args:args}; 
        } 
        function str_trim(str){ 
         //replaces multi-line characters 
         return str.replace(/\n/g,'').replace(/^ +| +$/gm, '').trim(); 
        } 
        function edit_func(func,callback){ 
         var a_func=get_func(func); 
         var lines = a_func.code.split('\n'); 
         var output=[]; 
         for(var i=0;i<lines.length;i++){ 
          var code=str_trim(lines[i]); 
          if(code!=''){ 
           code =callback(code,output.length,a_func.args); 
           output.push(code); 
          } 
         } 
         //var test=Function(['a','b'],'alert(a);alert(b);'); 
         //test(1,2); 
         //func=Function(a_func.args,output.join('\n')); 
         //return func; 
         var head = document.getElementsByTagName('head')[0]; 
         var script = document.createElement('script'); 
         script.type = 'text/javascript'; 
         script.text =a_func.func_full_name+'{'+ output.join('\n') + '}' ; 
         head.appendChild(script); 
        } 
        function test(id) { 
         var x, y; 
         x = 5; 
         y = 10; 
         alert(x + y); 
         alert(id); 
        } 
        edit_func(test,function(code,line,args){ 
         if(line==3){ 
          return 'alert(x*y);'; 
         } 
         return code; 
        }); 
         test(3); 

Try dieses Es wird die ursprüngliche Funktion mit einem neuen Code ändern, der einen Rückruf für das Überprüfen jeder Codezeile verwendet.

0

können Sie Funktionen wie andere Objekte ändern

var a1 = function(){return 1;} 
 
var b1 = a1; 
 
a1 = function(){ 
 
    return b1() + 1; 
 
}; 
 
console.log(a1()); // return 2 
 

 
// OR: 
 
function a2(){return 1;} 
 
var b2 = a2; 
 
a2 = function(){ 
 
    return b2() + 1; 
 
}; 
 
console.log(a2()); // return 2

Verwandte Themen