Является ли JavaScript языком передачи по ссылке или передачей по значению?

1236

Примитивные типы (Number, String и т.д.) передаются по значению, но объекты неизвестны, поскольку они могут быть как переданными по значению (в случае, если мы считаем, что переменная, содержащая объект, на самом деле является ссылкой к объекту) и передается по ссылке (когда мы считаем, что переменная к объекту содержит сам объект).

Хотя в конце концов это не имеет большого значения, я хочу знать, каков правильный способ представления соглашений о передаче аргументов. Есть ли выдержка из спецификации JavaScript, которая определяет, какова должна быть семантика относительно этого?

  • 0
    Я думаю, что у нас есть много доказательств ниже, какова точная семантика языка. Теперь мы пропускаем только выдержку из спецификации EcmaScript, которая ее определяет, и у нас будет ответ на этот вопрос.
  • 1
    Я думаю, что вы случайно перевернули свои определения переданных по значению и переданных по ссылке ... »переданных по значению (в случае, если мы считаем, что переменная, содержащая объект, на самом деле является ссылкой на объект) и передали -по ссылке (когда мы считаем, что переменная для объекта содержит сам объект) "
Показать ещё 12 комментариев
Теги:
pass-by-value
pass-by-reference

33 ответа

1375

Это интересно в 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.item не повлияло бы на obj1 вне функции.
  • Если бы это был чистый проход по ссылке, тогда все изменилось бы. num будет равно 100, а obj2.item будет читать "changed".

Вместо этого ситуация заключается в том, что переданный элемент передается по значению. Но элемент, который передается по значению, сам по себе является ссылкой. Технически это называется совместным использованием вызовов.

В практическом плане это означает, что если вы измените сам параметр (как и на num и obj2), это не повлияет на элемент, который был подан в параметр. Но если вы измените INTERNALS параметра, который будет распространяться обратно (как с obj1).

  • 25
    Это точно так же (или, по крайней мере, семантически), как C #. Объект имеет два типа: Value (примитивные типы) и Reference.
  • 42
    Я думаю, что это также используется в Java: ссылка по значению.
Показать ещё 23 комментария
403

Он всегда проходит по значению, но для объектов значение переменной является ссылкой. Из-за этого, когда вы передаете объект и меняете его члены, эти изменения сохраняются вне функции. Это заставляет его выглядеть как ссылка. Но если вы действительно измените значение переменной объекта, вы увидите, что это изменение не сохраняется, доказывая, что оно действительно передается по значению.

Пример:

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

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

var x = {member:"foo"};

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

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

Вывод:

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

before changeMember: foo
in changeMember: bar
after changeMember: bar
  • 0
    Привет Тим, спасибо за отличный ответ. У меня есть крошечное предложение для ясности - не могли бы вы инвертировать либо порядок объявлений функций, либо порядок вызовов для соответствия друг другу? Мне потребовалось некоторое время, чтобы понять, что результаты не сокращают ваше заключение! :)
  • 13
    @ Daylight: На самом деле, вы не правы; если он был передан const ref, попытка сделать changeObject вызовет ошибку, а не просто потерпит неудачу. Попробуйте назначить новое значение константной ссылке в C ++, и компилятор отклонит его. С точки зрения пользователя, это разница между передачей по значению и передачей по константной ссылке.
Показать ещё 8 комментариев
138

Переменная не "удерживает" объект, он содержит ссылку. Вы можете назначить эту ссылку другой переменной, теперь оба ссылаются на один и тот же объект. Он всегда проходит по значению (даже если это значение является ссылкой...).

Невозможно изменить значение, удерживаемое переменной, переданной как параметр, что было бы возможно, если бы JS поддерживала передачу по ссылке.

  • 2
    Это меня немного смущает. Не передается ли ссылка по ссылке?
  • 4
    Автор подразумевает, что, передавая ссылку, вы передаете ссылочное значение (другой способ думать об этом - передать значение адреса памяти). Поэтому, если вы переопределите объект, оригинал не изменится, потому что вы создаете новый объект в другом месте памяти. При изменении свойства исходный объект изменяется, потому что вы изменили его в исходной ячейке памяти (которая не была переназначена).
82

Мои 2 цента... Я так понимаю. (Не стесняйтесь исправить меня, если я ошибаюсь)

Пришло время выбросить все, что вы знаете о прохождении по значению/ссылке.

Потому что в JavaScript не имеет значения, прошел ли он по значению или по ссылке или что-то еще. Важна мутация и назначение параметров, переданных в функцию.

Хорошо, позвольте мне изо всех сил объяснить, что я имею в виду. Скажем, у вас есть несколько объектов.

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

То, что мы сделали, это "назначение"... Мы назначили 2 отдельных пустых объекта для переменных "object1" и "object2".

Теперь скажем, что нам нравится object1 лучше... Итак, мы "назначаем" новую переменную.

var favoriteObject = object1;

Далее, по какой-то причине мы решили, что нам нравится объект 2 лучше. Итак, мы просто немного переустановили.

favoriteObject = object2;

Ничего не произошло с object1 или object2. Мы вообще ничего не изменили. Все, что мы сделали, было переназначением того, что наш любимый объект. Важно знать, что объекты 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" favoriteString для новой строки. Это существенно отключило его от string1. В предыдущем примере, когда мы переименовали наш объект, мы ничего не назначали. (Ну, на самом деле... мы это сделали, мы присвоили свойство name новой строке.) Вместо этого мы просто мутировали объект, который поддерживает связи между двумя переменными и базовыми объектами.

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

Возьмите эти примеры.

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 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 reassign
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

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

Теперь то же самое, но с вызовом функции

function myFunc(otherObj) {

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

    // Now let 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 no longer mutates 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. Не имеет значения, передается ли что-то по ссылке или по значению... Что важно, так это назначение vs mutation.

Каждый раз, когда вы передаете переменную функции, вы "присваиваете" любому имени переменной параметра, как если бы вы использовали знак равенства (=).

Всегда помните, что знак равенства (=) означает назначение. Всегда помните, что передача параметра функции также означает назначение. Они одинаковы, и 2 переменные связаны точно так же.

Единственный момент, когда изменение переменной влияет на другую переменную, - это когда базовый объект мутирован.

Нет смысла делать различия между объектами и примитивами, потому что он работает точно так же, как если бы у вас не было функции, и просто использовал знак равенства для назначения новой переменной.

Единственное, что получилось, это то, что имя переменной, которую вы передаете в функцию, совпадает с именем параметра функции. Когда это происходит, вы должны обрабатывать параметр внутри функции, как если бы это была целая новая переменная, закрытая для функции (потому что она)

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'
  • 0
    @Rimbuaj - у меня действительно есть блог .. я делаю теперь так или иначе. Тот, который содержит обсуждения по кодированию. Я только что опубликовал свой первый пост о кодировании. constalink.com/blog
  • 0
    Любой программист на C думает о char *. foo(char *a){a="hello";} ничего не делает, но если вы делаете foo(char *a){a[0]='h';a[1]='i';a[2]=0;} он изменяется снаружи, потому что a - это ячейка памяти, передаваемая значением, которое ссылается на строку (массив символов). Передача структур (аналогично объектам js) по значению в C допускается, но не рекомендуется. JavaScript просто применяет эти лучшие практики и скрывает ненужные и, как правило, нежелательные излишества ... и, несомненно, облегчает чтение.
Показать ещё 5 комментариев
67

Рассмотрим следующее:

  • Переменные - это указатели на значения в памяти.
  • Переназначение переменной просто указывает на то, что указатель имеет новое значение.
  • Переназначение переменной никогда не повлияет на другие переменные, указывающие на тот же объект

Итак, забудьте о "pass by reference/value" , не зацикливайтесь на "pass by reference/value", потому что:

  • Термины используются только для описания поведения языка, а не для фактической базовой реализации. В результате этой абстракции теряются критические детали, которые необходимы для достойного объяснения, что неизбежно приводит к текущей ситуации, когда один термин не описывает адекватное описание фактического поведения и дополнительной информации.
  • Эти понятия изначально не были определены с целью описания 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
  • 0
    Дополнительные вопросы для дополнительного кредита;) Как работает сборка мусора? Если я перебираю переменную по миллиону значений {'George', 1} , но использую только одно из них одновременно, то как управляются остальные? И что происходит, когда я назначаю переменную значению другой переменной? Затем я указываю на указатель или указываю на правый операнд? var myExistingVar = {"blah", 42}; var obj = myExistingVar; в результате obj указывает на {"blah", 42} или на myExistingVar ?
  • 0
    @MichaelHoffmann Они заслуживают своих собственных вопросов SO и, вероятно, уже получили ответы лучше, чем я могу управлять. При этом, 1) Я запустил профиль памяти в инструментах разработчика браузера для функции цикла, такой как описанная вами, и увидел скачки в использовании памяти на протяжении всего цикла. Казалось бы, это указывает на то, что новые идентичные объекты действительно создаются на каждой итерации цикла. Когда шипы внезапно падают, сборщик мусора просто очищает группу от этих неиспользуемых объектов.
Показать ещё 4 комментария
23

Объект вне функции передается в функцию, давая ссылку на внешний obejct. Когда вы используете эту ссылку для манипулирования своим объектом, объект наружу, таким образом, затрагивается. Однако, если внутри функции вы решили указать ссылку на что-то еще, вы вообще не повлияли на объект снаружи, потому что все, что вы делали, перенаправляло ссылку на что-то еще.

19

Подумайте об этом так: он всегда проходит по значению. Однако значение объекта не является самим объектом, а ссылкой на этот объект.

Вот пример, передающий число (примитивный тип)

function changePrimitive(val) {
    // At this point there are two '10 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 reference now points to the new object.
    // x reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
14

Javascript всегда передается по значению, все имеет тип значения. Объекты являются значениями, функции-члены объектов сами являются значениями (помните, что функции являются первоклассными объектами в Javascript). Кроме того, в отношении концепции, что все в Javascript является объектом, это неправильно. Строки, символы, числа, булевы, нули и неопределенные значения являются примитивами. Иногда они могут использовать некоторые функции-члены и свойства, унаследованные от их базовых прототипов, но это только для удобства, это не значит, что они сами объекты. Попробуйте следующее для справки

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

В обоих предупреждениях вы найдете значение undefined.

  • 0
    Передайте 5 в функцию, затем вызовите theParam.toPrecision (4), и это работает. Это означает, что это объект. Если он может использовать что-либо из своего прототипа, то это должен быть объект. В отличие от Ruby, вы можете выполнять 5.someMethod (), но каждая переменная помещается в соответствующий объект.
  • 0
    Это не объект для запуска, но когда к нему вызывается метод, он переносится.
Показать ещё 14 комментариев
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.

Источник

  • 0
    Сначала вы заявляете «Составные значения всегда присваиваются / передаются с помощью reference-copy», а затем вы утверждаете, что «назначает копию ссылки на x». В случае того, что вы называете «составным значением», фактическое значение переменной является ссылкой (т. Е. Указатель памяти). Как вы объяснили, ссылка копируется ... поэтому значение переменной копируется , снова подчеркивая, что ССЫЛКА - ЗНАЧЕНИЕ. Это означает, что JavaScript передается по значению для всех типов. Передача по значению означает передачу копии значения переменной. Неважно, что значение является ссылкой на объект / массив.
  • 0
    Вы вводите новую терминологию (value-copy / reference-copy), и это только усложняет ситуацию. Есть только копии, точка. Если вы передаете примитив, вы передаете копию фактических данных примитива, если вы передаете объект, вы передаете копию расположения объекта в памяти. Это все, что тебе нужно сказать. Что-то еще, только еще больше смущает людей.
11

Очень подробное объяснение о копировании, передаче и сравнении по значению и по ссылке находится в в этой главе книги "JavaScript: The Definitive Guide".

Прежде чем оставить тему манипулирование объектами и массивами ссылку, нам нужно прояснить точку номенклатуры. Фраза "проходите мимо ссылка" может иметь несколько значений. Для некоторых читателей эта фраза относится к метод вызова функции, который позволяет назначать новые значения к его аргументам и измененные значения, видимые вне функция. Это не значит, что термин используется в этой книге. Здесь мы имеем в виду просто, что ссылка на объект или массив - не сам объект - передается функции. Функция может использовать ссылку для изменения свойства объекта или элементов массива. Но если функция перезаписывает ссылку с помощью ссылку на новый объект или массив, эта модификация не видна вне функции. Читатели знакомы с другим значением этот термин может предпочесть сказать, что объекты и массивы передаются значение, но переданное значение на самом деле ссылка, а не самого объекта.

Одна вещь, которую я до сих пор не могу понять. Проверьте код ниже. Любые мысли?

function A() {}
A.prototype.foo = function() {
    return 'initial value';
}


function B() {}
B.prototype.bar = A.prototype.foo;

console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value

A.prototype.foo = function() {
    return 'changed now';
}

console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???
  • 0
    Я точно объяснил, что вы сделали, и что это значит, но эти комментарии не позволяют мне размещать более 600 символов или что-то подобное, поэтому я разместил это в качестве ответа ниже. Пожалуйста, проверьте это и прокомментируйте. THX ~ дневной свет
  • 1
    Похоже, что B.prototype.bar = A.prototype.foo; назначается по ссылке, но последний A.prototype.foo = function () {...} изменяет значение свойства foo объекта A.prototype и прерывает ссылку. B.prototype.bar по-прежнему содержит ссылку на ту же первую функцию.
Показать ещё 2 комментария
6

JavaScript передает примитивные типы по значению и типы объектов по ссылке

Теперь людям нравится бесконечно спорить о том, является ли "передача по ссылке" правильным способом описания того, что Java et al. на самом деле Дело в следующем:

  1. Передача объекта не копирует объект.
  2. Объект, переданный функции, может иметь свои члены, модифицированные функцией.
  3. Примитивное значение, переданное функции, не может быть изменено функцией. Копия сделана.

В моей книге это называется передачей по ссылке.

- Брайан Би - Какие языки программирования передаются по ссылке?


Обновить

Вот опровержение этого:

В JavaScript нет "передачи по ссылке".

  • 0
    Книга этого парня неверна.
  • 1
    @nasch Смотрит прямо на меня. Хотите разработать?
Показать ещё 4 комментария
3

Это немного больше объясняет значение Pass by value и Pass by reference (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
  • применяется ко всем примитивным типам в JS (строка, число, логическое, неопределенное, null).
  • 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" }
  • Механизм JS присваивает объект переменной c, он указывает на некоторую память (0x012)
  • когда d = c, на этом этапе d указывает на одно и то же местоположение (0x012).
  • изменяя значение любого значения изменений для обеих переменных.
  • функции - объекты

Специальный случай, Передача по ссылке (объекты)

c = {"name" : "jane"}; 
console.log(c); // { "name" : "jane" }    
console.log(d); // { "name" : "doe" }
  • Оператор равенства (=) устанавливает новое пространство памяти или адрес
  • 0
    В вашем так называемом особом случае выделение пространства памяти вызывает не оператор присваивания, а сам литерал объекта . Обозначение фигурной скобки вызывает создание нового объекта. Свойство c устанавливается на копию ссылки на новый объект.
3

Семантика!! Установка конкретных определений обязательно сделает некоторые ответы и комментарии несовместимыми, поскольку они не описывают одно и то же даже при использовании одних и тех же слов и фраз, но крайне важно преодолеть путаницу (особенно для новых программистов).

Прежде всего, есть несколько уровней абстракции, которые не все видят. Более новые программисты, которые изучили языки 4-го или 5-го поколения, могут испытывать трудности с обдумыванием концепций, знакомых для сборки, или программистов C, которые не поэтапны указателями указателям на указатели. Передача по ссылке означает не просто способность изменять объект, на который ссылается, используя переменную параметра функции.

Переменная: комбинированная концепция символа, который ссылается на значение в определенном месте в памяти. Этот термин обычно слишком загружен для использования отдельно при обсуждении деталей.

Символ: текстовая строка, используемая для ссылки на переменную (например, имя переменной).

Значение: отдельные бит, хранящиеся в памяти и ссылающиеся с использованием символа переменной.

Местоположение памяти. Где хранится значение переменной. (Само местоположение представлено числом, отдельным от значения, хранящегося в местоположении.)

Параметр функции: переменная, объявленная в определении функции, используемая для ссылки на переменные, переданные функции.

Аргумент функции: переменная вне функции, которая передается функции вызывающим абонентом.

Объектная переменная: переменная, базовое базовое значение которой не является самим объектом, а его значение является указателем (значением ячейки памяти) в другое место в памяти, где хранятся фактические данные объекта. На большинстве языков более высокого поколения аспект "указатель" эффективно скрывается путем автоматического удаления ссылок в различных контекстах.

Примитивная переменная: переменная, значение которой является фактическим значением. Даже эта концепция может быть осложнена автоматическим боксированием и объектно-подобными контекстами разных языков, но общие идеи заключаются в том, что переменная value IS является фактическим значением, представленным символом переменной, а не указателем на другое местоположение памяти.

Аргументы и параметры функции - это не одно и то же. Кроме того, значение переменной не является объектом переменной (как уже указывалось разными людьми, но, по-видимому, игнорируется). Эти различия имеют решающее значение для правильного понимания.

Передача по значению или совместное использование вызовов (для объектов). Значение параметра функции COPIED - это другое место памяти, на которое ссылается символ параметра функции (независимо от того, включен ли он стек или куча). Другими словами, параметр функции получил копию переданного значения аргумента... И (критическое) значение аргумента НИКОГДА НЕ ОБНОВЛЯЕТСЯ/ИЗМЕНЕН/ИЗМЕНЕН вызывающей функцией. Помните, что значение переменной объекта НЕ является самим объектом, скорее это указатель на объект, поэтому передача переменной объекта по значению копирует указатель на переменную параметра функции. Значение параметра функции указывает на тот же самый объект в памяти. Сами данные объекта могут быть изменены непосредственно через параметр функции, НО значение аргумента функции НИКОГДА НЕ ОБНОВЛЯЕТСЯ, поэтому оно будет продолжать указывать на один и тот же объект на протяжении всего и даже после вызова функции (даже если данные объекта были изменены или если функциональному параметру назначается другой объект вообще). Неправильно заключить, что аргумент функции передавался по ссылке только потому, что ссылочный объект обновляется через переменную параметра функции.

Call/Pass-by-reference. Значение аргумента функции может/будет обновляться непосредственно соответствующим параметром функции. Если это помогает, параметр функции становится эффективным "псевдонимом" для аргумента - они фактически ссылаются на одно и то же значение в том же месте памяти. Если аргумент функции является объектной переменной, возможность изменения данных объекта не отличается от случая с байтовым значением, поскольку параметр функции все равно указывает на тот же объект, что и аргумент. Но в случае переменной объекта, если параметр функции установлен на совершенно другой объект, тогда аргумент также будет указывать на другой объект - этого не происходит в случае с переходом.

JavaScript не проходит по ссылке. Если вы внимательно прочитаете, вы поймете, что все противоположные мнения неправильно понимают, что подразумевается под поправкой, и они ошибочно полагают, что способность обновлять данные объекта через параметр функции является синонимом "pass-by-value".

Клонирование/копирование объектов. Создается новый объект и копируются исходные данные объекта. Это может быть глубокая копия или мелкая копия, но дело в том, что создается новый объект. Создание копии объекта - это отдельная концепция из пропущенного значения. Некоторые языки различают объект класса и структуры (или тому подобное) и могут иметь различное поведение для передачи переменных разных типов. Но JavaScript не делает ничего подобного автоматически при передаче объектных переменных. Но отсутствие автоматического клонирования объектов не переводится на передачу по ссылке.

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

Все аргументы функции в ECMAScript передаются по значению. Это означает, что значение вне функция копируется в аргумент внутри функции так же, как копируется значение от одной переменной к другой. Если значение является примитивным, то оно действует так же, как примитивная переменная копировать, и если значение является ссылкой, оно действует как копия ссылочной переменной. Это часто бывает путаницы для разработчиков, поскольку к переменным обращаются как по значению, так и по ссылке, но аргументы передаются только по значению. Когда аргумент передается по значению, значение копируется в локальную переменную (именованный аргумент и в ECMAScript - слот в объекте arguments). Когда аргумент передается по ссылке, расположение значения в памяти сохраняется в локальной переменной, что означает, что изменения в локальном переменная отражается вне функции. (Это невозможно в ECMAScript.)

2

поделиться тем, что я знаю о ссылках в 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
  • 0
    Это слишком упрощенный ответ, который ничего не говорит о том, что более ранние ответы не объяснили лучше. Я не понимаю, почему вы вызываете массивы как особый случай.
2

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

TL; DR: каждое значение передается по значению, но свойства объектов - это ссылки, а определение объекта, как правило, отсутствует в стандарте.

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

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

Произведение ArgumentList: AssignmentExpression оценивается следующим образом:

  • Пусть ref является результатом оценки AssignmentExpression.
  • Пусть arg - GetValue (ref).
  • Возвращает список, единственным элементом которого является arg.

В разделе также перечислены случаи, когда список аргументов имеет 0 или > 1 аргумент.

Таким образом, все передается по ссылке.

Доступ к объектным свойствам

Раздел 11.2.1 "Аксессоры свойств"

Произведение MemberExpression: MemberExpression [Expression] оценивается следующим образом:

  • Пусть baseReference будет результатом оценки MemberExpression.
  • Пусть baseValue будет GetValue (baseReference).
  • Пусть свойствоNameReference является результатом вычисления выражения.
  • Пусть свойствоNameValue будет GetValue (propertyNameReference).
  • Вызов CheckObjectCoercible (baseValue).
  • Пусть свойствоNameString - ToString (propertyNameValue).
  • Если синтаксическое производство, которое оценивается, содержится в коде строгого режима, пусть строгое истинно, иначе пусть строгое ложное.
  • Возвращает значение типа Reference, базовым значением которого является baseValue, а ссылочное имя - propertyNameString и строгий флаг строгого режима.

Таким образом, свойства объектов всегда доступны в качестве ссылки.

On Reference

Это описано в разделе 8.7 "Тип ссылочной спецификации", что ссылки не являются реальными типами в языке - они используются только для описания поведения операторов delete, typeof и присваивания.

Определение "Объект"

В редакции 5.1 определено, что "Object - это совокупность свойств". Поэтому мы можем заключить, что значение объекта - это коллекция, но что касается того, что значение коллекции плохо определено в спецификации, и требует немного усилий понять.

  • 0
    Меня не перестает удивлять то, как много людей смущают различия между аргументами, передаваемыми по значению, аргументами, передаваемыми по ссылке, операциями над целыми объектами и операциями над их свойствами. В 1979 году я не получил степень по информатике, вместо этого я решил добавить 15 часов факультативного обучения по программе CS в свою программу MBA. Тем не менее, вскоре мне стало ясно, что мое понимание этих концепций было, по крайней мере, таким же хорошим, как и у любого из моих коллег, имеющих ученые степени в области компьютерных наук или математики. Изучите Ассемблер, и это станет вполне понятно.
2

Я читал эти ответы несколько раз, но НЕ ДЕЙСТВИТЕЛЬНО получил его, пока не узнал о техническом определении "Call by sharing" как называется Барбара Лисков

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

То есть, ссылки на параметры могут быть изменены, если вы перейдете и получите доступ к самому значению параметра. С другой стороны, назначение параметра исчезает после оценки и не доступно для вызывающего функции.

  • 0
    Нет, проблема в том, является ли объект изменчивым или нет. Все всегда передается по значению. Это зависит только от того, что вы передаете (значение или ссылка). Смотрите это .
2

На языке низкого уровня, если вы хотите передать переменную по ссылке, вы должны использовать определенный синтаксис при создании функции:

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

&age является ссылкой на myAge, но если вам нужно значение, которое вы должны преобразовать ссылку, используйте *age.

Javascript - это язык высокого уровня, который делает это преобразование для вас. Таким образом, хотя объекты передаются по ссылке, язык преобразует ссылочный параметр в значение. Вам не нужно использовать &, в определении функции, передать его по ссылке, а не *, в тело функции, чтобы преобразовать ссылку на значение, JS делает это для вас.

Поэтому, когда вы пытаетесь изменить объект внутри функции, заменив его значением (т.е. age = {value:5}), это изменение не сохраняется, но если вы измените его свойства (т.е. age.value = 5), это произойдет.

Подробнее...

2

Мой простой способ понять это...

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

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2 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; 
    }
    
2

Здесь обсуждается использование термина "передать по ссылке" в JS здесь, но чтобы ответить на ваш вопрос:

Объект автоматически передается по ссылке, без необходимости конкретно указывать его

(Из статьи, упомянутой выше.)

  • 4
    Связанная статья больше не включает в себя эти утверждения и вообще избегает использования «передачи по ссылке».
1

Самое сжатое объяснение, которое я нашел, было в руководстве по стилю AirBNB:

  • Примитивы. Когда вы получаете доступ к примитивному типу, вы работаете непосредственно с его Значение

    • строка
    • номер
    • булева
    • NULL
    • undefined

например:.

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

т.е. эффективно примитивные типы передаются по значению, а сложные типы передаются по ссылке.

  • 0
    Нет, все всегда передается по значению. Это зависит только от того, что вы передаете (значение или ссылка). Смотрите это .
0

В JavaScript отсутствует "проход по ссылке". Вы можете передать объект (то есть вы можете передавать по значению ссылку на объект), а затем изменить функцию содержимого объекта:

function alterObject(obj) {
  obj.foo = "goodbye";
}

var myObj = { foo: "hello world" };

alterObject(myObj);

alert(myObj.foo); // "goodbye"

Важно отметить, что "перекрестная ссылка" - очень специфический термин. Это не значит, что можно передать ссылку на модифицируемый объект. Вместо этого это означает, что можно передать простую переменную таким образом, чтобы позволить функции изменять это значение в вызывающем контексте. Так:

function swap(a, b) {
   var tmp = a;
   a = b;
   b = tmp; //assign tmp to b
 }

 var x = 1, y = 2;
 swap(x, y);
 // x and y dont changed in method
 alert("x is " + x + " and y is " + y); // x is 1 and y is 2
0

В JavaScript примитивные параметры передаются функции по значению, например, когда вы передаете число функции. Если функция изменяет значение параметра, это изменение не отражается в вызывающей функции.

Если вместо этого вы передаете объект как не примитивный, как Array или определяемый пользователем объект в качестве параметра функции, объект передается как ссылка. Если функция изменяет свойства объекта, это изменение отражается в вызывающей функции.

0

Я бы сказал, что это проход за копией -

Рассмотрение аргументов и объектов переменных - это объекты, созданные во время контекста выполнения, созданного в начале вызова функции, - и ваше фактическое значение/ссылка, переданная в функцию, просто сохраняется в этих аргументах + объекты переменных.

Проще говоря, для примитивных типов значения будут скопированы в начале вызова функции, для типа объекта ссылка будет скопирована.

  • 0
    "передача по копии" === передача по значению
0
  • Примитивы (Number, Boolean) передаются по значению.
    • Строки неизменяемы, поэтому для них это не имеет особого значения.
  • Объекты передаются по ссылке (ссылка передается по значению)
  • 0
    Нет, все всегда передается по значению. Это зависит только от того, что вы передаете (значение или ссылка). Смотрите это .
0

Я нашел метод расширения библиотека Underscore.js очень полезно, когда я хочу передать объект как параметр, который может быть полностью или полностью изменен.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

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

Легкий способ определить, является ли что-то "переходом по ссылке", можно ли написать функцию "своп". Например, в C вы можете:

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

Если вы не можете сделать эквивалент этого в Javascript, это не "передать по ссылке".

  • 17
    Это на самом деле не передать по ссылке. Вы передаете указатели в функцию, и эти указатели передаются по значению. Лучшим примером может служить C ++ & operator или ключевое слово ref в C #, оба они действительно передаются по ссылке.
  • 0
    Еще проще то, что все передается по значению в JavaScript.
Показать ещё 1 комментарий
-1
  • переменная типа примитива, как строка, номер всегда проходит как проход по значению.
  • Массив и объект передаются как проход по ссылке или передаются по значению на основе этих двух условий.

    • если вы изменяете значение этого Объекта или массива с помощью нового объекта или массива, тогда оно передается по значению.

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

    здесь вы назначаете новый объект или массив старым. Вы не меняете значение свойства старого объекта. Также это пропуск по значению.

    • если вы изменяете значение свойства объекта или массива, тогда оно передается по ссылке.

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

    здесь вы меняете значение свойства старого объекта. Вы не назначаете новый объект или массив в old one.so он проходит по ссылке.

код

    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
    Оператор присваивания не следует путать с вызовом функции. Когда вы присваиваете новые данные существующей переменной, счетчик ссылок на старые данные уменьшается, а новые данные связываются со старой переменной. По сути, переменная в конечном итоге указывает на новые данные. То же самое верно для переменных свойств. Поскольку эти назначения не являются вызовами функций, они не имеют ничего общего с передачей по значению или передачей по ссылке.
  • 0
    Нет, все всегда передается по значению. Это зависит только от того, что вы передаете (значение или ссылка). Смотрите это .
-1

В некоторых случаях это может быть полезно изменить anArg:

function alterMyArg(func) {
    // process some data
    // ...
    func(data);
}

alertMyArg(function(d) {anArg = d;});
-3
  • Когда объект создается, он существует в куче.
  • Когда устанавливается примитивное значение, он живет в стеке.
  • Чтобы использовать либо, вы должны иметь связанную с ним переменную.

С объектом переменная по существу является указателем на стек, который указывает на ячейку памяти в куче, где хранится объект. С помощью примитива переменная и значение сохраняются вместе в стеке.

Когда вы передаете объектную переменную в JavaScript, она всегда передается по ссылке, что означает, что копия REFERENCE передается вызываемому. Если основной объект управляется вызываемым пользователем, исходный объект также будет изменен вне функции.

Когда вы передаете переменную стека в JavaScript, копия фактического значения передается вызываемому. Если вызывающий изменяет значение, которое он получает, значение не изменяется вне функции.

-3

Простые значения внутри функций не изменят эти значения вне функции (они передаются по значению), тогда как сложные (будут переданы по ссылке).

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
  • 0
    что смешно, у изменится из-за функционального уровня, он поднимается не потому, что передается по ссылке.
  • 0
    Нет, все всегда передается по значению. Это зависит только от того, что вы передаете (значение или ссылка). Смотрите это .
-4

Примитивы передаются по значению, а объекты передаются по ссылке. Это сильно отличается от других языков, таких как C, VB или Delphi. Я не могу сказать, как они точно обрабатывают объекты и примитивы, но я знаю VB и Delphi, что он может (и должен) быть указан.

php делает что-то подобное с версии 5: все объекты передаются по ссылке, но все примитивы могут передаваться по ссылке, если предшествует амперсанд (&). В противном случае примитивы передаются по значению.

Итак, в javascript, если я передам объект X в функцию через параметр, он все равно будет X. Если вы меняете данные внутри функции (или любого другого объекта, но это не важно), что новое значение также доступный вне функции.

  • 0
    «это новое значение также доступно вне функции». Это неверно. См. Jsfiddle.net/rdarc . Значение не изменяется, вместо этого изменяется ссылка.
  • 0
    @ Том, изменив свой код на var x = {hi : 'hi'}; change(x); console.log(x.hi); function change(x) { x.hi = 'changed'; } меняет поведение.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню