pass by reference - verständnisproblem
jobo
- javascript
Hallo,
Douglas Crockford schreibt: "JavaScript is a Pass By Reference language. That means that when you pass a value to a function, you're not passing a copy of it and you're not passing the name of it, you're passing a reference to it. In oldspeak that would be a pointer. Sometimes it's a little confusing when values are mutable, as some JavaScript values are, and Pass by Value and Pass By Reference look the same, but JavaScript is a Pass By Reference system as most systems are today."
Und hat Beispiele dafür:
variable = {};
a = variable;
b = variable;
alert(a === b); //true
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x); // Object!!!
//////////////////////
function swap(a, b) {
var t = a;
a = b;
b = t
}
var x = 1, y = 2;
swap(x, y);
alert(x); // 1!!
Dass a ==== b ist leuchtet ein. Aber warum ändern die beiden folgenden Funktionen nicht den Wert von x??? Ich dachte, pass-by-reference heißt eben, wie er auch sagt, einen Pointer übergeben. Wenn ich in PHP &$var übergebe, dann kann ich genau diesen Effekt ja erzielen, dass ich eben einen Verweis/Referenz habe auf die Variable in der Funktion und somit an der Variablen Änderungen vornehmen kann.
<?php
$var = "start";
function ref(&$var) {
$var = "ref";
}
function noref($var) {
$var = "noref";
}
echo $var; // start
noref($var);
echo $var; // start
ref($var);
echo $var; // ref
Was kapiere ich hier falsch?
my_object = {
blah : "blah"
}
function storer(obj, name) {
return function (result) {
obj[name] = result;
};
}
my_inputs = "newInput";
do_it(my_inputs, storer(my_object, 'blah'));
alert(my_object.blah); //newInput
setzt my_object.blah in dem Fall wohl auf die neue Var, weil das Objekt bzw. die Variable global ist? Die Beispielcodes finden sich übrigens auch hier.
Gruß
jobo
Hi,
Dass a ==== b ist leuchtet ein. Aber warum ändern die beiden folgenden Funktionen nicht den Wert von x???
weil Du nicht das referenzierte Objekt änderst, sondern einer lokalen Variable eine neue Referenz gibst.
setzt my_object.blah in dem Fall wohl auf die neue Var, weil das Objekt bzw. die Variable global ist?
Nein, weil Du ein Objekt änderst anstatt eine lokale Variable zu überschreiben. Ändere in Deiner funky()-Funktion mal o.irgendwas.
Cheatah
Hallo Cheatah,
merci.
Dass a ==== b ist leuchtet ein. Aber warum ändern die beiden folgenden Funktionen nicht den Wert von x???
weil Du nicht das referenzierte Objekt änderst, sondern einer lokalen Variable eine neue Referenz gibst.
Du meinst, o ist dann nicht mehr das Paramter-o aus der Funktion? Aber beim PHP-Beispiel ist doch genau das der Fall, oder? Zumindest, wenn ich den Referenzoperator zufüge (&).
setzt my_object.blah in dem Fall wohl auf die neue Var, weil das Objekt bzw. die Variable global ist?
Nein, weil Du ein Objekt änderst anstatt eine lokale Variable zu überschreiben. Ändere in Deiner funky()-Funktion mal o.irgendwas.
function funky2 (o) {
o.irgendwas = "irgendwas";
}
var x = {};
funky2(x);
alert(x.irgendwas); // "irgendwas"
Äh, aber wie würde ich dann o eine neue Variable zuordnen? Oder im Beispiel, wie müsste swap aussehen, damit es wirklich swapped? (Bei PHP ginge das ja mit dem &-Operator, bei C vermutlich mit dem Pointerzeichen (*)).
function swap(a, b) {
var t = a;
a = b;
b = t
}
Bringts ja nicht, aber analog zu obigem Beispiel würde es gehen, wenn es Eingeschaften eines Objektes wären, oder?
function mySwap(obj, a,b) {
var t = obj[a];
obj[a] = obj[b];
obj[b] = t
}
var myObj = {
x : "objVal1",
y : "objVal2"
}
mySwap(myObj,"x", "y");
alert(myObj.x); //ojbVal2
Dank und Gruß
jobo
Hallo,
Crockford sagt weiter:
"One problem with that is that do_it could hold onto that function that I passed and use it again, perhaps some time when it's not to my advantage. Maybe I don't want to allow him to do that, that he can only store once into that variable. So I'll create a once function, and the once function will take a function and return a function which wraps it and which guarantees that it can only be called once. If you try to call it a second time, you'll get a null exception and it'll fail. So there's the function again. We're taking advantage of closure. When the function it returns is calm, it nulls out func, and that guarantees that it cannot be called usefully again. That allows us to have it called only once. Then I can take the result of storer and pass that once, and now we've got a function that can only be called once. Doing this, I've made a pretty big change to what do_it is able to do, without changing do_it. Because none of this is do_it's business."
Ich probiere das aus:
my_object = {
blah : "blah"
}
function do_it(inputs, callback) {
result = inputs;
callback(result);
}
function storer(obj, name) {
return function (result) {
obj[name] = result;
};
}
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(this, arguments);
};
}
my_inputs = "fourthInput";
do_it(my_inputs, once(storer(my_object, 'blah')));
alert("first call do_it: " + my_object.blah); //fourthInput
my_inputs = "fifthInput";
do_it(my_inputs, once(storer(my_object, 'blah')));
alert("second call do_it: " + my_object.blah);//fifthInput
my_inputs = "sixthInput";
do_it(my_inputs, storer(my_object, 'blah'));
alert("third call do_it: " + my_object.blah); //sixthInput
Aber weil func in der Funktion once ja ausgenullt wurde, dürfte es eigentlich ja nicht möglich sein, dass die Funktion ein zweites Mal ausgeführt wird. Anderseits ist das ja das Problem wie am Anfang: die Variable func existiert ja nur im Scope von once, hat also mit dem Parameter ja nix zu tun. Ich habe extra nochmal den Vortrag von Crockford angeschaut, ob der Code auch korrekt transcribiert wurde. Wurde er. Ist der Code falsch? Oder habe ich was falsch verstanden?
Gruß
jobo
[latex]Mae govannen![/latex]
Dass a ==== b ist leuchtet ein. Aber warum ändern die beiden folgenden Funktionen nicht den Wert von x??? Ich dachte, pass-by-reference heißt eben, wie er auch sagt, einen Pointer übergeben.
Das bezieht sich nicht auf die sogenannten "Primitives". Diese werden in der Funktion als Kopie verwendet, eine Änderung innnerhalb der Funktion ändert also den Wert der als Parameter übergebenen ursprünglichen Variable in diesem Fall nicht.
Mathias hat das hier beschrieben: http://molily.de/js/kernobjekte.html#objects-primitives (4; 4.1)
Cü,
Kai
[latex]Mae govannen![/latex]
Das bezieht sich nicht auf die sogenannten "Primitives". Diese werden in der Funktion als Kopie verwendet, eine Änderung innnerhalb der Funktion ändert also den Wert der als Parameter übergebenen ursprünglichen Variable in diesem Fall nicht.
Eine allgemeine Swap-Funktion für beliebige primitive Werte ist somit nicht machbar. Aber man kann es sich zumindest zu einem Einzeiler vereinfachen:
var x = 1, y = 2;
x = [y, y = x][0]; // swap
alert(x); // 2
Cü,
Kai
Hallo,
Das bezieht sich nicht auf die sogenannten "Primitives". Diese werden in der Funktion als Kopie verwendet, eine Änderung innnerhalb der Funktion ändert also den Wert der als Parameter übergebenen ursprünglichen Variable in diesem Fall nicht.
Mathias hat das hier beschrieben: http://molily.de/js/kernobjekte.html#objects-primitives (4; 4.1)
Fein. Das ist ja dann analog zu PHP, wo seit PHP5 der "&"-Operator für Objekte nicht mehr nötig ist, weil sie immer als Referenz übergeben werden. Auch ist dann wohl die Aussage von Crockford ungenau und sein Beispiel merkwürdigerweise auch unpassend, wenn man da nicht Moilys Erklärung für beigibt.
Wie aber verhält es sich mit der Übergabe einer Funktion, wie im Beispiel function once(func)? Das müsste doch klappen, hätte ich mal gedacht. Crockford erläutert doch in solch einer zentralen Sache keinen Unfug, würde ich meinen. Also schließe ich daraus: Verständnislücke bei mir erstmal suchen.
Gruß
jobo
Hallo,
Das bezieht sich nicht auf die sogenannten "Primitives". Diese werden in der Funktion als Kopie verwendet, eine Änderung innnerhalb der Funktion ändert also den Wert der als Parameter übergebenen ursprünglichen Variable in diesem Fall nicht.
Mathias hat das hier beschrieben: http://molily.de/js/kernobjekte.html#objects-primitives (4; 4.1)
Was aber ist hiermit?
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x);
Hier wird ja ein Objekt übergeben.
Gruß
jobo
[latex]Mae govannen![/latex]
Was aber ist hiermit?
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x);
>
> Hier wird ja ein Objekt übergeben.
Offenbar wird eine interne Kopie angelegt, sobald die Parameter-Variable »o« überschrieben wird.
Belege muß ich schuldig bleiben; ich bin leider schon seit ~3 Jahren im Status "Will mich mal irgendwann™ näher mit JS-Interna befassen" gefangen. Scheiß Englisch. :( Aber hier lesen sicherlich auch Leute mit Ahnung mit.
Ich würde wohl, faul wie ich bin, wenn ich auf obigen Fall treffen würde, ganz pragmatisch vorgehen und »o« einfach explizit als Rückgabewert der Funktion definieren, ohne mich um das „Warum“ zu kümmern.
Cü,
Kai
--
~~~ ken SENT ME ~~~
Dank Hixies Idiotenbande geschieht grade eben wieder ein Umdenken
in Richtung "Mess up the Web".([suit](https://forum.selfhtml.org/?t=197497&m=1324775))
[SelfHTML-Forum-Stylesheet](http://selfhtml.knrs.de/#h_stylesheet)
Hallo,
[latex]Mae govannen![/latex]
Was aber ist hiermit?
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x);
> >
> > Hier wird ja ein Objekt übergeben.
>
> Offenbar wird eine interne Kopie angelegt, sobald die Parameter-Variable »o« überschrieben wird.
>
> Belege muß ich schuldig bleiben; ich bin leider schon seit ~3 Jahren im Status "Will mich mal irgendwann™ näher mit JS-Interna befassen" gefangen. Scheiß Englisch. :( Aber hier lesen sicherlich auch Leute mit Ahnung mit.
>
> Ich würde wohl, faul wie ich bin, wenn ich auf obigen Fall treffen würde, ganz pragmatisch vorgehen und »o« einfach explizit als Rückgabewert der Funktion definieren, ohne mich um das „Warum“ zu kümmern.
Das Beispiel war ja nur wegen des "Warum" (;-).
Warum kann ich das Objekt nicht überschreiben, wohl aber eine Eigenschaft/Property? Und wieso nennt sich das dann dennoch pass-by-reference? Vielleicht tauchen ja molily oder struppi auf. Oder ich versuche mal, dass da irgendwo in Crockfordnähe zu klären.
Gruß
jobo
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x);
> >
> Offenbar wird eine interne Kopie angelegt, sobald die Parameter-Variable »o« überschrieben wird.
In dem verlinkten Teil meiner Doku bringe ich verschiedene Sachen durcheinander. Das war ein Missverständnis meinerseits. Es ist nicht ganz falsch, aber auch nicht ganz richtig.
Eine Variable enthält entweder einen Primitive Value oder eine Referenz auf ein Object. Es ist im Gegensatz zu anderen Sprachen in JavaScript nicht möglich, den Wert von Variablen zu ändern, welche einer Funktion übergeben werden. Dieses Konzept gibt es einfach nicht. Man kann keine »Variablen übergeben«. Man übergibt immer Werte. Der lokalen Funktionsvariable wird beim Aufrufen der Funktion der übergebene Wert zugewiesen. Beispiel mit Primitives:
~~~javascript
function f (primitive) {
alert(primitive);
primitive = 2; // Ändert nicht p
}
var p = 1;
f(p);
alert(p); // bleibt 1
Der Wert ist hier ein Number-Primitive.
Bei Objects passiert im Grunde dasselbe. Jetzt muss man eigentlich nur wissen, dass Objects immer in Form von Referenzen vorliegen. Beim Übergeben wird also nur eine Referenz auf eine Speicherstelle übergeben. Die lokale Funktionsvariable enthält dann diese Referenz als Wert. Die REFERENZ wird KOPIERT, aber nicht das dahinterliegende Object.
function f (object) {
alert(object.prop);
object.prop = 321; // Das ist möglich
object = null; // Ändert nicht o
}
var o = { prop: 123 };
f(o);
alert(o); // bleibt dasselbe Object, wird NICHT null
alert(o.prop); // 321
Hier gibt es zu jeder Zeit nur ein Object im Speicher.
Wenn JavaScript call by reference erlauben würde, müsste folgendes gehen:
function funky (o) {
o = null;
}
var x = {};
funky(x);
alert(x);
Die Zuweisung zu o kann hier aber niemals den Wert der Variable x ändern. Die Variable o ist nicht an die Variable x gebunden. Es ist einfach eine lokale Variable, die beim Funktionsaufruf mit dem übergebenen Wert gefüllt wird (hier eine Object-Referenz). Weist man ihr einen neuen Wert zu, so hat das auf das referenzierte Object keinen Einfluss.
ich bin leider schon seit ~3 Jahren im Status "Will mich mal irgendwann™ näher mit JS-Interna befassen" gefangen
Da kann ich nur die Lektüre von ECMA-262 in detail empfehlen.
Zu meiner Doku:
Primitives werden als Kopie an Funktionen übergeben, während Objekte als Referenzen auf dieselbe Speicherstelle (in anderen Programmiersprachen »Zeiger« genannt) übergeben werden.
Das ist soweit noch richtig.
Gegeben ist folgender Fall: Sie notieren ein Objekt als Variable. Dieses Objekt übergeben Sie einer Funktion und in der Funktion nehmen Sie Änderungen am Objekt vor, fügen ihm z.B. eine Eigenschaft hinzu.
Wenn das Objekt als Referenz übergeben wird, dann haben Sie nach dem Funktionsaufruf auf das geänderte Objekt Zugriff. Denn an beiden Stellen, innerhalb und außerhalb der Funktion, haben Sie Zugriff auf ein und dasselbe Objekt.
Wenn das Objekt als Primitive übergeben wird, dann haben Änderungen daran keine Auswirkung auf die Variable im ursprünglichen Kontext – es sei denn, die Funktion gibt einen Primitive zurück und Sie arbeiten mit dem Rückgabewert der Funktion weiter.
Das ist missverständlich und ich verwende hier unsaubere Terminologie (ich wollte es einfach halten, aber das geht hier nach hinten los).
Ein Objekt (Object) wird immer als Referenz übergeben. Sonst wäre es kein Object, sondern ein Primitive. Man kann sich nicht aussuchen, wie man ein Object übergibt. Ein Object als Primitive zu übergeben, geht nicht, es sei denn, man wandelt es explizit vorher um.
Einen Einfluss auf die VARIABLE im ursprünglichen Kontext hat man in beiden Fällen nicht.
Ein Object kann man ändern, man kann seine Eigenschaften ändern. Dann ist das geänderte Object auch überall dort verfügbar, wo Referenzen darauf existieren, denn durch Übergeben werden Objects nicht kopiert.
Bei Primitives gibt es diese Möglichkeit, »sie selbst« zu verändern, schlicht nicht - sie haben keine (dauerhaften) Eigenschaften. Mit einem Funktionsaufruf wird eine neue Schublade aufgemacht und ein einfacher Wert darin abgelegt. Legt man etwas anderes dort hinein, ändert sich nicht plötzlich der Inhalt anderer Schubladen in ganz anderen Schränken.
Mathias
Mit einem Funktionsaufruf wird eine neue Schublade aufgemacht und ein einfacher Wert darin abgelegt. Legt man etwas anderes dort hinein, ändert sich nicht plötzlich der Inhalt anderer Schubladen in ganz anderen Schränken.
Je länger ich darüber nachdenke, so mehr erscheint mir das als passende Metapher. Gut, es ist nichts neues, Variablen als »Schubladen« zu veranschaulichen. Man muss dieses Bild allerdings noch modifizieren:
Es gibt verschiedene Regale (Variablenobjekte) mit benannten Schubladen (Variablen). Unter einem Namen sind dort Werte abgelegt. var a = 1; erzeugt eine Schublade mit dem Etikett a, darin liegt die Nummer 1.
Im Falle eines Objects, beispielsweise var a = {}, liegt der Wert nicht direkt in der Schublade. Sondern er liegt in einem separaten Object-Regal, das von den Variablen-Regalen getrennt ist. Dort ist es unter einer eindeutigen Nummer zu finden, meinetwegen 14825. In der Variablen-Schublade »a« liegt nur ein Zettel, auf dem steht: »siehe Object 14825«.
Beim Übergeben des Werts sowie beim Zuweisen an eine Variable wird immer mit einem Verweis-Zettel hantiert: Es wird ein weiterer Verweis-Zettel angelegt, auf dem dasselbe steht. Wenn man mit dem tatsächlichen Wert arbeiten will (Operatoren darauf anwendet), so wird anhand des Verweis-Zettels das Object aus dem Object-Regal geholt.
Solange es Zettel gibt, die auf ein Object verweisen, wird es im Object-Regal aufbewahrt. Andernfalls wird es beim Aufräumen irgendwann entfernt (Garbage Collection).
Mathias
Hallo,
Mit einem Funktionsaufruf wird eine neue Schublade aufgemacht und ein einfacher Wert darin abgelegt. Legt man etwas anderes dort hinein, ändert sich nicht plötzlich der Inhalt anderer Schubladen in ganz anderen Schränken.
Je länger ich darüber nachdenke, so mehr erscheint mir das als passende Metapher. Gut, es ist nichts neues, Variablen als »Schubladen« zu veranschaulichen. Man muss dieses Bild allerdings noch modifizieren:
Es gibt verschiedene Regale (Variablenobjekte) mit benannten Schubladen (Variablen). Unter einem Namen sind dort Werte abgelegt. var a = 1; erzeugt eine Schublade mit dem Etikett a, darin liegt die Nummer 1.
Im Falle eines Objects, beispielsweise var a = {}, liegt der Wert nicht direkt in der Schublade. Sondern er liegt in einem separaten Object-Regal, das von den Variablen-Regalen getrennt ist. Dort ist es unter einer eindeutigen Nummer zu finden, meinetwegen 14825. In der Variablen-Schublade »a« liegt nur ein Zettel, auf dem steht: »siehe Object 14825«.
Beim Übergeben des Werts sowie beim Zuweisen an eine Variable wird immer mit einem Verweis-Zettel hantiert: Es wird ein weiterer Verweis-Zettel angelegt, auf dem dasselbe steht. Wenn man mit dem tatsächlichen Wert arbeiten will (Operatoren darauf anwendet), so wird anhand des Verweis-Zettels das Object aus dem Object-Regal geholt.
Solange es Zettel gibt, die auf ein Object verweisen, wird es im Object-Regal aufbewahrt. Andernfalls wird es beim Aufräumen irgendwann entfernt (Garbage Collection).
Erklärt sich so auch, warum ich zwar eine Objektvariable ändern kann (obj.x) wenn mir obj als Paramter übergeben wurde, nicht aber das Objekt selbst auslöschen kann?
Schubladenmodelle finde ich sehr anschaulich. So kann man ja auch Pointer veranschaulichen. Ein reservierte Speicher im RAM ist ja auch nix anderes als eine Schublade eigentlich. Ich wollte mal ein Stackmodell mit Streichholzschachteln basteln (;-).
Gruß
jobo
Erklärt sich so auch, warum ich zwar eine Objektvariable ändern kann (obj.x) wenn mir obj als Paramter übergeben wurde, nicht aber das Objekt selbst auslöschen kann?
Ja. Wenn du nur den Zettel besitzt, kannst du damit auf das Object zugreifen und dessen Eigenschaften ändern.
Was du aber nicht ändern kannst, ist der Inhalt einer Schublade in einem anderen Regal, in dem zufällig ein gleicher Verweis-Zettel liegt. Von dem weißt du gar nicht - zumindest nicht darüber, dass du den Verweis-Zettel besitzt. Lediglich über die Scope-Chain wäre der Zugriff auf Variablen eines anderen Regals möglich.
Schubladenmodelle finde ich sehr anschaulich. So kann man ja auch Pointer veranschaulichen.
Bei Call by Reference gäbe es eben keine Verweis-Zettel und keine Trennung zwischen »Variablen-Regalen« und »Object-Regalen«. Es gäbe nur ein Regal, in dem dann auch das wirklich Object gespeichert ist. Bei der Übergabe wird nicht eine weitere Schublade aufgemacht, sondern ein und dieselbe Schublade verfügbar gemacht. Diese Schublade kann dann verschiedene Namen tragen.
Mathias
Hallo,
Erklärt sich so auch, warum ich zwar eine Objektvariable ändern kann (obj.x) wenn mir obj als Paramter übergeben wurde, nicht aber das Objekt selbst auslöschen kann?
Ja. Wenn du nur den Zettel besitzt, kannst du damit auf das Object zugreifen und dessen Eigenschaften ändern.
Was du aber nicht ändern kannst, ist der Inhalt einer Schublade in einem anderen Regal, in dem zufällig ein gleicher Verweis-Zettel liegt. Von dem weißt du gar nicht - zumindest nicht darüber, dass du den Verweis-Zettel besitzt. Lediglich über die Scope-Chain wäre der Zugriff auf Variablen eines anderen Regals möglich.
Wie bist du denn auf Soshnikov gestoßen?
Gruß
jobo
Wie bist du denn auf Soshnikov gestoßen?
Ich lese zahlreiche Blogs und Twitterer, die sich mit JavaScript beschäftigen, wahrscheinlich hat ihn jemand verlinkt. Was die Übersetzung von JavaScript. The core. betrifft, so hatte er mich anschrieben weil er meine Artikel zu JS-OOP gefunden hatte.
Mathias
Hallo,
Wie bist du denn auf Soshnikov gestoßen?
Ich lese zahlreiche Blogs und Twitterer, die sich mit JavaScript beschäftigen, wahrscheinlich hat ihn jemand verlinkt. Was die Übersetzung von JavaScript. The core. betrifft, so hatte er mich anschrieben weil er meine Artikel zu JS-OOP gefunden hatte.
Fein. Da schließt sich bei Dir ja auch der Kreis zu Crockford. Ich war immer am überlegen, ob ich mich eher mit YUI oder Dojo befassen sollte. YUI wegen Crockford, weil mich seine Vorträge und seine Art, die Dinge überschaubar darzustellen ansprechen. Dojo, weil es im Zend-Framework partnerschaftlich mit eingebunden ist, und ich das wiederum sehr stringent finde.
Gruß
jobo
Ich dachte, pass-by-reference heißt eben, wie er auch sagt, einen Pointer übergeben. Wenn ich in PHP &$var übergebe, dann kann ich genau diesen Effekt ja erzielen, dass ich eben einen Verweis/Referenz habe auf die Variable in der Funktion und somit an der Variablen Änderungen vornehmen kann.
Damit hast du völlig Recht. Es ist irreführend zu sagen, dass JavaScript pass by reference macht. Lies dir mal diesen Thread durch:
</archiv/2010/10/t200805/#m1353932>, speziell </archiv/2010/10/t200805/#m1354040>
JavaScript macht weder striktes pass by reference noch striktes pass by value. Der Artikel [http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/] erklärt die Unterschiede sehr gut. Dort wird das Verhalten von JS »call by sharing« bzw. »call by value where value is the reference copy« genannt.
<?php
$var = "start";
function ref(&$var) {
$var = "ref";
}
echo $var; // start
ref($var);
echo $var; // ref
So etwas geht nicht in JavaScript. Eine JavaScript-Funktion kann nie den Wert einer Variable ändern, die von außen in die Funktion hineingegeben wurde. Eine Swap-Funktion ist daher in JavaScript nicht möglich. Das wäre, was m.W. nötig wäre, um von pass by reference sprechen zu können.
my_object = {
blah : "blah"
}
function storer(obj, name) {
return function (result) {
obj[name] = result;
};
}
my_inputs = "newInput";
do_it(my_inputs, storer(my_object, 'blah'));
alert(my_object.blah); //newInput
>
> setzt my\_object.blah in dem Fall wohl auf die neue Var, weil das Objekt bzw. die Variable global ist?
Nein, das hat nichts damit zu tun, dass das Objekt global it. Das ist ein ganz anderer Fall. Wie Cheatah sagst änderst du hier die Eigenschaft eines Objekts, auf dass du durch eine Closure Zugriff hast.
Mathias
Hallo Matthias,
Danke für die Antwort(en) und Links.
Nein, das hat nichts damit zu tun, dass das Objekt global it. Das ist ein ganz anderer Fall. Wie Cheatah sagst änderst du hier die Eigenschaft eines Objekts, auf dass du durch eine Closure Zugriff hast.
Weil hier das Objekt wie eine Array funktioniert, ich also Elemente anhängen kann bzw. Arrayelemente eben überschreiben kann? Denn Fakt ist ja: Properties/Eigentschaften kann ich überschreiben oder neu hinzufügen. Das Objekt selbst aber kann ich nicht nullen. In dem Fall ist die Aussage von Crockford also eigentlich wirklich nicht wahr:
"JavaScript is a Pass By Reference language. That means that when you pass a value to a function, you're not passing a copy of it and you're not passing the name of it, you're passing a reference to it. In oldspeak that would be a pointer."
Denn gerade eben der Pointer würde mir ja _alles_ das erlauben, denn ich erhalte damit ja den direkten Zugriff, weil ich die Speicheradresse kenne. Schade dass sich bei dem Vortrag kein Kommentar abgeben lässt. Nr. 6 ist im yuiblog veröffentlicht, da geht das.
Kannst Du mir vielleicht noch bei der Funktion once() weiterhelfen?
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(this, arguments);
};
}
Die soll laut Crockford ja bewirken
"One problem with that is that do_it could hold onto that function that I passed and use it again, perhaps some time when it's not to my advantage. Maybe I don't want to allow him to do that, that he can only store once into that variable. So I'll create a once function, and the once function will take a function and return a function which wraps it and which guarantees that it can only be called once."
In meinem Testbeispiel funktioniert das aber nicht.
Der Hintergedanke ist doch wohl der eines Closures, oder? Wenn once zum zweiten Mal aufgerufen wird, ist intern func (immer noch) null, im Scope der Funktion, und der übergebene Paramter func sollte das dann wohl nicht überschreiben, insofern wäre var f dann auch null und könnte nicht "applied" werden. Oder, was meinst Du?
Dank und Gruß
jobo
»»» Kannst Du mir vielleicht noch bei der Funktion once() weiterhelfen?
my_inputs = "fourthInput";
do_it(my_inputs, once(storer(my_object, 'blah')));
alert("first call do_it: " + my_object.blah); //fourthInput
my_inputs = "fifthInput";
do_it(my_inputs, once(storer(my_object, 'blah')));
alert("second call do_it: " + my_object.blah);//fifthInput
Du rufst once() hier zweimal auf. once gibt jeweils eine Funktion zurück, die man nur einmal nutzen kann. Sie wrappt dazu die übergebene Funktion.
Der Witz hier ist eben, dass du die Funktion nur einmal nutzt. Beim zweiten mal verwendest du sie nicht wieder, sondern legst eine neue Funktion an, indem du once() nochmal aufrufst.
Was du hier machst, ist äquivalent zu folgendem:
var once1 = once(storer(my_object, 'blah'));
once1("fourthInput");
alert(my_object.blah); //fourthInput
var once2 = once(storer(my_object, 'blah'));
once2("fifthInput");
alert(my_object.blah); //fifthInput
once1 ist eine andere Wrapperfunktion als once2. Crockford aber redet davon, dass once1 nur einmal verwendet werden kann. Versuch sie mal wiederzuverwenden:
var once_storer = once(storer(my_object, 'blah'));
once_storer("fourthInput");
alert(my_object.blah); //fourthInput
once_storer("fifthInput"); // Exception
Das geht natürlich nicht, weil die Wrapper-Funktion das verhindert.
Deine Beispiele sind auch etwas gestellt. Crockford redet von asynchronen Callbacks z.B. bei Ajax, beim Abarbeiten von Listen oder einfach dem Ausführen verschiedener Aufgaben hintereinander. do_it() macht also nur Sinn, wenn es einen Payload hat, der ausgeführt wird, bevor das Ergebnis an die Callback-Funktion weitergegeben wird (in deinen Beispiel ist es nur ein unnötiger Wrapper). Dazu beschreibt er die Möglichkeit, Callbacks funktional zu erzeugen, sodass sie gemäß dem »object capability security system« Zugriff auf die nötigen Daten »by introduction« haben und auf keine sonstigen Daten. Es geht hier um die Trennung von Aufgaben und damit die Trennung von Zugriffsrechten, deshalb betont er ständig »none of this is do_it's business«. In dem Zusammenhang soll die Ausführung von Callbacks durch das wrappen mit once() limitiert werden. Das ist eben weder die Aufgabe von do_it noch des Callbacks an sich. Wichtig ist hier, dass do_it keinen direkten Zugriff auf den Callback hat (technisch gesehen in deinen Beispielen schon, aber das soll eben vermieden werden). Es bekommt nur die Wrapper-Funktion übergeben. Und diese kann eben nur einmal ausgeführt werden.
Mathias
Hallo Matthias,
Du rufst once() hier zweimal auf. once gibt jeweils eine Funktion zurück, die man nur einmal nutzen kann. Sie wrappt dazu die übergebene Funktion.
Ok, jetzt ist der Groschen offenbar gefallen.
var once_storer = once(storer(my_object, 'blah'));
once_storer("fourthInput");
alert(my_object.blah); //fourthInputonce_storer("fifthInput"); // Exception
>
> Das geht natürlich nicht, weil die Wrapper-Funktion das verhindert.
>
Fein.
> Deine Beispiele sind auch etwas gestellt.
Die Codeschnipsel stammen alle aus dem Vortrag und sind auf der [Yahooseite](http://developer.yahoo.com/yui/theater/video.php?v=crockonjs-5) nicht transkribiert, dafür bei [slideshare](http://www.slideshare.net/douglascrockford/crockford-on-javascript-part-5-the-end-of-all-things).
> Crockford redet von asynchronen Callbacks z.B. bei Ajax, beim Abarbeiten von Listen oder einfach dem Ausführen verschiedener Aufgaben hintereinander. do\_it() macht also nur Sinn, wenn es einen Payload hat, der ausgeführt wird, bevor das Ergebnis an die Callback-Funktion weitergegeben wird (in deinen Beispiel ist es nur ein unnötiger Wrapper).
Ja, offenbar falsch die Beispiele zusammengestellt.
> Dazu beschreibt er die Möglichkeit, Callbacks funktional zu erzeugen, sodass sie gemäß dem »object capability security system« Zugriff auf die nötigen Daten »by introduction« haben und auf keine sonstigen Daten.
Ja, das Grundprinzip war mir schon eingängig. do\_it ist nur da, um es zu "machen", der "dispatcher" vielleicht. Es weiß nur, wem es den Result übergeben muss. "Pack das Ding da rein" ist seine Aufgabe. Es weiß nicht, woher "das Ding" kommt und auch nicht, was "da drin" damit passiert.
> Es geht hier um die Trennung von Aufgaben und damit die Trennung von Zugriffsrechten, deshalb betont er ständig »none of this is do\_it's business«. In dem Zusammenhang soll die Ausführung von Callbacks durch das wrappen mit once() limitiert werden. Das ist eben weder die Aufgabe von do\_it noch des Callbacks an sich. Wichtig ist hier, dass do\_it keinen direkten Zugriff auf den Callback hat.
Genau.
> (technisch gesehen in deinen Beispielen schon, aber das soll eben vermieden werden).
Weil ich nicht wie Du erläuterst, eine Instanz der Funktion erzeugt habe.
> Es bekommt nur die Wrapper-Funktion übergeben. Und diese kann eben nur einmal ausgeführt werden.
Danke vielmals. Ich dachte schon, dass das eine zentraler Punkt ist, wenn Crockford das auch explizit so voranführt:
"It's important to understand this because people often get confused between values and variables and misunderstand what their relative goals are...". Und das Verständnis der Sicherheit, die sich aus seiner Sicht ja im Grunde mit Javascript selbst erzeugen lässt, basieren ja darauf: "This is a really important thing to understand about the language because all of the security properties we're going to talk about later derive from this."
... " JavaScript gets beat up for its security problems, but it is actually closer to being a secure language than virtually any other language out there. You just have to recognize the pattern and make it work."
Gruß
jobo