Чи JavaScript - це мова проходження посилань або мова про значення?


1404

Примітивні типи (число, рядок тощо) передаються за значенням, але об'єкти невідомі, оскільки вони можуть бути як переданими за значенням (якщо ми вважаємо, що змінна, що містить об'єкт, насправді є посиланням на об'єкт ) і передається посилання (коли ми вважаємо, що змінна на об'єкт містить сам об'єкт).

Хоча це насправді не має значення в кінці, я хочу знати, що є правильним способом подання аргументів, що приймають конвенції. Чи є уривок із специфікації JavaScript, який визначає, якою має бути семантика щодо цього?


2
Я думаю, ви випадково перевернули свої визначення пройдених за значенням і переданих посилань ... "переданих за значенням (якщо ми вважаємо, що змінна, що містить об'єкт, насправді є посиланням на об'єкт) і передала -посилання (коли ми вважаємо, що змінна на об'єкт вміщує сам об’єкт) "
Ніко Белік

5
Так. Незалежно від синтаксису, в будь-якому виклику функції будь-якою мовою програмування передача посилання означає, що дані, пов'язані з переданою змінною, не копіюються при передачі функції, і таким чином будь-які модифікації, внесені функцією до переданої змінної, будуть збережені в програмі після припинення виклику функції. Передане значення означає, що дані, пов'язані зі змінною, фактично копіюються при передачі функції, і будь-які модифікації, внесені такою функцією в таку змінну, втрачаються, коли змінна вийде за межі тіла функції, коли функція повертається.
Джон Сондерсон

5
Це старе питання є дещо токсичним, оскільки його сильно обґрунтована відповідь є невірною. JavaScript суворо проходить через значення .
Пойні

6
@DanailNachev Термінологія, на жаль, заплутана. Річ у тому, що "пройти за значенням" та "пройти посилання" - це терміни, які передують безлічі сучасних функцій мови програмування. Слова "значення" та "посилання" стосуються конкретно параметра, як він відображається у виразі виклику функції. JavaScript завжди оцінює кожен вираз у списку параметрів виклику функції перед викликом функції, тому параметри завжди є значеннями. Конфуз полягає в тому, що посилання на об'єкти є загальними значеннями JavaScript. Однак це не робить його мовою "пройти посилання".
Pointy

2
@DanailNachev "пройти через посилання" конкретно означає, що якщо у вас є var x=3, y=x; f(x); alert(y === x);функція, то f()можна скласти звіт попередження, falseа не true. У JavaScript це неможливо, тому це не проходить посилання. Добре, що можна передавати посилання на об'єкти, що змінюються, але це не те, що означає "пройти через посилання". Як я вже сказав, прикро, що термінологія настільки заплутана.
Точковий

Відповіді:


1598

Це цікаво в JavaScript. Розглянемо цей приклад:

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);

Це дає результат:

10
changed
unchanged
  • Якщо б obj1взагалі не було посилання, зміна obj1.itemне впливала б на obj1зовнішню функцію.
  • Якби аргумент був належним посиланням, то все змінилося б. numбув би 100і obj2.itemчитав би "changed".

Натомість ситуація полягає в тому, що переданий елемент передається за значенням. Але предмет, переданий за значенням, сам по собі є еталонним. Технічно це називається закликом за спільним доступом .

На практиці це означає, що якщо ви зміните сам параметр (як numі з obj2), це не вплине на елемент, який вводився в параметр. Але якщо змінити INTERNALS параметра, він поширюватиметься резервне копіювання (як і у випадку obj1).


32
Це точно так само (або принаймні семантично), як і C #. Об'єкт має два типи: значення (примітивні типи) та еталон.
Пітер Лі

53
Я думаю, що це також використовується в Java: посилання на значення.
Jpnh

295
справжня причина полягає в тому, що всередині changeStuff, num, obj1 і obj2 є посиланнями. Коли ви змінюєте itemвластивість об'єкта, на який посилається obj1, ви змінюєте значення властивості елемента, яке спочатку було встановлено на "незмінне". Якщо ви присвоюєте obj2 значення {item: "change"}, ви змінюєте посилання на новий об'єкт (який негайно виходить із сфери застосування, коли функція закінчується). Зрозумілішим стає те, що відбувається, якщо ви назвали функцію params речей, таких як numf, obj1f і obj2f. Потім ви бачите, що парами ховали зовнішні назви вар.
jinglesthula

13
@BartoNaz Не дуже. Те, що ви хочете, - це передавати посилання за допомогою посилання, а не передавати посилання за значенням. Але JavaScript завжди передає посилання за значенням, як і все інше за значенням. (Для порівняння, C # має поведінку проходження посилань за значенням, схожу на JavaScript та Java, але дозволяє вказати пропускний посилання за посиланням за refключовим словом.) Зазвичай вам просто потрібно, щоб функція повертала новий об'єкт і робила призначення в точці, де ви викликаєте функцію. Наприклад, foo = GetNewFoo();замістьGetNewFoo(foo);
Тім Гудмен

55
Хоча ця відповідь є найпопулярнішою, вона може бути дещо заплутаною, оскільки в ній сказано "Якби це був чистий прохід за значенням". JavaScript - це чисте прохідне значення. Але значення, яке передається, є еталонним. Це зовсім не обмежується передачею параметрів. Ви можете просто скопіювати змінну на var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';та спостерігати такий же ефект, як у вашому прикладі. Тому я особисто посилаюсь на відповідь Тіма Гудмена
chiccodoro

476

Це завжди передається за значенням, але для об'єктів значення змінної є еталонним. Через це, коли ви передаєте об'єкт і змінюєте його членів , ці зміни зберігаються поза функцією. Це робить його виглядати як прохід по посиланню. Але якщо ви насправді зміните значення змінної об'єкта, ви побачите, що зміна не зберігається, доводячи, що вона дійсно передається за значенням.

Приклад:

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 */

Вихід:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar

14
@daylight: насправді ви помиляєтесь; якщо це було передано const ref, спроба зробити changeObject спричинить помилку, а не просто збій. Спробуйте призначити нове значення посилання const в C ++, і компілятор відхиляє його. З точки зору користувача, це різниця між прохідною вартістю та передачею через посилання const.
deworde

5
@daylight: Це не постійні посилання. У changeObject, я змінив, xщоб містити посилання на новий об'єкт. x = {member:"bar"};еквівалентно тому, x = new Object(); x.member = "bar"; що я кажу, до речі, також стосується і C #.
Тім Гудман

2
@daylight: для C # ви можете бачити це за межами функції, якщо ви використовуєте refключове слово, ви можете передавати посилання за посиланням (замість типового для передачі посилання за значенням), і тоді зміна для вказівки на a new Object() буде зберігатися .
Тім Гудман

11
@adityamenon Важко відповісти "чому", але зауважу, що дизайнери Java та C # зробили подібний вибір; це не просто дивацтво JavaScript. Дійсно, це дуже послідовно проходить за вартістю, те, що робить людей заплутаним, полягає в тому, що значення може бути еталонним. Це не набагато інакше, ніж передати вказівник навколо (за значенням) в C ++, а потім перенаправити його для встановлення членів. Ніхто не буде здивований, що ця зміна зберігається. Але оскільки ці мови абстрагують вказівник і мовчки роблять перенаправлення для вас, люди плутаються.
Тім Гудман

41
Іншими словами, тут непорозуміла річ - це не прохідна вартість / пропускна посилання. Все - прохідна цінність, повна зупинка. Заплутаною річчю є те, що ви не можете передавати об’єкт, а також не можете зберігати об'єкт у змінній. Кожен раз, коли ви думаєте, що це робите, ви насправді передаєте або зберігаєте посилання на цей об’єкт. Але коли ви переходите до його членів, відбувається безшумна перенаправлення, яка увічнює вигадку про те, що ваша змінна містила фактичний об'єкт.
Тім Гудман

150

Змінна не "утримує" об'єкт; він містить посилання. Ви можете призначити цю посилання іншій змінній, і тепер обидва посилаються на один і той же об'єкт. Це завжди передається за значенням (навіть коли це значення є посиланням ...).

Немає можливості змінити значення, що міститься змінною, переданою як параметр, що було б можливо, якщо підтримка JavaScript проходила за посиланням.


2
Це мене трохи заплутує. Не передає посилання прохідний посилання?

8
Автор означає, що передаючи посилання, ви передаєте опорне значення (інший спосіб думати про це - передача значення адреси пам'яті). Тож тому, якщо ви повторно декларуєте об'єкт, оригінал не змінюється, оскільки ви створюєте новий об’єкт в іншому місці пам'яті. Якщо ви змінюєте властивість, початковий об'єкт змінюється, оскільки ви змінили його в початковому місці пам'яті (це не було призначено повторно).
Хуй-Ань Хоанг

113

Мої два центи ... Це те, як я це розумію. (Не соромтеся виправити мене, якщо я помиляюся)

Настав час викинути все, що ви знаєте про прохідне значення / посилання.

Оскільки в JavaScript це не має значення, передається він за значенням, за посиланням чи іншим. Важливим є мутація проти присвоєння параметрів, переданих у функції.

Добре, дозвольте зробити все можливе, щоб пояснити, що я маю на увазі. Скажімо, у вас є кілька об’єктів.

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

Що ми зробили - це "присвоєння" ... Ми присвоїли 2 окремі порожні об'єкти змінним "object1" та "object2".

Тепер скажімо, що нам подобається object1 краще ... Отже, ми «присвоюємо» нову змінну.

var favoriteObject = object1;

Далі, з будь-якої причини, ми вирішуємо, що нам подобається об’єкт 2 краще. Отже, ми просто робимо невелике перепризначення.

favoriteObject = object2;

Нічого не сталося з об’єктом1 або з об’єктом2. Ми взагалі не змінили жодних даних. Все, що ми зробили, було перепризначити те, що наш улюблений об’єкт. Важливо знати, що object2 та favoriteObject обидва присвоєні одному об'єкту. Ми можемо змінити цей об’єкт за допомогою будь-якої з цих змінних.

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

Гаразд, тепер давайте подивимося на примітиви, як, наприклад, рядки

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

Знову ми вибираємо фаворита.

var favoriteString = string1;

І наші змінні favoriteString, і string1 призначені для "Hello world". А що робити, якщо ми хочемо змінити свою улюбленуString ??? Що станеться???

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

Ух о .... Що сталося. Ми не могли змінити string1, змінивши favoriteString ... Чому ?? Тому що ми не змінили наш рядковий об'єкт . Все, що ми зробили, було "RE ASSIGN" змінною FavorString в нову рядок. Це по суті від'єднало його від string1. У попередньому прикладі, коли ми перейменували наш об’єкт, ми нічого не призначили. (Ну, а не самої змінної , ... ми, однак, призначили властивість імені новому рядку.) Натомість ми просто мутували об'єкт, який зберігає зв'язки між двома змінними та основними об'єктами. (Навіть якщо б ми хотіли змінити або мутувати рядок об'єкт сам, ми не могли цього зробити, оскільки рядки насправді незмінні в JavaScript.)

Тепер, до функцій та передачі параметрів .... Коли ви викликаєте функцію та передаєте параметр, то, що ви по суті робите, це "присвоєння" новій змінній, і вона працює точно так само, як якщо б ви просто призначили за допомогою знак рівності (=)

Візьміть ці приклади.

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'

Тепер те саме, але з функцією

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'

Добре, тепер давайте кілька прикладів, використовуючи замість них об'єкти ... по-перше, без функції.

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';

Тепер те саме, але з викликом функції

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

Гаразд, якщо ви читаєте весь цей пост, можливо, тепер ви краще розумієте, як функціонують дзвінки в JavaScript. Не має значення, передається щось за посиланням чи за значенням ... Важливим є присвоєння проти мутації.

Кожен раз, коли ви передаєте змінну функції, ви "присвоюєте" будь-яку назву змінної параметра, як і якщо ви використовували знак рівності (=).

Завжди пам’ятайте, що знак рівності (=) означає присвоєння. Завжди пам’ятайте, що передача параметра до функції в JavaScript також означає призначення. Вони однакові, і дві змінні з'єднані абсолютно однаково (що означає, що вони не є, якщо не порахувати, що вони призначені одному і тому ж об'єкту).

Єдиний раз, коли "модифікація змінної" впливає на іншу змінну - це коли мутація основного об'єкта (у цьому випадку ви змінили не змінну, а сам об'єкт.

Немає сенсу проводити розмежування між об'єктами та примітивами, оскільки це працює так само, як якщо б у вас не було функції, а просто використовується знак рівності для присвоєння новій змінній.

Єдиний gotcha - це те, коли ім'я змінної, яку ви передаєте у функцію, є такою ж, як і ім'я параметра функції. Коли це відбувається, ви повинні ставитися до параметра всередині функції так, ніби це абсолютно нова змінна, приватна для функції (тому що вона є)

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'

2
Для будь-яких програмістів на C подумайте про char *. foo(char *a){a="hello";} нічого не робить, але якщо ви foo(char *a){a[0]='h';a[1]='i';a[2]=0;}це зробите, це змінюється зовні, оскільки aце місце в пам'яті, передане за значенням, що посилається на рядок (char масив). Передавання структур (подібних до js-об'єктів) за значенням у C дозволено, але не рекомендується. JavaScript просто застосовує ці найкращі практики та приховує непотрібну і, як правило, небажану суть ... і це впевнено полегшує читання.
технозавр

2
Це правильно - терміни " перехід за значенням" та "посилання на посилання" мають значення в дизайні мови програмування, і ці значення взагалі не мають нічого спільного з мутацією об'єкта. Це все про те, як працюють параметри функції.
Пойні

2
Тепер, коли я розумію, що obj1 = obj2 означає, що i obj1 і obj2 зараз вказують на одне і те ж опорне місце, і якщо я модифікую внутрішні місця obj2, посилання obj1 виявить ті самі внутрішні. Як скопіювати об'єкт таким чином, що коли я його виконую source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2", все ще залишається {"id": "1"}?
Махтін

1
Я розмістив ще одну відповідь з традиційними визначеннями, щоб сподіватися зменшити плутанину. Традиційні визначення поняття "пропускна вартість" та "пропускний посилання" були визначені ще в день покажчиків пам'яті перед автоматичною перенаправленням. Було чудово зрозуміло, що значенням змінної об'єкта є фактично розташування вказівника пам'яті, а не об'єкт. Хоча ваша дискусія щодо призначення та мутації, мабуть, корисна, не варто викидати традиційні терміни та їх визначення. Мутація, присвоєння, пропускна вартість, передача посилань тощо не повинні суперечити один одному.
C Перкінс

чи "Число" теж є "незмінним"?
ебрам халіл

72

Розглянемо наступне:

  1. Змінні - покажчики на значення в пам'яті.
  2. Перепризначення змінної просто вказує на вказівник на нове значення.
  3. Переназначення змінної ніколи не вплине на інші змінні, які вказували на той самий об’єкт

Отже, забудьте про "пройти через посилання / значення" , не зациклюйтесь на "пройти за посиланням / значенням", оскільки:

  1. Терміни використовуються лише для опису поведінки мови, не обов'язково фактичної основної реалізації. В результаті цього абстрагування втрачаються критичні деталі, які мають важливе значення для гідного пояснення, що неминуче призводить до поточної ситуації, коли один термін не належним чином описує фактичну поведінку, а додаткова інформація повинна бути надана.
  2. Ці поняття спочатку не були визначені з метою опису JavaScript, зокрема, тому я не відчуваю вимушеності використовувати їх, коли вони лише додають плутанини.

Щоб відповісти на ваше запитання: покажчики передаються.


// 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'

Кінцеві коментарі:

  • Привабливо думати, що примітиви виконуються за спеціальними правилами, поки об'єктів немає, а примітиви - це просто кінець ланцюжка покажчиків.
  • В якості останнього прикладу розглянемо, чому загальна спроба очистити масив не працює так, як очікувалося.


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

Подальші питання для додаткового кредитування;) Як працює сміття? Якщо я перемикаю змінну через мільйон {'George', 1}значень, але використовую лише одне з них одночасно, то як керуються іншими? А що відбувається, коли я призначу змінну значенню іншої змінної? Я вказую тоді на вказівник чи вказую на покажчик правого операнда? Чи var myExistingVar = {"blah", 42}; var obj = myExistingVar;призводить до objвказівки на {"blah", 42}, чи на myExistingVar?
Майкл Гофман

@MichaelHoffmann Ці заслуговують власних запитань щодо ПЕ, і, мабуть, вже відповіли краще, ніж я можу впоратися. При цьому 1)я запустив профіль пам’яті в інструментах розробки браузера для функції циклу, наприклад, описаного вами, і побачив сплески у використанні пам’яті протягом певного циклу. Це, мабуть, вказує на те, що справді створюються нові однакові об'єкти при кожній ітерації циклу. Коли шипи раптово падають, сміттєзбірник просто прибирав групу цих невикористаних предметів.
geg

1
@MichaelHoffmann 2)Що стосується чогось подібного var a = b, javascript не забезпечує механізм використання покажчиків, і тому змінна ніколи не може вказувати на вказівник (як можна на C), хоча базовий механізм JavaScript, безсумнівно, використовує їх. Отже ... var a = bвкаже a"на пуантета правого операнда"
geg

Я задав тут питання №1 (зокрема про Chrome, оскільки реалізація, ймовірно, відрізняється в кожному браузері) stackoverflow.com/q/42778439/539997, і я все ще намагаюся думати, як сформулювати питання №2. Будь-яка допомога вдячна.
Майкл Гофман

1
Не потрібно забувати про "пройти через посилання / значення" ! Ці терміни мають історичні значення, які точно описують те, що ви намагаєтесь описати. Якщо ми викинемо історичні терміни та визначення та станемо лінивим, щоб дізнатися, що вони спочатку означали, то ми втрачаємо здатність ефективно спілкуватися між поколіннями. Не було б гарного способу обговорення відмінностей між різними мовами та системами. Натомість новим програмістам потрібно вивчити та зрозуміти традиційні терміни, чому вони і звідки вони походили. В іншому випадку ми колективно втрачаємо знання та розуміння.
C Перкінс

24

Об'єкт поза функцією передається у функцію шляхом подання посилання на зовнішній об'єкт.

Якщо ви використовуєте це посилання для маніпулювання його об'єктом, це впливає на зовнішній об'єкт. Однак якщо всередині функції ви вирішили вказати посилання на щось інше, ви взагалі не вплинули на об'єкт, оскільки все, що ви зробили, було перенаправити посилання на щось інше.


20

Подумайте про це так: Це завжди передається цінністю. Однак значення об’єкта - це не сам об’єкт, а посилання на цей об’єкт.

Ось приклад, передаючи число (примітивний тип)

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

Повторення цього об'єкта дає різні результати:

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 }

Ще один приклад:

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}

19

Дуже детальне пояснення щодо копіювання, передачі та порівняння за значенням та посиланням знаходиться в цій главі книги "JavaScript: Постійний посібник" .

Перш ніж ми покинемо тему маніпулювання об'єктами та масивами за посиланням, нам потрібно розібратися у номенклатурі.

Фраза "пройти посилання" може мати кілька значень. Деяким читачам словосполучення стосується техніки виклику функції, яка дозволяє функції присвоювати нові значення своїм аргументам і мати видозмінені значення видимими поза функцією. Це не спосіб використання цього терміна в цій книзі.

Тут ми маємо на увазі просто те, що посилання на об'єкт або масив - а не на сам об’єкт - передається функції. Функція може використовувати посилання для зміни властивостей об'єкта або елементів масиву. Але якщо функція перезаписує посилання з посиланням на новий об'єкт або масив, ця модифікація не видно за межами функції.

Читачі, знайомі з іншим значенням цього терміна, можуть вважати за краще сказати, що об’єкти та масиви передаються за значенням, але значення, яке передається, насправді є посиланням, а не самим об’єктом.


Ого, це неймовірно заплутано. Хто в їх розумній думці визначив би усталений термін, щоб означати саме протилежне, а потім використовувати його таким чином? Недарма стільки відповідей на це питання настільки заплутано.
Jörg W Mittag

16

JavaScript - це завжди прохідне значення ; все має ціннісний тип.

Об'єкти - це значення, а функції членів об'єктів - самі значення (пам’ятайте, що функції - це об'єкти першокласного в JavaScript). Також щодо поняття, що все в JavaScript є об'єктом ; це неправильно. Рядки, символи, цифри, булеві, нульові та невизначені - примітиви .

Іноді вони можуть використовувати деякі функції та властивості, успадковані від базових прототипів, але це лише для зручності. Це не означає, що вони самі є об’єктами. Спробуйте наступне для довідки:

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

В обох попередженнях ви знайдете значення, яке не визначається.


12
-1, це не завжди передається за значенням. Від MDC: "Якщо ви передаєте об'єкт (тобто непримітивне значення, наприклад, масив або визначений користувачем об'єкт) як параметр, посилання на об'єкт передається функції."
Нік

37
@ Nick: Це завжди передається за значенням. Період. Посилання на об'єкт передається за значенням функції. Це не проходить через посилання. "Перейти через посилання" майже можна вважати передачею самої змінної, а не її значенням; будь-які зміни, які функція вносить в аргумент (включаючи заміну його повністю іншим об'єктом!), відображатимуться в абонента. Цей останній біт неможливий у JS, оскільки JS не передає посилання - він передає посилання за значенням. Відмінність тонка, але досить важлива для розуміння її обмежень.
cHao

1
Для майбутніх укладачів ... Про ваше посилання: x = "teste"; x.foo = 12;і т. Д. Тільки тому, що властивість не є стійкою, це не означає, що це не об'єкт. Як каже MDN: У JavaScript майже все є об’єктом. Усі примітивні типи, крім нульових та невизначених, трактуються як об'єкти. Їм можуть бути призначені властивості (присвоєні властивості деяких типів не є стійкими), і вони мають усі характеристики об'єктів. посилання
slacktracer

9
MDN - це редакція, яку редагує користувач, і вона там неправильна. Нормативна довідка - ECMA-262. Див. S. 8 "Тип посилальної специфікації", де пояснюється спосіб вирішення посилань, а також 8.12.5 "[[Put]]", який використовується для пояснення AssignmentExpression посиланням та для примусу об'єкта 9.9 ToObject. Щодо примітивних значень, Майкл вже пояснив, що робить ToObject, як у специфікації. Але див. Також s. 4.3.2 примітивне значення.
Гаррет

1
@WonderLand: Ні, його немає. Люди, які ніколи не змогли пройти посилання, ніколи не можуть зрозуміти відмінності між проходженням посилання та передачею посилання за значенням. Але вони там є, і вони мають значення. Мені байдуже дезінформувати людей, тому що це звучить легше.
cHao

12

У JavaScript тип значення визначає виключно те, чи буде це значення присвоєно копією значення або копією посилання .

Примітивні значення завжди присвоюються / передаються копією значення :

  • null
  • undefined
  • рядок
  • число
  • булева
  • символ в ES6

Значення складових завжди призначаються / передаються за допомогою копії довідки

  • об’єкти
  • масиви
  • функція

Наприклад

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]

У наведеному фрагменті, оскільки 2є скалярним примітивом, aзберігається одна початкова копія цього значення та bприсвоюється інша копія значення. Змінюючись b, ви жодним чином не змінюєте значення в a.

Але і те, cі інше dє окремими посиланнями на одне і те саме спільне значення [1,2,3], яке є складною цінністю. Важливо зауважити, що cні dбільше, ні більше "не володіють" [1,2,3]значенням - обидва є просто рівними одноранговими посиланнями на значення. Отже, використовуючи будь-яку посилання для зміни ( .push(4)) власне спільного arrayзначення, це впливає лише на одне спільне значення, і обидві посилання будуть посилатися на нещодавно змінене значення [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]

Коли ми робимо завдання b = [4,5,6], ми не робимо абсолютно нічого, щоб вплинути на те, де aвсе ще посилаються ( [1,2,3]). Для цього bпотрібно було б бути вказівником, aа не посиланням на array- але такої можливості в JS не існує!

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]

Коли ми передаємо аргумент a, він призначає копію aпосилання на x. xі aє окремими посиланнями, що вказують на одне [1,2,3]значення. Тепер усередині функції ми можемо використовувати це посилання для мутації самого значення ( push(4)). Але коли ми робимо завдання x = [4,5,6], це жодним чином не впливає на те, де aвказується початкова посилання - усе ще вказує на (тепер модифіковане) [1,2,3,4]значення.

Щоб ефективно передавати значення сполуки (як-от array) за допомогою копіювання значення, потрібно вручну зробити його копію, щоб передана посилання все ще не вказувала на оригінал. Наприклад:

foo( a.slice() );

Складене значення (об'єкт, масив тощо), яке може передаватися копією довідки

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

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Тут objдіє як обгортка для скалярної примітивної властивості a. Після передачі до нього передається foo(..)копія objпосилання та встановлюється wrapperпараметр. Тепер ми можемо використовувати wrapperпосилання для доступу до спільного об’єкта та оновлення його властивості. Після завершення функції obj.aпобачить оновлене значення 42.

Джерело


Спочатку ви заявляєте "Складені значення завжди призначаються / передаються посиланнями копії", а потім ви "призначаєте копію посилання на x". У випадку, що ви називаєте "складеним значенням", фактичне значення змінної IS є посиланням (тобто вказівкою пам'яті). Як ви пояснили, посилання копіюється ... так що значення змінних копіюється , знову підкреслюючи, що ПОСИЛАННЯ - ЦІННЕ. Це означає, що JavaScript є прохідним значенням для всіх типів. Pass-by-value означає передачу копії значення змінних. Не має значення, що значення є посиланням на об'єкт / масив.
C Перкінс

Ви впроваджуєте нову термінологію (value-copy / reference-copy), і це просто ускладнює справи. Є просто копії, період. Якщо ви передаєте примітив, ви передали копію фактичних примітивних даних, якщо ви передаєте об'єкт, ви передали копію пам'яті об'єкта. Це все, що вам потрібно сказати. Все більше просто ще більше бентежить людей.
Скотт Маркус

9

ну, мова йде про "продуктивність" і "швидкість" і просте слово "управління пам'яттю" мовою програмування.

у javascript ми можемо помістити значення у два шари: type1 - objectsта type2 - всі інші типи значень, такі як string& boolean& тощо

якщо ви уявляєте пам'ять нижче квадратів, у кожному з яких можна зберегти лише одне значення type2:

введіть тут опис зображення

кожне значення type2 (зелене) - це один квадрат, тоді як значення type1 (синє) - це їх група :

введіть тут опис зображення

справа в тому, що якщо ви хочете вказати значення type2, адреса проста, але якщо ви хочете зробити те ж саме для type1-значення, що зовсім не просто! :

введіть тут опис зображення

і в більш складній історії:

введіть тут опис зображення

тому тут посилання можуть нас врятувати: введіть тут опис зображення

в той час як зелена стрілка тут є типовою змінною, фіолетова - це об'єктивна змінна, тому що зелена стрілка (типова змінна) має лише одне завдання (і це вказує на типове значення), нам не потрібно відокремлювати це значення від це так, ми переміщуємо зелену стрілку зі значенням того, куди б вона не пішла, і у всіх завданнях, функціях і так далі ...

але ми не можемо робити те ж саме з фіолетовою стрілкою, ми можемо захотіти перенести клітинку «Джон» сюди або багато інших речей ..., тому фіолетова стрілка буде триматися на своєму місці і просто типові стрілки, які були призначені їй, будуть рухатися ...

дуже заплутаною є ситуація, коли ви не можете усвідомити, як змінюється ваша посилальна змінна, давайте розглянемо дуже вдалий приклад:

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

Це трохи більше пояснень для передачі за значенням та передачі посилання (JavaScript). У цій концепції вони говорять про передачу змінної за посиланням та передачу змінної за посиланням.

Передати за значенням (примітивний тип)

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
  • застосовується до всіх примітивних типів у JavaScript (рядок, число, булевий, невизначений та нульовий).
  • a виділяється пам'ять (скажімо, 0x001) і b створює копію значення в пам'яті (скажімо, 0x002).
  • Отже зміна значення змінної не впливає на іншу, оскільки вони обидва проживають у двох різних місцях.

Перейти за посиланням (об'єкти)

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" }
  • Двигун JavaScript присвоює об'єкт змінній c, і він вказує на деяку пам'ять, скажімо, (0x012).
  • Коли d = c, на цьому кроці dвказується на те саме місце (0x012).
  • Зміна значення будь-яких змін для обох змінних.
  • Функції - це об’єкти

Спеціальний випадок, передайте посилання (об'єкти)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Оператор рівних (=) встановлює новий простір пам'яті або адресу

У вашому так званому спеціальному випадку не розподіл простору пам’яті викликає не оператор присвоєння, а сам об’єкт буквальний . Позначення дужки керлі викликає створення нового об’єкта. Властивість cвстановлюється копією посилання нового об’єкта.
georgeawg

6

обмін тим, що я знаю про посилання в JavaScript

У JavaScript, при призначенні об'єкта змінній, значення, присвоєне змінній, є посиланням на об'єкт:

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


1
Це надто спрощена відповідь, яка нічого не говорить про те, що попередні відповіді не пояснили краще. Мене бентежить питання, чому ви називаєте масиви як особливий випадок.
Квентін

1
" об'єкти зберігаються як посилання " вводить в оману. Я вважаю, що ви маєте на увазі те, що при призначенні об'єкта змінній, значення, присвоєне змінній, є посиланням на об'єкт.
RobG

це не вирішує проблему оновлення об’єкта всередині функції, яка не оновлює об'єкт поза функцією. Це вся картина, де вона, здається, працює як значення, а не посилання. Звідси -1
amaster

@amaster Дякую, що вказали на це! Чи можете ви запропонувати змінити, будь ласка?
xameeramir

Ха-ха, я спробував ... моя запропонована редакція змінилася занадто багато. AMD не було дозволено
amaster

4

Семантика !! Встановлення конкретних визначень обов'язково зробить деякі відповіді та коментарі несумісними, оскільки вони не описують одне й те саме, навіть коли використовують одні й ті ж слова та фрази, але дуже важливо, щоб не було плутанини (особливо для нових програмістів).

Перш за все, існує кілька рівнів абстракції, які, здається, не всі розуміють. Нові програмісти, які вивчають мови 4-го чи 5-го поколінь, можуть зіткнутися з концепціями, знайомими для складання або програмістами C, які не поетапно вказують на покажчики на покажчики. Пропускний посилання не означає просто можливість зміни об'єкта, що посилається, за допомогою змінної параметру функції.

Змінна : комбіноване поняття символу, яке посилається на значення в певному місці в пам'яті. Цей термін, як правило, занадто завантажений, щоб використовувати його окремо для обговорення деталей.

Символ : Текстовий рядок, який використовується для позначення змінної (тобто імені змінної).

Значення : Конкретні біти, що зберігаються в пам'яті та посилаються на символ перемінної.

Місце пам'яті : де зберігається значення змінної. (Саме місце розташування представлене числом, окремим від значення, збереженого в цьому місці.)

Параметр функції : Змінна, оголошена у визначенні функції, використовується для посилання змінних, переданих функції.

Аргумент функції : Змінна поза функцією, яку передає функція абонентом.

Змінна об'єкта : змінна, основним базовим значенням якої є не сам "об'єкт", а його значення - це вказівник (значення місця в пам'яті) на інше місце в пам'яті, де зберігаються фактичні дані об'єкта. У більшості мов вищого покоління аспект "вказівника" ефективно прихований автоматичним відстеженням у різних контекстах.

Примітивна змінна : змінна, значення якої є фактичним значенням. Навіть ця концепція може бути ускладнена автоматичним боксуванням та об'єктоподібними контекстами різних мов, але загальна ідея полягає в тому, що значення змінної IS є фактичним значенням, представленим символом змінної, а не вказівником на інше місце пам'яті.

Аргументи та параметри функції - це не одне і те ж. Також значенням змінної не є об'єкт змінної (як уже вказували різні люди, але, мабуть, ігнорується). Ці відмінності мають вирішальне значення для правильного розуміння.

Захоплююча цінність або Обмін за допомогою дзвінка (для об'єктів): Значення аргументу функції COPIED для іншого місця пам'яті, на яке посилається символ параметра параметра (незалежно від того, знаходиться він у стеці чи купі). Іншими словами, параметр функції отримав копію значення переданого аргументу ... І (критичне) значення аргументу НІКОЛИ НЕ ОНОВЛЕНО / ЗМІНЕНО / ЗМІнено функцією виклику. Пам'ятайте, що значення змінної об'єкта НЕ є самим об'єктом, скоріше це вказівник на об'єкт, тому передача змінної об'єкта за значенням копіює вказівник на змінну параметру функції. Значення параметра параметра вказує на той самий об’єкт у пам'яті. Самі дані об'єкта можуть бути змінені безпосередньо через параметр функції, АЛЕ значення аргументу функції НІКОЛИ НЕ ОНОВЛЕНО, тому воно продовжуватиме вказувати на самеоб'єкта протягом і навіть після виклику функції (навіть якщо дані його об'єкта були змінені або якщо параметру функції присвоєно зовсім інший об'єкт). Неправильно робити висновок, що аргумент функції передається посиланням лише тому, що посилається об'єкт можна оновлювати через змінну параметра параметра.

Дзвінок / пропуск посилання : значення аргументу функції можна / буде оновлено безпосередньо відповідним параметром функції. Якщо це допомагає, параметр функції стає ефективним "псевдонімом" для аргументу - вони фактично посилаються на те саме значення в одному і тому ж місці пам'яті. Якщо аргумент функції є змінною об'єкта, можливість зміни даних об'єкта не відрізняється від випадку передачі значення, оскільки параметр функції все одно вказуватиме на той самий об'єкт, що і аргумент. Але у випадку об'єктної змінної, якщо параметр функції встановлений на зовсім інший об'єкт, аргумент також вказуватиме на інший об'єкт - цього не відбувається у випадку передачі значення.

JavaScript не проходить через посилання. Якщо ви уважно прочитаєте, то зрозумієте, що всі протилежні думки неправильно розуміють, що мається на увазі під прохідною вартістю, і вони помилково роблять висновок про те, що можливість оновлення даних об’єкта за допомогою параметра параметра є синонімом «пропускної вартості».

Клон / копія об’єкта : створюється новий об’єкт і копіюються вихідні дані об’єкта. Це може бути глибока копія або неглибока копія, але справа в тому, що створюється новий об’єкт. Створення копії об'єкта - це окреме поняття від прохідного значення. Деякі мови розрізняють об'єкт та структури класу (або подібні) і можуть мати різну поведінку для передачі змінних різних типів. Але JavaScript нічого подібного не робить автоматично при передачі об'єктних змінних. Але відсутність автоматичного клонування об’єктів не перекладається на пропускну інформацію.


4

JavaScript передає примітивні типи за значенням та типами об'єктів за посиланням

Тепер люди люблять нескінченно замислюватися над тим, "правильний посилання" є правильним способом описати те, що Java та ін. насправді так. Сенс у цьому:

  1. Передача об'єкта не копіює об'єкт.
  2. Об'єкт, переданий функції, може мати його членів, змінених функцією.
  3. Примітивне значення, передане функції, не може бути змінено функцією. Робиться копія.

У моїй книзі, що називається проходження посилання.

- Брайан Бі - Які мови програмування передаються за посиланням?


Оновлення

Ось спростування цього:

У JavaScript немає "пройти посилання".


@Amy Оскільки це опис пропуску за значенням, а не проходження посиланням. Ця відповідь хороша, що показує різницю: stackoverflow.com/a/3638034/3307720
nasch

@nasch Я розумію різницю. №1 та №2 описують семантику проходження реф. №3 описує семантику прохідних значень.
Емі

@Amy 1, 2 і 3 всі відповідають значенню пропуску. Для проходження посилання вам також знадобиться 4: присвоєння посилання новому значенню всередині функції (з оператором =) також переназначає посилання поза функцією. Це не так у Javascript, що робить його виключно переданим за значенням. Під час передачі об'єкта ви передаєте вказівник на об'єкт і передаєте цей покажчик за значенням.
нащ

Це взагалі не те, що розуміється під "проходженням посилання". Ви задовольнили мій запит, і я не згоден з вами. Дякую.
Емі

"У моїй книзі, яка називається проходження посилання". - У кожній окремій книзі-компіляторі, книзі перекладача, книзі теорії мови програмування та книзі з інформатики, що колись написана, це не так.
Jörg W Mittag

3

Мій простий спосіб зрозуміти це ...

  • Під час виклику функції ви передаєте вміст (посилання чи значення) змінних аргументів, а не самих змінних.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Всередині функції, змінні параметрів inVar1і inVar2, отримують переданий вміст.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Так inVar2отримали звернення { prop: 2 }, ви можете змінити значення властивості об'єкта.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }

3

Передача аргументів функції в JavaScript є аналогом передачі параметрів за значенням вказівника в 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;
}

1
Я не думаю, що це так у JavaScript: `` `javascript var num = 5;
Данаїл Начев

@DanailNachev: Хоча це може бути технічно правдивим, різниця помітна лише для об'єктів, що змінюються, яких примітиви ECMAScript не є.
Jörg W Mittag

3

Для юристів з мов програмування я ознайомився з наступними розділами ECMAScript 5.1 (який легше читати, ніж останнє видання), і перейшов до запитання у списку розсилки ECMAScript.

TL; DR : Усі передаються за значенням, але властивості Об'єктів є посиланнями, а визначення об'єкта в стандарті не вистачає.

Побудова списків аргументів

Розділ 11.2.4 "Списки аргументів" говорить про створення списку аргументів, що складається лише з 1 аргументу:

Виробничий аргументList: AssignmentExpression оцінюється наступним чином:

  1. Нехай ref є результатом оцінки AssignmentExpression.
  2. Нехай аргумент буде GetValue (ref).
  3. Поверніть список, єдиним елементом якого є аргумент.

У цьому розділі також перераховані випадки, коли список аргументів містить 0 або> 1 аргумент.

Таким чином, все передається шляхом посилання.

Доступ до властивостей об'єкта

Розділ 11.2.1 "Приватні об'єкти"

Виробничий MemberExpression: MemberExpression [Вираз] оцінюється наступним чином:

  1. Нехай baseReference є результатом оцінки MemberExpression.
  2. Нехай baseValue буде GetValue (baseReference).
  3. Нехай властивістьNameReference є результатом оцінки виразів.
  4. Нехай propertyNameValue буде GetValue (propertyNameReference).
  5. Виклик CheckObjectCoercible (baseValue).
  6. Нехай propertyNameString буде ToString (propertyNameValue).
  7. Якщо синтаксичне виробництво, яке оцінюється, міститься в строгому режимі коду, нехай строгий буде істинним, інакше нехай строгий буде хибним.
  8. Поверніть значення типу Reference , базовим значенням якого є baseValue і чиє посилається ім'я властивостіNameString, а прапор суворого режиму суворий.

Таким чином, властивості Об'єктів завжди доступні як посилання.

Довідково

У розділі 8.7 "Тип посилальної специфікації" описано, що посилання не є реальними типами в мові - вони використовуються лише для опису поведінки операторів видалення, typeof та призначень.

Визначення "Об'єкт"

У виданні 5.1 визначено, що "Об'єкт - це сукупність властивостей". Отже, ми можемо зробити висновок, що цінністю об'єкта є колекція, але щодо того, яка цінність колекції, визначено погано в специфікації, і потрібно трохи зусиль, щоб зрозуміти.


Мене ніколи не перестає дивувати, скільки людей плутають розрізнення між аргументами, переданими за значенням, аргументами, переданими посиланням, операціями над цілими об'єктами та операціями над їх властивостями. У 1979 році я не отримав ступінь з інформатики, вибравши замість того, щоб додати 15 годин або більше CS факультативів до своєї програми MBA. Тим не менш, мені незабаром стало зрозуміло, що я розумію ці поняття як мінімум настільки ж добре, як це було у когось із моїх колег, які мали ступінь з інформатики чи математики. Вивчіть Асемблер, і це стане цілком зрозумілим.
Девід А. Грей

3

Документи MDN пояснюють це чітко, не надто багатослівно:

Параметри виклику функції - це аргументи функції . Аргументи передаються функції за значенням . Якщо функція змінює значення аргументу, ця зміна не відображається глобально або в викличній функції. Однак посилання на об'єкти теж є значеннями, і вони є особливими: якщо функція змінює властивості згаданого об'єкта, ця зміна видно поза функцією, (...)

Джерело: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description


1

У мові низького рівня, якщо ви хочете передати змінну за посиланням, ви повинні використовувати певний синтаксис при створенні функції:

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

Це &ageпосилання на myAge, але якщо ви хочете, щоб значення, ви повинні перетворити посилання, використовуючи*age .

Javascript - мова високого рівня, яка робить це перетворення для вас. Отже, хоча об'єкти передаються за посиланням, мова перетворює опорний параметр у значення. Вам не потрібно використовувати &для визначення функції функцію, щоб передати її посиланням, а також *на тіло функції, щоб перетворити посилання на значення, JS робить це за вас.

Ось чому, коли ви намагаєтеся змінити об’єкт всередині функції, замінивши його значення (тобто age = {value:5}), зміна не зберігається, але якщо ви зміните його властивості (тобто age.value = 5), це відбувається.

Вивчайте більше


1

Я читав ці відповіді кілька разів, але НЕ РОЗПОВІДАЮТЬ її, поки не дізнався про технічне визначення "Зателефонуйте через спільний доступ", як його називали Барбара Лісков

Семантика виклику шляхом спільного використання відрізняється від виклику за посиланням тим, що присвоєння функціям аргументів у межах функції не видно абоненту (на відміну від посилальної семантики) [потрібне цитування], тому, наприклад, якщо змінна була передана, це неможливо для імітації присвоєння цій змінній в області виклику. Однак, оскільки функція має доступ до того ж об'єкта, що і абонент (копія не робиться), мутації до цих об'єктів, якщо об'єкти є змінними, в межах функції видно абоненту, який, можливо, відрізняється від виклику за значенням семантика. Мутації об'єкта, що змінюється, у межах функції видно абоненту, оскільки об'єкт не копіюється та не клонується - він спільний.

Тобто посилання на параметри змінюються, якщо ви переходите до самого значення параметра. З іншого боку, присвоєння параметру зникне після оцінки та недоступне для абонента функції.


Ні, чи об’єкт є змінним чи ні, насправді це не проблема. Все завжди передається цінністю. Це просто залежить від того, що ви проходите (значення чи посилання). Дивіться це .
Скотт Маркус

Те, що вона описує, передає посилання BY-VALUE. Немає підстав для введення нової термінології.
Санджив

1

Здебільшого найпростіший спосіб

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )є жахливим способом глибокого клонування предметів. Це не тільки повільно, але й може призвести до втрати даних.
Д. Пардал

0

Я знайшов спосіб продовжити з бібліотеки Underscore.js дуже корисно , коли я хочу передати в об'єкт в якості параметра , який може бути або змінені або замінені повністю.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

Найяскравіше пояснення, яке я знайшов, було в посібнику зі стилю AirBNB :

  • Примітиви : Коли ви отримуєте доступ до примітивного типу, ви працюєте безпосередньо над його значенням

    • рядок
    • число
    • булева
    • нуль
    • невизначений

Наприклад:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Комплекс : Коли ви отримуєте доступ до складного типу, ви працюєте над посиланням на його значення

    • об’єкт
    • масив
    • функція

Наприклад:

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

bar[0] = 9;

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

Тобто фактично примітивні типи передаються за значенням, а складні типи передаються за посиланням.


Ні, все завжди передається за значенням. Це просто залежить від того, що ви проходите (значення чи посилання). Дивіться це .
Скотт Маркус

-1

Я б сказав, що це пропускна копія -

Розглянемо аргументи та змінні об'єкти - це об'єкти, створені під час виконання контексту, створеного на початку виклику функції - і ваше фактичне значення / посилання, передане у функцію, просто зберігається в цьому аргументі + об'єкти змінної.

Простіше кажучи, для примітивних типів значення копіюються на початку виклику функції, для типу об'єкта посилання копіюється.


1
"pass-by-copy" === пропуск за значенням
Скотт Маркус

-1

Там якась - то дискусія по приводу використання терміна «передати за посиланням» в JavaScript тут , але відповісти на ваше запитання:

Об'єкт автоматично передається шляхом посилання без необхідності конкретно його констатувати

(Зі згаданої статті.)


7
Пов'язана стаття більше не включає ці твердження і уникає використання "пройти посилання" взагалі.
C Перкінс

Значення є посиланням

-2

Найпростіший спосіб визначити, чи є щось "пройти за посиланням", чи можна написати функцію "swap". Наприклад, в C ви можете:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Якщо ви не можете зробити еквівалент цього в JavaScript, це не "пройти посиланням".


21
Це насправді не проходить шляхом посилання. Ви передаєте покажчики у функцію, і ці вказівники передаються за значенням. Кращим прикладом може бути ключове слово C &+ або оператор або ключове слово C # 'ref', обидва справді проходять через посилання.
Метт Грір

Ще простіше, що все передається за значенням у JavaScript.
Скотт Маркус


-3
  1. Змінна примітивного типу, як рядок, число, завжди передаються як значення передачі.
  2. Масив і Об'єкт передаються як прохідні посилання або пропускні значення на основі цих двох умов.

    • якщо ви змінюєте значення цього Об'єкта чи масиву за допомогою нового Об'єкта чи масиву, він передається за значенням.

      object1 = {item: "car"}; array1=[1,2,3];

    тут ви присвоюєте новий об’єкт або масив старому. ви не змінюєте значення властивості старого об'єкта. тому воно передається за значенням.

    • якщо ви змінюєте значення властивості об'єкта чи масиву, воно передається посиланнями.

      object1.key1= "car"; array1[0]=9;

    тут ви змінюєте значення властивості старого об'єкта. ви не присвоюєте новий об'єкт чи масив старому. тому він передається посиланням.

Код

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10

1
Оператора призначення не слід плутати з викликом функції. Якщо ви присвоюєте нові дані існуючій змінній, кількість посилань старих даних зменшується, а нові дані пов'язані зі старою змінною. В основному, змінна в кінцевому підсумку вказує на нові дані. Те саме стосується змінних властивостей. Оскільки ці призначення не є функціональними викликами, вони не мають нічого спільного з прохідною цінністю або пропускною посиланням.
Джон Сондерсон

1
Ні, все завжди передається за значенням. Це просто залежить від того, що ви проходите (значення чи посилання). Дивіться це .
Скотт Маркус

-3
  1. Примітиви (число, булеве значення тощо) передаються за значенням.
    • Струни незмінні, тому для них це насправді не має значення.
  2. Об'єкти передаються за допомогою посилання (посилання передається за значенням).

Ні, все завжди передається за значенням. Це просто залежить від того, що ви проходите (значення чи посилання). Дивіться це .
Скотт Маркус

Ваше друге твердження суперечить собі.
Jörg W Mittag

-5

Прості значення всередині функцій не змінять ці значення поза функцією (вони передаються за значенням), тоді як складні (передаються за посиланням).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

що смішно, y зміниться через функціонування рівня функціонального рівня, він піднімається не тому, що передається посиланням.
Parijat Kalia

Ні, все завжди передається за значенням. Це просто залежить від того, що ви проходите (значення чи посилання). Дивіться це .
Скотт Маркус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.