Aop wird auch als aspektorientierte Programmierung bezeichnet, wobei „Benachrichtigung“ die spezifische Implementierung von Aspekten ist, die in „vor“ (vor der Benachrichtigung), „nachher“ (nach der Benachrichtigung) und „um die herum“ (Surround-Benachrichtigung) unterteilt ist Wer Spring verwendet hat, muss damit vertraut sein, aber in js ist AOP ein ernsthaft ignorierter technischer Punkt. Die Verwendung von AOP kann jedoch die Logik des js-Codes effektiv verbessern. In den Front-End-Frameworks dojo und yui3 wird AOP beispielsweise zu einem internen Mechanismus für benutzerdefinierte Ereignisse hochgestuft, der überall im Quellcode zu sehen ist. Dank dieser Abstraktion sind die benutzerdefinierten Ereignisse von Dojo äußerst leistungsstark und flexibel. Die Implementierung von AOP im Dojo/Aspect-Modul erfolgt im Wesentlichen durch die Methode „Vorher“, „Nachher“ und „Around“. des Struktursystems des Dojo/Aspekt-Moduls.
Um die Surround-Benachrichtigung in js zu implementieren, ist die Verwendung von Callback die einfachste und nachdenklichste Möglichkeit
advice = function(originalFunc){ console.log("before function"); originalFunc(); console.log("after function"); } var obj = { foo: function(){ console.log('foo'); } } advice(obj.foo)
Ergebnis:
vor der Funktion
foo
nach Funktion
Haha, es ist zu einfach. Kannst du wieder schlafen? . . .
Aber ist es nicht etwas zu grob? . . . Die versprochene Umgebung. . . . Zumindest der nächste Aufruf von obj.foo sollte dieses Ergebnis haben, statt eines trockenen „foo“ müssen wir dafür einige Änderungen vornehmen und Schließungen verwenden
advice = function(originalFunc){ return function() { console.log("before function"); originalFunc(); console.log("after function"); } } var obj = { foo: function(){ console.log(this.name); }, name: "obj" } obj.foo = advice(obj.foo) obj.foo()
Ausgabe:
vor der Funktion
nach Funktion
Es scheint, dass der Surround-Effekt erreicht wurde, aber wo ist der versprochene Name geblieben? . . .
Bei der durch Beratung zurückgegebenen Schließung müssen wir uns auch mit Umfangsproblemen befassen
advice = function(originalFunc){ return function() { console.log("before function"); originalFunc(); console.log("after function"); } } var obj = { foo: function(){ console.log(this.name); }, name: "obj" } keepContext = function() { return obj['foo'].call(obj); } obj.foo = advice(keepContext);
Es scheint, dass das Bereichsproblem durch die Verwendung von call gelöst wird. Lassen Sie es uns ausführen und sehen:
Verdammt, ist das die legendäre Endlosschleife? . . .
Es scheint, dass wir noch einige Änderungen vornehmen und eine Zwischenvariable verwenden müssen, um die Endlosschleife zu beseitigen
advice = function(originalFunc){ return function() { console.log("before function"); originalFunc(); console.log("after function"); } } var obj = { foo: function(){ console.log(this.name); }, name: "obj" } var exist = obj.foo; keepContext = function() { return exist.call(obj); } obj.foo = advice(keepContext); obj.foo();
Ausgabe:
vor der Funktion
obj
nach Funktion
Haha, die Welt wurde plötzlich zu einem wunderschönen Ort. . . .
Aber scheint dieser Haufen Code zu niedrig zu sein? Sollten wir uns ein paar Abstraktionen auf hoher Ebene einfallen lassen?
function around(obj, prop, advice){ var exist = obj[prop]; var advised = advice(function(){ return exist.call(obj, arguments); }); obj[prop] = advised; } advice = function(originalFunc){ return function() { console.log("before function"); originalFunc(); console.log("after function"); } } var obj = { foo: function(){ console.log(this.name); }, name: "obj" } around(obj, 'foo', advice); obj.foo();
advice = function(originalFunc){ return function() { //before originalFunc(); //after } }
Dann stellt sich die Frage: Was soll ich tun, wenn ich versehentlich die Methode „around“ noch einmal aufrufe? . . . Stirn. . . . Dies ist eine Frage: Sollten wir ein Handle mit einer Remove-Methode zurückgeben, um die Bindung zu beseitigen, genau wie beim Binden/Entfernen von Ereignissen?
Entfernen bedeutet, dass die Funktion bei der nächsten Ausführung nicht mehr die entsprechende around-Methode ausführt, sondern nur noch die originalFunc-Methode
function around(obj, prop, advice){ var exist = obj[prop]; var previous = function(){ return exist.call(obj, arguments); }; var advised = advice(previous); obj[prop] = advised; return { remove: function(){ obj[prop] = exist; advice = null; previous = null; exist = null; obj = null; } } } var count = 1; advice = function(originalFunc){ var current = count++; return function() { console.log("before function " + current); originalFunc(arguments); console.log("after function " + current); } } var obj = { foo: function(arg){ console.log(this.name + " and " + arg); }, name: "obj" } h1 = around(obj, 'foo', advice); h2 = around(obj, 'foo', advice); obj.foo(); h1.remove(); obj.foo(); h2.remove(); obj.foo();
before function 2 before function 1 obj and [object Arguments] after function 1 after function 2 obj and undefined before function 1
Dies. . Es stellt sich nicht nur als etwas chaotisch heraus. . . Habe auch einen Fehler gemeldet. . . . Ja, es ist erträglich, aber mein Onkel kann es nicht ertragen. Mein Onkel kann es nicht ertragen, aber meine Schwägerin kann es nicht ertragen!
Ah, Abschluss. . . Bitte gib mir Kraft!
function around(obj, prop, advice){ var exist = obj[prop]; var previous = function(){ return exist.apply(obj, arguments); }; var advised = advice(previous); obj[prop] = function(){ //当调用remove后,advised为空 //利用闭包的作用域链中可以访问到advised跟previous变量,根据advised是否为空可以来决定调用谁 return advised ? advised.apply(obj, arguments) : previous.apply(obj, arguments); }; return { remove: function(){ //利用闭包的作用域链,在remove时将advised置空,这样执行过程中不会进入本次around //这几个不能删 //obj[prop] = exist; advised = null; advice = null; //previous = null; //exist = null; //obj = null; } } } var count = 1; advice = function(originalFunc){ var current = count++; return function() { console.log("before function " + current); originalFunc.apply(this, arguments); console.log("after function " + current); } } var obj = { foo: function(arg){ console.log(this.name + " and " + arg); }, name: "obj" } h1 = around(obj, 'foo', advice); h2 = around(obj, 'foo', advice); obj.foo('hello world'); h1.remove(); obj.foo('hello world'); h2.remove(); obj.foo('hello world');
Ausgabe:
before function 2 before function 1 obj and hello world after function 1 after function 2 before function 2 obj and hello world after function 2 obj and hello world
Als ich zum ersten Mal die ganze Nacht wach blieb, um zu bloggen, war ich auch betrunken. Um vier Uhr hörte ich auch das Krähen Unbekanntes Vogelgezwitscher. Um fünf Uhr zwitscherten viele Vögel. . . .
Referenzartikel:
Verwenden Sie AOP, um Javascript-Code zu verbessern
AOP (aspektorientierte Programmierung) und OOP (objektorientierte Programmierung) von yui3