Czy JavaScript jest językiem przekazującym lub przekazującym wartość?

Feb 06 2009

Typy pierwotne (liczba, ciąg znaków itp.) Są przekazywane przez wartość, ale obiekty są nieznane, ponieważ oba mogą być przekazywane przez wartość (w przypadku, gdy uznamy, że zmienna przechowująca obiekt jest w rzeczywistości odniesieniem do obiektu ) i przekazane przez referencję (jeśli weźmiemy pod uwagę, że zmienna obiektu zawiera sam obiekt).

Chociaż na końcu nie ma to większego znaczenia, chcę wiedzieć, jaki jest właściwy sposób przedstawienia argumentów zgodnych z konwencjami. Czy istnieje fragment specyfikacji JavaScript, który określa, jaka powinna być semantyka w tym zakresie?

Odpowiedzi

1665 deworde Sep 04 2010 at 00:17

Jest to interesujące w JavaScript. Rozważmy ten przykład:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

To daje wynik:

10
changed
unchanged
  • Gdyby w obj1ogóle nie było odniesieniem, to zmiana obj1.itemnie miałaby wpływu na obj1zewnętrzną stronę funkcji.
  • Gdyby argument był właściwym odniesieniem, wszystko by się zmieniło. numbyłby 100i obj2.itemczytał "changed".

Zamiast tego sytuacja jest taka, że ​​przekazywany element jest przekazywany według wartości. Ale element, który jest przekazywany przez wartość, sam jest odwołaniem. Z technicznego punktu widzenia nazywa się to udostępnianiem połączeń .

W praktyce oznacza to, że jeśli zmienisz sam parametr (jak w przypadku numi obj2), nie wpłynie to na element, który został wprowadzony do parametru. Ale jeśli zmienisz INTERNALS parametru, nastąpi propagacja kopii zapasowej (tak jak w przypadku obj1).

493 TimGoodman Mar 15 2011 at 23:38

Jest zawsze przekazywany przez wartość, ale dla obiektów wartość zmiennej jest odniesieniem. Z tego powodu, gdy przekazujesz obiekt i zmieniasz jego składowe , te zmiany pozostają poza funkcją. To sprawia, że wygląda jak przekazywanie przez odniesienie. Ale jeśli faktycznie zmienisz wartość zmiennej obiektu, zobaczysz, że zmiana nie jest trwała, udowadniając, że naprawdę jest przekazywana przez wartość.

Przykład:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

Wynik:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar
156 Shog9 Feb 06 2009 at 04:37

Zmienna nie "trzyma" obiektu; zawiera odniesienie. Możesz przypisać to odniesienie do innej zmiennej, a teraz oba odnoszą się do tego samego obiektu. Jest zawsze przekazywany przez wartość (nawet jeśli ta wartość jest odniesieniem ...).

Nie ma możliwości zmiany wartości przechowywanej przez zmienną przekazaną jako parametr, co byłoby możliwe, gdyby JavaScript obsługiwał przekazywanie przez referencję.

115 RayPerea Aug 04 2014 at 18:06

Moje dwa centy ... Tak to rozumiem. (Możesz mnie poprawić, jeśli się mylę)

Nadszedł czas, aby wyrzucić wszystko, co wiesz o przekazaniu przez wartość / odniesienie.

Ponieważ w JavaScript nie ma znaczenia, czy jest przekazywana przez wartość, czy przez referencję, czy cokolwiek innego. Liczy się mutacja a przypisanie parametrów przekazywanych do funkcji.

OK, pozwól, że zrobię co w mojej mocy, aby wyjaśnić, co mam na myśli. Powiedzmy, że masz kilka obiektów.

var object1 = {};
var object2 = {};

To, co zrobiliśmy, to „przypisanie” ... Do zmiennych „object1” i „object2” przypisaliśmy 2 oddzielne puste obiekty.

Powiedzmy teraz, że bardziej lubimy obiekt1 ... Więc „przypisujemy” nową zmienną.

var favoriteObject = object1;

Następnie z jakiegoś powodu stwierdzamy, że obiekt 2 bardziej nam się podoba. Więc dokonujemy małej zmiany przydziału.

favoriteObject = object2;

Nic się nie stało z obiektem object1 ani object2. W ogóle nie zmieniliśmy żadnych danych. Wszystko, co zrobiliśmy, to ponowne przypisanie naszego ulubionego obiektu. Ważne jest, aby wiedzieć, że object2 i favouriteObject są przypisane do tego samego obiektu. Możemy zmienić ten obiekt za pomocą jednej z tych zmiennych.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

OK, spójrzmy teraz na prymitywy, takie jak na przykład łańcuchy

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Ponownie wybieramy ulubioną.

var favoriteString = string1;

Obie nasze zmienne favouriteString i string1 są przypisane do „Hello world”. A co jeśli chcemy zmienić nasz ulubiony ciąg ??? Co się stanie???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Ups ... Co się stało. Nie mogliśmy zmienić string1, zmieniając favouriteString ... Dlaczego ?? Ponieważ nie zmieniliśmy naszego obiektu string . Wszystko, co zrobiliśmy, to „RE ASSIGN”, zmienną favouriteString na nowy ciąg. To zasadniczo odłączyło go od string1. W poprzednim przykładzie, kiedy zmieniliśmy nazwę naszego obiektu, niczego nie przypisaliśmy. (No cóż, nie samej zmiennej , ... jednak przypisaliśmy właściwość name do nowego ciągu znaków.) Zamiast tego zmutowaliśmy obiekt, który utrzymuje połączenia między dwiema zmiennymi a obiektami bazowymi. (Nawet gdybyśmy chcieli zmodyfikować lub zmutować sam obiekt ciągu znaków , nie moglibyśmy tego zrobić, ponieważ łańcuchy znaków są w rzeczywistości niezmienne w JavaScript).

A teraz do funkcji i przekazywania parametrów ... Kiedy wywołujesz funkcję i przekazujesz parametr, zasadniczo robisz "przypisanie" do nowej zmiennej i działa to dokładnie tak samo, jak gdybyś przypisywał ją za pomocą znak równości (=).

Weź te przykłady.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

Teraz to samo, ale z funkcją

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

OK, teraz podajmy kilka przykładów użycia zamiast tego obiektów ... po pierwsze, bez funkcji.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Teraz to samo, ale z wywołaniem funkcji

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, jeśli przeczytałeś cały ten post, być może teraz lepiej rozumiesz, jak działają wywołania funkcji w JavaScript. Nie ma znaczenia, czy coś jest przekazywane przez odniesienie, czy według wartości ... Liczy się przypisanie czy mutacja.

Za każdym razem, gdy przekazujesz zmienną do funkcji, "przypisujesz" jakąkolwiek nazwę zmiennej parametru, tak jak gdybyś użył znaku równości (=).

Zawsze pamiętaj, że znak równości (=) oznacza przypisanie. Zawsze pamiętaj, że przekazanie parametru do funkcji w JavaScript oznacza również przypisanie. Są takie same, a 2 zmienne są połączone dokładnie w ten sam sposób (co oznacza, że ​​nie są, chyba że policzysz, że są przypisane do tego samego obiektu).

Jedyny przypadek, w którym „modyfikacja zmiennej” wpływa na inną zmienną, to sytuacja, w której obiekt bazowy jest zmutowany (w takim przypadku nie zmodyfikowano zmiennej, ale sam obiekt.

Nie ma sensu rozróżniać obiektów i prymitywów, ponieważ działa to dokładnie tak samo, jak gdybyś nie miał funkcji i po prostu użył znaku równości, aby przypisać nową zmienną.

Jedynym problemem jest sytuacja, gdy nazwa zmiennej, którą przekazujesz do funkcji, jest taka sama, jak nazwa parametru funkcji. Kiedy tak się dzieje, musisz traktować parametr wewnątrz funkcji tak, jakby była zupełnie nową zmienną prywatną dla funkcji (ponieważ tak jest)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'
75 geg Sep 26 2015 at 11:06

Rozważ następujące:

  1. Zmienne to wskaźniki wartości w pamięci.
  2. Ponowne przypisanie zmiennej po prostu wskazuje ten wskaźnik na nową wartość.
  3. Ponowne przypisanie zmiennej nigdy nie wpłynie na inne zmienne, które wskazywały na ten sam obiekt

Więc zapomnij o „przekazywaniu przez odniesienie / wartość” , nie rozłączaj się na „przekazywanie przez odniesienie / wartość”, ponieważ:

  1. Terminy są używane tylko do opisania zachowania języka, niekoniecznie do faktycznej podstawowej implementacji. W wyniku tej abstrakcji zostają utracone krytyczne szczegóły, które są niezbędne do rzetelnego wyjaśnienia, co nieuchronnie prowadzi do obecnej sytuacji, w której pojedynczy termin nie opisuje odpowiednio rzeczywistego zachowania i należy podać dodatkowe informacje
  2. Pojęcia te nie zostały pierwotnie zdefiniowane z zamiarem opisania w szczególności javascript, więc nie czuję się zmuszony do ich używania, gdy tylko zwiększają zamieszanie.

Odpowiadając na twoje pytanie: wskaźniki są przekazywane.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Kilka uwag końcowych:

  • Kuszące jest myślenie, że prymitywy są wymuszane przez specjalne reguły, podczas gdy obiekty nie są, ale prymitywy są po prostu końcem łańcucha wskaźników.
  • Jako ostatni przykład zastanów się, dlaczego zwykła próba wyczyszczenia tablicy nie działa zgodnie z oczekiwaniami.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
24 user779764 Jun 03 2011 at 06:04

Obiekt znajdujący się poza funkcją jest przekazywany do funkcji przez podanie odniesienia do obiektu zewnętrznego.

Kiedy używasz tego odniesienia do manipulowania jego obiektem, wpływa to na obiekt na zewnątrz. Jeśli jednak w funkcji zdecydowałeś się wskazać odniesienie do czegoś innego, w ogóle nie wpłynąłeś na obiekt na zewnątrz, ponieważ wszystko, co zrobiłeś, to przekierowanie odniesienia do czegoś innego.

22 PhilMander Feb 13 2015 at 18:00

Pomyśl o tym w ten sposób: zawsze przechodzi przez wartość. Jednak wartością obiektu nie jest sam obiekt, ale odniesienie do tego obiektu.

Oto przykład przekazywania liczby (typ pierwotny)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Powtórzenie tego z obiektem daje różne wyniki:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Jeszcze jeden przykład:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
20 igor May 13 2011 at 05:20

Bardzo szczegółowe wyjaśnienie dotyczące kopiowania, przekazywania i porównywania według wartości i przez odniesienie znajduje się w tym rozdziale książki „JavaScript: The Definitive Guide” .

Zanim opuścimy temat manipulowania obiektami i tablicami przez odniesienie, musimy wyjaśnić pewien punkt nazewnictwa.

Wyrażenie „przekazywanie przez odniesienie” może mieć kilka znaczeń. Dla niektórych czytelników fraza odnosi się do techniki wywoływania funkcji, która pozwala funkcji przypisywać nowe wartości do swoich argumentów i mieć te zmodyfikowane wartości widoczne poza funkcją. To nie jest sposób, w jaki termin ten jest używany w tej książce.

Tutaj mamy na myśli po prostu to, że odniesienie do obiektu lub tablicy - a nie sam obiekt - jest przekazywane do funkcji. Funkcja może używać odwołania do modyfikowania właściwości obiektu lub elementów tablicy. Ale jeśli funkcja nadpisze odniesienie odwołaniem do nowego obiektu lub tablicy, ta modyfikacja nie będzie widoczna poza funkcją.

Czytelnicy zaznajomieni z innym znaczeniem tego terminu mogą woleć powiedzieć, że obiekty i tablice są przekazywane przez wartość, ale wartość, która jest przekazywana, jest w rzeczywistości odniesieniem, a nie samym obiektem.

18 MichaelRoberts Feb 06 2009 at 04:49

JavaScript jest zawsze przekazywana przez wartość ; wszystko ma charakter wartościowy.

Obiekty są wartościami, a funkcje składowe obiektów same w sobie są wartościami (pamiętaj, że funkcje są obiektami pierwszej klasy w JavaScript). Również w odniesieniu do koncepcji, że wszystko w JavaScript jest obiektem ; To jest źle. Łańcuchy, symbole, liczby, wartości logiczne, wartości null i nieokreślone są prymitywami .

Czasami mogą wykorzystać niektóre funkcje i właściwości składowe odziedziczone z ich podstawowych prototypów, ale jest to tylko dla wygody. Nie oznacza to, że same są przedmiotami. Wypróbuj poniższe w celach informacyjnych:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

W obu alertach znajdziesz wartość, która ma być niezdefiniowana.

13 zangw Dec 30 2015 at 09:20

W JavaScript typ wartości decyduje wyłącznie o tym, czy wartość ta zostanie przypisana przez kopię wartości, czy kopię referencyjną .

Wartości pierwotne są zawsze przypisywane / przekazywane przez kopię wartości :

  • null
  • undefined
  • strunowy
  • numer
  • boolean
  • symbol w ES6

Wartości złożone są zawsze przypisywane / przekazywane przez kopię referencyjną

  • obiekty
  • tablice
  • funkcjonować

Na przykład

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

W powyższym fragmencie, ponieważ 2jest prymitywem skalarnym, aprzechowuje jedną początkową kopię tej wartości i bprzypisuje jej drugą kopię wartości. Zmieniając b, w żaden sposób nie zmieniasz wartości w a.

Ale oba ci dsą oddzielnymi odniesieniami do tej samej wspólnej wartości [1,2,3], która jest wartością złożoną. Należy zauważyć, że ani jeden, cani dwięcej „nie jest właścicielem” [1,2,3]wartości - oba są po prostu równymi odniesieniami równorzędnymi do wartości. Tak więc użycie dowolnego odwołania do modyfikacji ( .push(4)) samej rzeczywistej arraywartości współdzielonej wpływa tylko na jedną wspólną wartość, a oba odwołania będą odnosić się do nowo zmodyfikowanej wartości [1,2,3,4].

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

Kiedy wykonujemy przypisanie b = [4,5,6], nie robimy absolutnie nic, aby wpłynąć na ato, gdzie nadal występuje odwołanie ( [1,2,3]). Aby to zrobić, bmusiałby być raczej wskaźnikiem aniż odniesieniem do array- ale w JS nie ma takiej możliwości!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

Kiedy przekazujemy argument a, przypisuje on kopię aodwołania do x. xi asą oddzielnymi odnośnikami wskazującymi na tę samą [1,2,3]wartość. Teraz, wewnątrz funkcji, możemy użyć tego odwołania do zmiany samej wartości ( push(4)). Ale kiedy dokonujemy przypisania x = [4,5,6], w żaden sposób nie wpływa to na to, gdzie awskazuje początkowe odniesienie - nadal wskazuje na (teraz zmodyfikowaną) [1,2,3,4]wartość.

Aby skutecznie przekazać wartość złożoną (taką jak wartość array) przez kopię wartości, musisz ręcznie wykonać jej kopię, aby przekazane odniesienie nie wskazywało nadal na oryginał. Na przykład:

foo( a.slice() );

Wartość złożona (obiekt, tablica itp.), Która może być przekazana przez kopię referencyjną

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Tutaj objdziała jako opakowanie dla pierwotnej właściwości skalarnej a. Po przekazaniu do foo(..), kopia objodwołania jest przekazywana i ustawiana na wrapperparametr. Możemy teraz użyć wrapperodwołania, aby uzyskać dostęp do udostępnionego obiektu i zaktualizować jego właściwość. Po zakończeniu funkcji obj.azobaczy zaktualizowaną wartość 42.

Źródło

10 kianasirzadeh Nov 14 2019 at 07:48

cóż, chodzi o „wydajność” i „szybkość”, a po prostu „zarządzanie pamięcią” w języku programowania.

w javascript możemy umieścić wartości w dwóch warstwach: typ1 - objectsi typ2 - wszystkie inne typy wartości, takie jak string& boolean& itp.

jeśli wyobrazisz sobie pamięć jako poniższe kwadraty, w których w każdym z nich można zapisać tylko jedną wartość typu 2:

każda wartość type2 (zielona) jest pojedynczym kwadratem, podczas gdy wartość type1 (niebieska) to ich grupa :

Chodzi o to, że jeśli chcesz wskazać wartość-type2, adres jest zwykły, ale jeśli chcesz zrobić to samo dla wartości-type1, to wcale nie jest łatwe! :

i w bardziej skomplikowanej historii:

więc tutaj mogą nas uratować referencje :

podczas gdy zielona strzałka jest tutaj typową zmienną, fioletowa jest zmienną obiektową, więc ponieważ zielona strzałka (typowa zmienna) ma tylko jedno zadanie (i to wskazuje typową wartość), nie musimy oddzielać jej wartości od więc przesuwamy zieloną strzałkę z wartością tego, gdziekolwiek się pojawi i we wszystkich przypisaniach, funkcjach i tak dalej ...

ale nie możemy zrobić tego samego z fioletową strzałką, możemy chcieć przenieść tutaj komórkę 'john' lub wiele innych rzeczy ... więc fioletowa strzałka przylgnie do swojego miejsca i tylko typowe strzałki, które zostały do ​​niej przypisane, będą się poruszać ...

bardzo zagmatwana sytuacja polega na tym, że nie możesz zrozumieć, jak zmienia się zmienna, do której się odwołujesz, spójrzmy na bardzo dobry przykład:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

8 AshishSinghRawat Sep 26 2018 at 00:31

To trochę więcej wyjaśnienia przekazywania wartości i przekazywania przez referencję (JavaScript). W tej koncepcji mówią o przekazywaniu zmiennej przez odniesienie i przekazywaniu zmiennej przez odniesienie.

Przekaż według wartości (typ pierwotny)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • ma zastosowanie do wszystkich typów pierwotnych w JavaScript (ciąg, liczba, wartość logiczna, nieokreślona i null).
  • a ma przydzieloną pamięć (powiedzmy 0x001), a b tworzy kopię wartości w pamięci (powiedzmy 0x002).
  • Zatem zmiana wartości zmiennej nie wpływa na drugą, ponieważ obie znajdują się w dwóch różnych lokalizacjach.

Przekaż przez odniesienie (obiekty)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • Silnik JavaScript przypisuje obiekt do zmiennej ci wskazuje na jakąś pamięć, powiedzmy (0x012).
  • Gdy d = c, w tym kroku dwskazuje tę samą lokalizację (0x012).
  • Zmiana wartości dowolnej powoduje zmianę wartości obu zmiennych.
  • Funkcje są obiektami

Przypadek specjalny, przekazywanie przez odniesienie (obiekty)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Operator równości (=) ustawia nową przestrzeń pamięci lub adres
6 ZameerAnsari Aug 10 2017 at 19:43

dzielenie się tym, co wiem o referencjach w JavaScript

W JavaScript, podczas przypisywania obiektu do zmiennej, wartość przypisana do zmiennej jest odniesieniem do obiektu:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4

4 CPerkins Dec 24 2016 at 01:43

Semantyka!! Ustalenie konkretnych definicji z konieczności sprawi, że niektóre odpowiedzi i komentarze będą niekompatybilne, ponieważ nie opisują tego samego, nawet przy użyciu tych samych słów i wyrażeń, ale ważne jest, aby ominąć zamieszanie (szczególnie w przypadku nowych programistów).

Po pierwsze, istnieje wiele poziomów abstrakcji, których nie każdy zdaje się rozumieć. Nowi programiści, którzy uczyli się języków czwartej lub piątej generacji, mogą mieć trudności z ogarnięciem umysłu wokół pojęć znanych programistom asemblera lub programistów C, które nie są stopniowane przez wskaźniki do wskaźników do wskaźników. Przekazywanie przez odwołanie nie oznacza po prostu możliwości zmiany obiektu, do którego istnieje odwołanie, przy użyciu zmiennej parametru funkcji.

Zmienna : Połączona koncepcja symbolu, który odwołuje się do wartości w określonym miejscu w pamięci. Termin ten jest zwykle zbyt obciążony, aby można go było używać samodzielnie przy omawianiu szczegółów.

Symbol : Ciąg tekstowy używany do odniesienia się do zmiennej (np. Nazwa zmiennej).

Wartość : poszczególne bity przechowywane w pamięci i do których są odniesienia przy użyciu symbolu zmiennej.

Lokalizacja pamięci : miejsce przechowywania wartości zmiennej. (Sama lokalizacja jest reprezentowana przez liczbę oddzielną od wartości przechowywanej w lokalizacji).

Parametr funkcji : zmienna zadeklarowana w definicji funkcji, używana do odwoływania się do zmiennych przekazanych do funkcji.

Argument funkcji : zmienna poza funkcją, która jest przekazywana do funkcji przez wywołującego.

Zmienna obiektu : zmienna, której podstawową wartością bazową nie jest sam „obiekt”, a raczej jej wartość jest wskaźnikiem (wartością miejsca w pamięci) do innej lokalizacji w pamięci, w której przechowywane są rzeczywiste dane obiektu. W większości języków wyższej generacji aspekt „wskaźnika” jest skutecznie ukrywany przez automatyczne usuwanie odniesień w różnych kontekstach.

Zmienna pierwotna : zmienna, której wartość JEST wartością rzeczywistą. Nawet ta koncepcja może być skomplikowana przez auto-boxing i konteksty obiektowe w różnych językach, ale ogólna koncepcja jest taka, że ​​wartość zmiennej JEST rzeczywistą wartością reprezentowaną przez symbol zmiennej, a nie wskaźnik do innej lokalizacji w pamięci.

Argumenty i parametry funkcji to nie to samo. Ponadto wartość zmiennej nie jest obiektem zmiennej (jak już wskazywały różne osoby, ale najwyraźniej jest ignorowana). Te rozróżnienia mają kluczowe znaczenie dla właściwego zrozumienia.

Wartość przekazywana lub Call-by-sharing (dla obiektów) : wartość argumentu funkcji jest KOPIOWANA do innej lokalizacji w pamięci, do której odwołuje się symbol parametru funkcji (niezależnie od tego, czy znajduje się na stosie, czy na stercie). Innymi słowy, parametr funkcji otrzymał kopię przekazanej wartości argumentu ... ORAZ (krytyczna) wartość argumentu NIGDY NIE JEST AKTUALIZOWANA / ZMIENIONA / ZMIENIONA przez funkcję wywołującą. Pamiętaj, że wartość zmiennej obiektowej NIE jest samym obiektem, a raczej wskaźnikiem do obiektu, więc przekazanie zmiennej obiektowej według wartości powoduje skopiowanie wskaźnika do zmiennej parametru funkcji. Wartość parametru funkcji wskazuje dokładnie ten sam obiekt w pamięci. Same dane obiektu można zmienić bezpośrednio za pomocą parametru funkcji, ALE wartość argumentu funkcji NIGDY NIE JEST AKTUALIZOWANA, więc będzie wskazywać ten sam obiekt przez cały czas, a nawet po wywołaniu funkcji (nawet jeśli dane obiektu zostały zmienione lub jeśli parametrowi funkcji jest przypisany zupełnie inny obiekt). Nieprawidłowe jest stwierdzenie, że argument funkcji został przekazany przez odwołanie tylko dlatego, że obiekt, do którego się odwołuje, można aktualizować za pośrednictwem zmiennej parametru funkcji.

Wywołanie / przekazanie referencji : wartość argumentu funkcji może / zostanie zaktualizowana bezpośrednio przez odpowiedni parametr funkcji. Jeśli to pomaga, parametr funkcji staje się efektywnym „aliasem” dla argumentu - skutecznie odwołują się do tej samej wartości w tym samym miejscu pamięci. Jeśli argument funkcji jest zmienną obiektową, możliwość zmiany danych obiektu nie różni się od przypadku przekazania wartości, ponieważ parametr funkcji będzie nadal wskazywał na ten sam obiekt co argument. Ale w przypadku zmiennej obiektowej, jeśli parametr funkcji jest ustawiony na zupełnie inny obiekt, to argument będzie również wskazywał na inny obiekt - nie dzieje się tak w przypadku przekazywania przez wartość.

JavaScript nie jest przekazywany przez odwołanie. Jeśli uważnie się przeczytasz, zdasz sobie sprawę, że wszystkie przeciwne opinie nie rozumieją, co oznacza przekazywanie wartości i błędnie dochodzą do wniosku, że możliwość aktualizacji danych obiektu za pomocą parametru funkcji jest równoznaczna z „przekazaniem wartości”.

Klonowanie / kopiowanie obiektu: tworzony jest nowy obiekt, a dane oryginalnego obiektu są kopiowane. Może to być głęboka lub płytka kopia, ale chodzi o to, że tworzony jest nowy obiekt. Tworzenie kopii obiektu jest pojęciem odrębnym od przekazywania wartości. Niektóre języki rozróżniają obiekt klasy i struktury (lub tym podobne) i mogą mieć różne zachowanie przy przekazywaniu zmiennych różnych typów. Ale JavaScript nie robi niczego podobnego automatycznie podczas przekazywania zmiennych obiektowych. Ale brak automatycznego klonowania obiektów nie przekłada się na przekazywanie przez odniesienie.

4 georgeawg Jul 25 2018 at 22:47

JavaScript przekazuje typy pierwotne według wartości i typy obiektów przez odwołanie

Teraz ludzie lubią sprzeczać się bez końca o to, czy „przekazywanie przez odniesienie” jest właściwym sposobem opisania tego, co Java i in. faktycznie to robią. Chodzi o to:

  1. Przekazanie obiektu nie powoduje jego skopiowania.
  2. Obiekt przekazany do funkcji może mieć zmodyfikowane elementy składowe przez funkcję.
  3. Wartość pierwotna przekazana do funkcji nie może być modyfikowana przez funkcję. Kopia jest wykonana.

W mojej książce nazywa się to przekazywaniem przez odniesienie.

- Brian Bi - Które języki programowania są przekazywane przez odniesienie?


Aktualizacja

Oto odpowiedź na to pytanie:

W JavaScript nie ma opcji „przekazywanie przez odniesienie”.

3 dpp Sep 16 2014 at 16:19

Mój prosty sposób, aby to zrozumieć ...

  • Podczas wywoływania funkcji przekazujesz zawartość (referencję lub wartość) argumentów zmiennych, a nie same zmienne.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
    
  • Wewnątrz funkcji zmienne parametrów inVar1i inVar2odbierają przekazywaną zawartość.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
    
  • Od momentu inVar2otrzymania referencji { prop: 2 }możesz zmienić wartość właściwości obiektu.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
    
3 JohnSonderson Nov 01 2014 at 19:43

Przekazywanie argumentów do funkcji w JavaScript jest analogiczne do przekazywania parametrów przez wartość wskaźnika w C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}
3 DannyNiu Jul 28 2017 at 20:17

Dla prawników języków programowania przeszedłem przez następujące sekcje ECMAScript 5.1 (który jest łatwiejszy do odczytania niż ostatnie wydanie) i posunąłem się nawet do pytania o to na liście mailingowej ECMAScript.

TL; DR : Wszystko jest przekazywane przez wartość, ale właściwości Objects są referencjami, a definicja obiektu jest przerażająco brakująca w standardzie.

Konstrukcja list argumentów

Sekcja 11.2.4 „Listy argumentów” mówi, co następuje na temat tworzenia listy argumentów składającej się tylko z 1 argumentu:

Produkcja ArgumentList: AssignmentExpression jest oceniana w następujący sposób:

  1. Niech ref będzie wynikiem obliczania AssignmentExpression.
  2. Niech arg będzie GetValue (ref).
  3. Zwraca listę, której jedynym elementem jest arg.

Sekcja wylicza również przypadki, w których lista argumentów ma 0 lub> 1 argumentów.

Tak więc wszystko jest przekazywane przez odniesienie.

Dostęp do właściwości obiektu

Sekcja 11.2.1 „Dostęp do właściwości”

Produkcja MemberExpression: MemberExpression [Expression] jest oceniany w następujący sposób:

  1. Niech baseReference będzie wynikiem oceny MemberExpression.
  2. Niech baseValue będzie GetValue (baseReference).
  3. Niech propertyNameReference będzie wynikiem oceny wyrażenia.
  4. Niech propertyNameValue będzie GetValue (propertyNameReference).
  5. Wywołaj CheckObjectCoercible (baseValue).
  6. Niech propertyNameString będzie ToString (propertyNameValue).
  7. Jeśli oceniana produkcja składniowa jest zawarta w kodzie trybu ścisłego, niech ścisłe będzie prawdziwe, w przeciwnym razie ścisłe będzie fałszem.
  8. Zwraca wartość typu Reference, którego wartością bazową jest baseValue i którego nazwa, do której istnieje odwołanie, to propertyNameString, i którego flaga trybu ścisłego jest ścisła.

Dlatego właściwości obiektów są zawsze dostępne jako odniesienie.

Na odniesienie

Opisano w sekcji 8.7 „Typ specyfikacji referencyjnej”, że odwołania nie są typami rzeczywistymi w języku - są używane tylko do opisania zachowania operatorów delete, typeof i przypisania.

Definicja „obiektu”

W wydaniu 5.1 zdefiniowano, że „Obiekt jest zbiorem właściwości”. Dlatego możemy wywnioskować, że wartością obiektu jest kolekcja, ale to, jaka jest wartość kolekcji, jest słabo zdefiniowana w specyfikacji i wymaga trochę wysiłku, aby ją zrozumieć.

3 miguelr Apr 20 2019 at 13:06

Dokumentacja MDN wyjaśnia to jasno, nie będąc zbyt szczegółowym:

Parametry wywołania funkcji są argumentami funkcji . Argumenty są przekazywane do funkcji według wartości . Jeśli funkcja zmienia wartość argumentu, ta zmiana nie jest odzwierciedlana globalnie ani w funkcji wywołującej. Jednak odniesienia do obiektów również są wartościami i są wyjątkowe: jeśli funkcja zmienia właściwości obiektu, do którego odwołuje się, ta zmiana jest widoczna poza funkcją, (...)

Źródło: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

2 JonasWilms Jul 26 2020 at 22:32

obserwacja: jeśli obserwator nie ma możliwości zbadania pamięci bazowej silnika, nie ma sposobu, aby określić, czy niezmienna wartość zostanie skopiowana lub czy odniesienie zostanie przekazane.

JavaScript jest mniej lub bardziej niezależny od bazowego modelu pamięci. Nie ma czegoś takiego jak odniesienie ². JavaScript ma wartości . Dwie zmienne mogą mieć tę samą wartość (lub dokładniej: dwa rekordy środowiskowe mogą wiązać tę samą wartość). Jedynym typem wartości, które można modyfikować, są obiekty poprzez ich abstrakcyjne operacje [[Get]] i [[Set]]. Jeśli zapomnisz o komputerach i pamięci, to wszystko, czego potrzebujesz, aby opisać zachowanie skryptów JavaScripts, pozwala zrozumieć specyfikację.

 let a = { prop: 1 };
 let b = a; // a and b hold the same value
 a.prop = "test"; // the object gets mutated, can be observed through both a and b
 b = { prop: 2 }; // b holds now a different value
 

Teraz możesz zadać sobie pytanie, w jaki sposób dwie zmienne mogą mieć tę samą wartość na komputerze. Możesz wtedy zajrzeć do kodu źródłowego silnika JavaScript i najprawdopodobniej znajdziesz coś, co programista języka, w którym silnik został napisany, nazwałby referencją.

W rzeczywistości możesz powiedzieć, że JavaScript jest „przekazywana przez wartość”, podczas gdy wartość może być udostępniana, możesz powiedzieć, że JavaScript jest „przekazywana przez odniesienie”, co może być użyteczną logiczną abstrakcją dla programistów z języków niskiego poziomu. może nazwać to zachowanie „zadzwoń przez udostępnienie”. Ponieważ w JavaScript nie ma czegoś takiego jak odniesienie, wszystkie one nie są ani błędne, ani celowe. Dlatego nie sądzę, aby poszukiwanie odpowiedzi było szczególnie przydatne.

² Termin Odniesienie w specyfikacji nie jest odniesieniem w tradycyjnym znaczeniu. Jest to pojemnik na obiekt i nazwę właściwości oraz jest wartością pośrednią (np. Wartościuje a.bdo Reference { value = a, name = "b" }). Termin odniesienie pojawia się również czasami w specyfikacji w niepowiązanych sekcjach.

1 lid May 17 2014 at 08:01

Najbardziej zwięzłe wyjaśnienie, jakie znalazłem, znajduje się w przewodniku stylistycznym AirBNB :

  • Prymitywy : kiedy uzyskujesz dostęp do typu pierwotnego, pracujesz bezpośrednio nad jego wartością

    • strunowy
    • numer
    • boolean
    • zero
    • nieokreślony

Na przykład:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Złożony : kiedy uzyskujesz dostęp do typu złożonego, pracujesz nad odniesieniem do jego wartości

    • obiekt
    • szyk
    • funkcjonować

Na przykład:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Oznacza to, że w rzeczywistości typy pierwotne są przekazywane przez wartość, a typy złożone są przekazywane przez odwołanie.

1 steviejay Oct 29 2016 at 21:50

Czytałem te odpowiedzi wiele razy, ale NAPRAWDĘ ich nie zrozumiałem, dopóki nie dowiedziałem się o technicznej definicji „Zadzwoń przez udostępnienie”, jak określiła to Barbara Liskov

Semantyka wywołania przez współdzielenie różni się od wywołania przez odniesienie tym, że przypisania do argumentów funkcji w funkcji nie są widoczne dla wywołującego (w przeciwieństwie do semantyki referencji) [potrzebne cytowanie], więc np. Jeśli zmienna została przekazana, nie jest to możliwe aby zasymulować przypisanie tej zmiennej w zakresie wywołującego. Ponieważ jednak funkcja ma dostęp do tego samego obiektu co wywołujący (nie jest wykonywana kopia), mutacje tych obiektów, jeśli obiekty są zmienne, w ramach funkcji są widoczne dla wywołującego, co może wydawać się różnić od wywołania przez wartość semantyka. Mutacje zmiennego obiektu w funkcji są widoczne dla wywołującego, ponieważ obiekt nie jest kopiowany ani klonowany - jest udostępniany.

Oznacza to, że odniesienia do parametrów można zmieniać, jeśli przejdziesz i uzyskasz dostęp do samej wartości parametru. Z drugiej strony przypisanie do parametru zniknie po ocenie i jest niedostępne dla wywołującego funkcję.

1 Narayon Oct 19 2016 at 19:07

W języku niskiego poziomu, jeśli chcesz przekazać zmienną przez odniesienie, musisz użyć określonej składni podczas tworzenia funkcji:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

&ageJest odniesieniem do myAge, ale jeśli chcesz, wartość trzeba konwertować odniesienia, używając *age.

JavaScript to język wysokiego poziomu, który wykonuje tę konwersję za Ciebie.

Tak więc, chociaż obiekty są przekazywane przez odwołanie, język konwertuje parametr odwołania na wartość. Nie musisz używać &definicji funkcji, aby przekazać ją przez odwołanie, ani *w treści funkcji, aby przekonwertować odwołanie na wartość, JavaScript robi to za Ciebie.

Dlatego, gdy próbujesz zmienić obiekt wewnątrz funkcji, zastępując jego wartość (tj. age = {value:5}), Zmiana nie jest zachowywana, ale jeśli zmieniasz jej właściwości (tj. age.value = 5), Zachowuje się.

Ucz się więcej