Присвоение нового значения объекту, переданному в функцию в JavaScript

Я новичок в JavaScript (хотя и опытен в C++), и сегодня я написал что-то вроде этого:

function foo(bar) {
    bar = "something else";
}
var x = "blah";
foo(x);
alert(x); // Alerts with "blah", but I was expecting it to alert with "something else"

это меня очень смутило, так как я смотрел некоторые видеоролики JavaScript Дугласа Крокфорда и помню, как он сказал что-то вроде "JavaScript всегда проходит по ссылке".

я могу объяснить эту ситуацию тем, что JavaScript передает ссылки на объекты, но эти ссылки копируются. Это означало бы в

6 ответов


"JavaScript всегда проходит по ссылке" - это, ну,[белый] ложь и путаница терминов. Пока есть какая-то" серая зона", Я иду мимо эти определения стратегий оценки.

вот мои аргументы и рассуждения. Если вы придерживаетесь другого представления, убедитесь, что вы можете хотя бы поддержать его.

Вызов По Ссылке

вызов по ссылочным средствам (to много человек), что присвоение параметру влияет "привязки" абонента.

в оценке call-by-reference (также называемой pass-by-reference) функция получает неявную ссылку на переменную, используемую в качестве аргумента, а не копию ее значения. это обычно означает, что функция может изменять (т. е. присваивать) переменную, используемую в качестве аргумента, - то, что будет видно ее вызывающему.

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

иногда "вызов по ссылке "[путано] используется для обозначения" вызова путем совместного использования "или" вызова по значению [ссылки]", как обсуждается далее; истинный вызов по ссылке найден в таких языках, как C++ и VB, но не JavaScript.

Призыв [Объект] Обмен

соглашения о вызовах JavaScript можно обсуждать полностью с точки зрения Вызов По [Object] Sharing семантика.

все объекты JavaScript are значения; и все примитивные значения (которые являются подмножеством всех значений) неизменяемы.

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

пример Shared мутации предоставляются в ответ ultrayoshi и может быть объяснен просто: когда выражение (например, доступ к переменной) оценивает объект, и указанный объект передается функции,нет копирование / клонирование сделано.

вызов по значению [ссылка]

хотя терминология "вызов по значению [ссылки]" часто используется для описания поведения, следует отметить, что JavaScript его не есть "ссылки" (или " non-reference" значения) в смысле Java / C#, поэтому эта терминология тонко вводит в заблуждение - по крайней мере, это не говорит о вызове по ссылке, с различными коннотациями, и многие люди понимают низкое объяснение.

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

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

описание "вызов по значению, где ссылка" является общим (но не следует понимать как вызов по значению); другой термин-call-by-sharing.


таким образом, когда я говорю о вызове соглашений в JavaScript,

Я предпочитаю использовать призыв поделиться, чтобы обсудить поведение и я избежать вызов по [Value / Reference], поскольку они имеют слишком много разных "значений" и перетаскивают ненужные детали реализации.


ваша интерпретация находится на месте.

во-первых, у вас есть переменная с именем x что это ссылка к строковому объекту. Предположим, что память 0x100. x указывает на 0x100, который содержит байты мля:

var x = "blah"; // x is 0x100 which references a string in memory

Далее, вы проходите 0x100 функции foo:

function foo(bar) {
    bar = "something else";
}

As все в JavaScript передается по значению, даже ссылки, JavaScript делает копию эта ссылка в памяти, которая теперь называется bar в этой функции:

foo(x); // Copies the value of x (a reference) to bar

на данный момент у нас есть две отдельные переменные. x и bar. Оба!-Произошло -38--> иметь такое же значение, 0x100. Таким образом, если вы измените свойство объекта, на которое ссылается любой из них, это повлияет на оба x и bar.

однако,ты делаете назначение bar, чтобы указать на что-то еще:

bar = "something else"; // Now references some other string we just created

теперь bar получает повторное назначение для ссылки на новую строку, для которой мы только что выделили память. bar не имеет значения 0x100, теперь он имеет значение другого адреса (скажем 0x500). x конечно еще имеет значение 0x100 С bar просто копия x, а не Ссылка на x.

по этой причине, когда вы:

alert(x);

вы все равно получите исходное значение, так как это то, что x указывает на.

второй вопрос:

есть ли способ, чтобы сказать, изменить данные, на которые ссылается эта переменная? Это ситуация, которая часто возникает, или ее можно легко избежать?

да, просто оберните его в другой объект. Например:

var x = {Value: "blah"};
foo(x);

теперь у нас есть ссылка на объект со свойством Value, который содержит ссылку на a строка где-то в памяти.

на foo, мы можем сделать:

bar.Value = "something else";

который повлияет на Value собственность x, так как bar и x ссылается на один и тот же объект, и вы никогда не меняли значение ни одного из них.

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


ваша интерпретация верна.

вы можете изменить значения ключей в объекте, что позволяет сделать что-то похожее на pass-by-reference:

function foo(bar) {
  bar.msg = "something else";
}
var x = { msg: "blah" };
foo(x);
alert(x.msg);

Дуглас Крокфорд в видео, которое я смотрел, говорит "объектов всегда прошел по ссылке, они не передаются по значению", что является правильным, но аргументы функции передаются по значению, это просто ссылка передается по значению.

это неправильно. То, что это описывает, точно называется pass-by-value. JavaScript, как и Java, имеет только значение pass-by-value. Нет никакой ссылки.

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


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

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

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

x = {};
y = x; //point y reference to same object x is pointed to

console.log(x === y) //true both pointed at same object

function funky(o) //pass by value a reference to object
{
  o = null; //change reference to point to null away from object
}

funky(x);

console.log(x)//not null still an object

var myObj = {
  value: "Hello"
};

function change(localObj) {//pass reference to object
  localObj.value = "Bye";//change property through the reference
  localObj = null;//point the reference to null away from object
}

console.log(x === y) //still same reference;
change(myObj);
console.log(myObj.value) // Prompts "Bye"
console.log(myObj) //not null - its an object;
x = myObj; //point x to different object
console.log(x === y) //false pointed at different object;
console.log(x);
console.log(y);

https://codepen.io/waynetheisinger/pres/YewMeN


примитивные значения как числа, строки и т. д. не передаются по ссылке, только объекты. Например:

var myObj = {
  value: "Hello"
};

function change(localObj) {
  localObj.value = "Bye";
}

change(myObj);
console.log(myObj.value) // Prompts "Bye"