Dienstag, 1. Oktober 2019

Interessantes zu JQuery und Javascript

Notizen zu Jquery

1: Da $() eine Fkt ist und $.get() eine Klassenfunktion, muss es möglich sein, dass zu jeder Fkt f Unterfunktionen und Eigenschaften definert werden können der Art: f.g ; mit g ist Funktion oder Eigenschaft. g selbst kann auch nur eine Eigenschaft mit einem zugewiesenen Wert sein. Auch als solche ist es ihr möglich Unterfuntionen oder Untereigenschaften zu definieren. Also z.B. f.g="Hallo Welt" und f.g.h=function(){...}

2: Globale Funktionen von JQuery entsprechen Klassenfunktionen von Java. Sie bekommen keinen this Zeiger bei der Ausführung gesetzt. Man kann globale Funktionen definieren, indem man sie dem Namensraum $ zuweist:
$.neuGlobFunc = function(){...}

3: Objektfunktionen von JQuery entsprechen Memberfunktionen von Java. Sie bekommen als this Zeiger das JQuery-Objekt mitgegeben, mit welchem Sie aufgerufen werden. Durch Definition von Objektfunktionen, werden alle JQuery Objekte im Nachhinein um Funktionen erweitert. Dies geschieht duch die Vereinbarung, dass alle Funktionen im Namensraum $.fn als Objektfunktionen agieren und über einen impliziten this Zeiger ihr Aufrufobjekt refernzieren können. Dabei symbolisiert fn das Aufruferobjekt für die Objektfunktionen.
Beispiel:
$.fn.neuObjFunc = function(){...}
Hat man z.B. ein JQuery Objekt mittels
$anObj = $("#anID");
$.fn.printVal = function(){console.log(this.val());}
$anObj.printVal();

4: Bei der JQuery Funktion .each() ( == $.fn.each()) wird der übergebenen Callbackfunktion ein Javascript Dom Element und nicht das JQuery Objekt als this Zeiger mitgegeben.
$("a").each(function(laufindex){console.log("Iteration: "+laufindex); $(this).addClass("classname");//Die callback Funktion kann die Iteration frühzeitig durch return false; abbrechen lassen}
https://api.jquery.com/each/#each-function:
"Note: most jQuery methods that return a jQuery object also loop through the set of elements in the jQuery collection — a process known as implicit iteration."

Man kann aber auf den meisten JQuery Objektfunktionen, welche den Aufrufer am Ende Ihrer Ausführung zurückgeben, auf die Anwendung von .each Verzichten, da in diesen Fälle eine implizite Ausführung auf den einzelnen Collection Elementen durchgeführt wird:
$("a").addClass("classname");
==
$("a").each(function(){$(this).addClass("classname");

============================

(function () {
     // recommended by JSLint and "Crockford School" Thinking
//recommended syntax by http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
    //Notice the () around the anonymous function. This is required by the language, since statements that begin with the token function are always considered to be function declarations. Including () creates a function expression instead.
}());

VS

(function () {
  //NOT WRONG and NOT less performant or efficient
  //USED BY jquery.imageareaselect.js
})();

Auch:

(function dummyname (par1, par2) {

}(arg1,arg2));

VS

(function dummyname (par1, par2) {

})(arg1,arg2);

===============================
jquery.imageareaselect.js:

(function($) {
....
$.fn.imgAreaSelect = function (options) {
    options = options || {};

    this.each(function () {
        /* Is there already an imgAreaSelect instance bound to this element? */
        if ($(this).data('imgAreaSelect')) {
            /* Yes there is -- is it supposed to be removed? */
            if (options.remove) {
....
})(JQuery);

Interessant:
Die anonyme Callbackfunction von .each() (deren Argument) hat Zugriff auf options, obwohl dies nicht als Argument weitergereicht wird. Daher kann man davon ausgehen, dass alle lokalen Variablen (Parameter der Funktion und weitere lokal im Fkt.-Körper vereinbarte Variablen) des die anonyme Funktionsdefinition umschließenden Codeblocks für diese zurgreifbar sind. Dieser Codeblock ist quasi die Closure für die anonyme Callbackfunktion. Dabei können allerdings einige der Closure Variablen durch lokale und evtl. fremdgesetzte gleichnamige Variablen verdeckt sein. So zum Beispiel bei der this Variablen. Diese this Variable im Callbackcode verdeckt diejenige aus dem Closure. Die Closure this stellt ein JQuery Objekt dar, welches auch eine Collection sein kann. Die Objektfunktion .each macht nun aber folgendes:
.each(cbackfnc){
    for(i=0;i        //Ruft cbackfnc im Kontext von this.get(i) mit Argument i auf
        //== this.get(i).cbackfnc(i) ,so als ob cbackfnc eine Memberfunktion von
        //this.get(i) wäre
        //Damit wird im cbackfnc Codeblock this=this.get(i) gesetzt und damit das ursprüngliche JQuery this verdeckt.
        //Man beachte: Innerhalb von cbackfnc ist this nun kein JQuery Objekt mehr, sondern ein natives JS Dom Element. Daher die notwendige Umwandlung mit $(this), wenn man JQuery darauf anwenden will.
        if(!cbackfnc.call(this.get(i),i)) break;
    }
}

==============================
$.imgAreaSelect = function (img, options) {
...
 Irgendwas nimmt Bezug auf this
...

Durch new $.imgAreaSelect(this, options) passiert folgendes.

Die Funktion, die mit $.imgAreaSelect referenziert wird (Fkts-zeiger), wird aufgerufen und wegen des new Operators wird eine this Referenz zur Aufrechterhaltung der Closure für diese Funktion zurückgegebe. Es wird sozusagen eine Closure instanziiert. Vergleichbar mit der Instanziierung einer Klasse.
Der Konstruktorcode im $.imgAreaSelect(...)  Funktionsblock ergibt sich implizit durch denjenigen Code, welcher als Anweisungen beim durchlaufen des Funktionskörpers zur Anwendung kommt. Das heist jede Codezeile, welche die Ausführung und/oder Auswertung von Funktionen und Variablen dieses Codeblocks oder der Closure des Codeblocks der Funktionszuweisung $.imgAreaSelect = function (img, options){...} bewirkt, stellt in seiner Gesamtheit Konstruktorcode dar. Es ist quasi eine verstreute Einbettung von Code zwischen Variablen- und Funktionsdefinitions-Abschnitten.

Statt wie in Java:

class A{
public A(x,y){s1=x+y; return this;}
String s1;
public f1(){}
public f2(){}
private f3(){}
}

gibt es in JS:

=function(x,y){
 String s1;
 s1=x+y;
 function f1(){}
 function f2(){}
 function f3(){}
 this.f1=f1;
 this.f2=f2;
 //return this
 //ist implizit durch Verwendung von new Operator beim Funktionsaufruf gegeben.
}
 
===================================

Zu Closures:

1)this.trojan = function trojan(horse){horse("hallo","Welt",this);console.log("inneres this:",this);};

2)imgselect.trojan(function tgf(){console.log("hallo",this,arguments);});

Rufen wir 2 in der Console auf wird, this in tgf auf Window gesetzt.
Da tgf als Argument für trojan dient, und dort seinerseits aufgerufen wird, könnte man denken, dass der Context der Ausführung von trojan derjenige für horse() wird. Dies ist aber nicht so. Wenn tgf/horse in seinem Fktskörper Bezug auf this nimmt, dann aus der Closure seiner Compilierzeit. Also der Closure die Gültig war als die Anonyme Fkt als Argument für trojan() erstellt wurde.
Diese Closure von tgf kann man nicht neu setzen, jedoch den this Zeiger aus dieser Closure überdecken, indem trojan() die Fkt tgf() als sein Argument horse() folgendermaßen aufruft: horse.call(newthis[,arg1]...). Dabei kann es z.B. sein (das von trojan) this verwenden. Es kann aber nicht seinen (von trojan) Closure weiterreichen. Nur Bestandteile der Closures von Fktionen können überschrieben/überdeckt werden; =beschränkt sich auf den this Zeiger und damit dessen Unterlemente?

Als Variante könnte man einer Art reflection bzw. Service API bauen, dem man Fktsnamen oder Auflistungsgesuche als Strings mitgibt und die dann intern dynamisch die privaten Funktionen als Funktionen zum weiteren Aufruf (reverseCallback) nach aussen schickt. Die nach aussen geschickten Fkt bringen ihren Closure mit. Dies stellt kein Sicherheitsrisiko an sich dar, da der Code der Funktionen als privater Code die erlaubns hat. Einzig die Service API selbts ist der Gefährder, der in der Schuld des Un-Blackboxings steht.

Beispiel:

//Meine Ergänzung zum API Bereich des imageareplugins

this.trojan = function trojan(horse,ff){
            horse(function(){
                // adjust();
                //Eval wird in der privaten Closure vom imageareaselect Kon-
                //struktorcodeblock evaluiert. Die Closure besteht aus dem
                //Funktionsblock der Konstruktorfunktion und dem Codeblock,
                //welche diesen Codeblock umschließt.
                eval(ff+"()");
            });
        };

Auf der Console:
imgselect.trojan(function tgf(f){console.log("start");f();console.log("end");},"adjust");

Info: Bei der Ausführung von adjust() über den Umweg der Funktion tgf mit f(), hat adjust() wie gesagt seine private Closure, aber der this Zeiger während seiner Codeausführung ist der des Window Objektes. Dies wird hervorgerufen durch den Aufruf von imgselect.trojan() auf der Console, wo der Context das Window Objekt ist.

Das hier geht genauso gut:

this.trojan = function trojan(horse){
            horse(adjust);
        };

-------------------------------------

Zum this Zeiger beim Eventdispatching:

Jedes Dom Element besitzt eventhandler hook Funktionen:
Beim Eventeintritt auf dem Dom Element wird vom Eventdispatcher von JQuery die hook Funktion im Kontext des auslösenden Dom Elements aufgerufen.

elemx.ev1handler = multihandler_1_3;
elemx.ev2handler = multihandler_1_3;
elemx.ev3handler = multihandler_1_3;
elemx.ev4handler = handler4;
elemx.ev5handler = undefined;
...
elemx.evhandler = undefined;

Habe gehört, das JS Singlethreaded ist:

Wenn der Eventdispatcher mit dem this ,zeigend auf das Window Objekt, ausgeführt wird, dann ruft der Dispatcher code den Handler von z.B. elemx.ev2handler so auf:
elemx.ev2handler.call(elemx,event).

Im multihandler_1_3 Funktionscode werden aber weitere direkte Funktionsaufrufe im window Object Kontext aufgerufen. D.h. der durch .call() gesetzte this Context wird nicht fortgeführt. Man müsste dies manuell weiterreichen, indem alle weiteren Funktionsaufrufe statt direkt ebenfalls mit .call() ausgelöst werden.

Codepen BeispielCode:
function f(g){
  console.log(this);
  console.log("start ");
  g();
  //g.call(a);
  //g.call(this);
  console.log("end");}

//globale Variable als Unterobjekt von window
var a = {k:10};
(function (a) {
  console.log(this+"AN ");
  function i1(){
    //Wenn i1 innerhal von a() ohne .call(this)
    //aufgerufen wird, dann bekommt i1 das window Objekt als this Zeiger und nicht etwa den ersten this Zeiger in der Kette der Funktionsaufrufe. Hier z.B. die Variable window.a
    console.log(this.a);//Wurde die Kette der Fkten durch .call(a,f) gestartet
    //dann würde diese Zeile zur Fehlermeldung führen, wenn die Theorie des Fall
    //backs auf dasjenige this zurückzufallen, mit dem die Kette gesrtet wurde.
    //Denn dann wäre das initiale this ja window.a selbst und würde dann mit this.a == window.a.a bedeuten. Die Tatsache, dass this.a aber [Object a] zurück
    //gibt, macht klar das immer auf window zurückgegangen wird.
    console.log("innere  i1");
  }
  var b = {k:5};
  a.call(b,i1);
}.call(a,f));
//(f));//gleiche Wirkung wie: }.call(window,f));



Keine Kommentare:

Kommentar veröffentlichen