Passerar JavaScript med referens?
Passerar JavaScript med referenser eller med värden?
Här är ett exempel från JavaScript: The Good Parts. Jag är väldigt förvirrad över my-parametern för rektangelfunktionen. Den är faktiskt undefined och definieras om inne i funktionen. Det finns inga ursprungliga referenser. Om jag tar bort den från funktionsparametern kan inte den inre area-funktionen komma åt den.
Är det en closure? Men ingen funktion returneras.
var shape = function (config) {
var that = {};
that.name = config.name || "";
that.area = function () {
return 0;
};
return that;
};
var rectangle = function (config, my) {
my = my || {};
my.l = config.length || 1;
my.w = config.width || 1;
var that = shape(config);
that.area = function () {
return my.l * my.w;
};
return that;
};
myShape = shape({
name: "Unhnown"
});
myRec = rectangle({
name: "Rectangle",
length: 4,
width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
Svar #1
Primitiva typer skickas som värde, och objekt skickas som "kopia av en referens".
Specifikt, när du skickar ett objekt (eller en array) skickar du (osynligt) en referens till det objektet, och det är möjligt att ändra innehållet i det objektet, men om du försöker skriva över referensen kommer det inte att påverka kopian av referensen som hålls av anroparen - det vill säga referensen själv skickas efter värde:
function replace(ref) {
ref = {}; // this code does _not_ affect the object passed
}
function update(ref) {
ref.key = 'newvalue'; // this code _does_ affect the _contents_ of the object
}
var a = { key: 'value' };
replace(a); // a still has its original value - it's unmodfied
update(a); // the _contents_ of 'a' are changed
Svar #2
Mina två cent.... Det är irrelevant om JavaScript skickar parametrar som referens eller värde. Vad som verkligen spelar roll är tilldelning kontra mutation.
Jag skrev en längre, mer detaljerad förklaring i den här länken.
När du skickar något (oavsett om det är ett objekt eller en primitiv), gör allt JavaScript är att tilldela en ny variabel inne i funktionen... precis som att använda likhetstecknet (=).
Hur den parametern beter sig inne i funktionen är exakt samma som den skulle bete sig om du bara tilldelade en ny variabel med lika med-tecknet... Ta dessa enkla exempel.
var myString = 'Test string 1';
// Assignment - A link to the same place as myString
var sameString = myString;
// If I change sameString, it will not modify myString,
// it just re-assigns it to a whole new string
sameString = 'New string';
console.log(myString); // Logs 'Test string 1';
console.log(sameString); // Logs 'New string';Om jag skulle passera myString som en parameter till en funktion, beter det sig som om jag helt enkelt tilldelade det till en ny variabel. Nu, låt oss göra samma sak, men med en funktion istället för en enkel tilldelning
function myFunc(sameString) {
// Reassignment... Again, it will not modify myString
sameString = 'New string';
}
var myString = 'Test string 1';
// This behaves the same as if we said sameString = myString
myFunc(myString);
console.log(myString); // Again, logs 'Test string 1';Den enda anledningen till att du kan ändra objekt när du skickar dem till en funktion är att du inte tilldelar om dem... Istället kan objekt ändras eller muteras.... Återigen, det fungerar på samma sätt.
var myObject = { name: 'Joe'; }
// Assignment - We simply link to the same object
var sameObject = myObject;
// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'
sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'
// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'
Om jag skulle skicka myObject som en parameter till en funktion, beter det sig som om jag helt enkelt tilldelade det till en ny variabel. Igen, samma sak med exakt samma beteende men med en funktion.
function myFunc(sameObject) {
// We mutate the object, so the myObject gets the change too... just like before.
sameObject.name = 'Jill';
// But, if we re-assign it, the link is lost
sameObject = {
name: 'Howard'
};
}
var myObject = {
name: 'Joe'
};
// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'Varje gång du skickar en variabel till en funktion, "tilldelar" du vad namnet på parametern än är, precis som om du använde lika med-tecknet =.
Kom alltid ihåg att likhetstecknet = betyder tilldelning. Och att skicka en parameter till en funktion betyder också tilldelning. De är samma sak och de två variablerna är kopplade på exakt samma sätt.
Den enda gången att modifiera en variabel påverkar en annan variabel är när det underliggande objektet muteras.
Det är meningslöst att göra en åtskillnad mellan objekt och primitiva typer, eftersom det fungerar på precis samma sätt som om du inte hade en funktion och bara använde likhetstecknet för att tilldela till en ny variabel.
Svar #3
Tänk på det så här:
När du skapar ett objekt i ECMAscript, bildas detta objekt på en mystisk universell ECMAscript-plats där ingen någonsin kommer att kunna nå. Allt du får tillbaka är en referens till det objektet på denna mystiska plats.
var obj = { };
Även obj är bara en referens till objektet (som är beläget på den där speciella underbara platsen) och därför kan du bara skicka runt denna referens. I praktiken kommer alla kodstycken som får åtkomst till obj att modifiera objektet som ligger långt, långt borta.
Svar #4
Funktionsargument skickas antingen med värde eller med delning, men aldrig aldrig med referens i JavaScript!
Anrop efter värde
Primära typer skickas efter värde:
var num = 123, str = "foo";
function f(num, str) {
num += 1;
str += "bar";
console.log("inside of f:", num, str);
}
f(num, str);
console.log("outside of f:", num, str);Omfördelningar inom ett funktionsomfång är inte synliga i det omgivande omfånget.
Detta gäller också Strings, som är en sammansatt datatyp och ändå oföränderliga:
var str = "foo";
function f(str) {
str[0] = "b"; // doesn't work, because strings are immutable
console.log("inside of f:", str);
}
f(str);
console.log("outside of f:", str);Anrop-via-delning
Objekt, det vill säga alla typer som inte är primitiva, passeras genom delning. En variabel som håller en referens till ett objekt håller egentligen endast en kopia av denna referens. Om JavaScript skulle använda en call-by-reference-utvärderingsstrategi, skulle variabeln hålla den ursprungliga referensen. Detta är den avgörande skillnaden mellan delning och referens.
Vilka är de praktiska konsekvenserna av denna skillnad?
var o = {x: "foo"}, p = {y: 123};
function f(o, p) {
o.x = "bar"; // Mutation
p = {x: 456}; // Reassignment
console.log("o inside of f:", o);
console.log("p inside of f:", p);
}
f(o, p);
console.log("o outside of f:", o);
console.log("p outside of f:", p);Mutera betyder att modifiera vissa egenskaper hos ett befintligt Object. Referenskopian som en variabel är bunden till och som refererar till detta objekt förblir densamma. Mutationer är således synliga i anroparens scope.
Omfördelning innebär att ersätta referenskopian som är bunden till en variabel. Eftersom det endast är en kopia påverkas inte andra variabler som håller en kopia av samma referens. Omfördelningar är därför inte synliga i anroparens scope på samma sätt som de skulle vara med en anropa-via-referens-utvärderingsstrategi.
Ytterligare information om utvärderingsstrategier i ECMAScript.
Svar #5
Som med C, i slutändan passerar allt värde. Till skillnad från C kan du faktiskt inte gå tillbaka och skicka platsen för en variabel, eftersom den inte har pekare, bara referenser.
Och referenserna den har är alla till objekt, inte till variabler. Det finns flera sätt att uppnå samma resultat, men de måste göras för hand, inte bara genom att lägga till ett nyckelord vid antingen anrops- eller deklarationsplatsen.
