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

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

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

38 ответов

Это интересно в 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), это не повлияет на элемент, который был введен в параметр. Но если вы измените ВНУТРЕННИЕ параметры, это будет распространяться обратно (как с obj1).

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

Пример:

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

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

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

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

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

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

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

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

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

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

var favoriteObject = object1;

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

favoriteObject = object2;

Ничего не случилось с object1 или object2. Мы не изменили никаких данных вообще. Все, что мы сделали, это переназначили наш любимый объект. Важно знать, что object2 и FavoritesObject назначены одному и тому же объекту. Мы можем изменить этот объект с помощью любой из этих переменных.

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;

Обе переменные favouriteString и string1 присвоены "Hello world". А что если мы захотим изменить нашу любимую строку??? Что случится???

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

Э-э-э... Что случилось? Мы не могли изменить string1, изменив favourString... Почему?? потому что строки неизменны, и мы не мутировали его. Все, что мы сделали, это "RE ASSIGN" FavoritesString для новой строки. Это по существу отключило его от 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'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
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'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 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. Неважно, передается ли что-то по ссылке или по значению... Что важно, так это присвоение или мутация.

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

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

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

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

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

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'

Учтите следующее:

  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

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

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

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

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}

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

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

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

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

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

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

В обоих оповещениях вы найдете значение, которое не определено.

В 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,

Источник

Ну , это о "производительности" и "скорости" и простом слове "управление памятью" в языке программирования.

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

если вы представите память в виде квадратов ниже, в каждом из которых может быть сохранено только одно значение type2:

каждое значение type2 (зеленый) - это один квадрат, а значение type1 (синий) - это их группа:

Дело в том, что если вы хотите указать значение type2, адрес будет простым, но если вы хотите сделать то же самое для значения type1, это совсем не просто!:

и в более сложной истории:

так что здесь ссылки могут нас спасти:

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

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

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

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

Это немного больше объяснения для передачи по значению и передачи по ссылке (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(строка, число, логическое значение, неопределенное значение, нуль).
  • 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" }
  • Оператор равенства (=) устанавливает новое пространство памяти или адрес

Все передается по стоимости.

Базовые типы передаются по значению (т. Е. В функцию передается новая копия фактического значения переменной).

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

Путаница здесь возникает по поводу «передачи указателя по значению» и «передачи объекта по ссылке».

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

JavaScript более или менее независим от базовой модели памяти. Там нет такого понятия, как ссылки². У JavaScript есть значения. Две переменные могут содержать одно и то же значение (точнее: две записи среды могут связывать одно и то же значение). Единственный тип значений, которые можно изменять, - это объекты с помощью их абстрактных операций [[Get]] и [[Set]]. Если вы забудете о компьютерах и памяти, это все, что вам нужно для описания поведения JavaScripts, и это позволит вам понять спецификацию.

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

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

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

² Термин " Ссылка" в спецификации не является ссылкой в ​​традиционном смысле. Это контейнер для объекта и имени свойства и промежуточное значение (например,a.b оценивает Reference { value = a, name = "b" }). Ссылка на термин также иногда появляется в спецификации в несвязанных разделах.

делиться тем, что я знаю о ссылках в 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

Документы MDN объясняют это ясно, не будучи слишком многословным:

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

Источник: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions

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

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

  1. Передача объекта не копирует объект.
  2. Объект, переданный функции, может иметь свои члены, модифицированные функцией.
  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; 
    }
    

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

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

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

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

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

  1. Пусть ref будет результатом вычисления AssignmentExpression.
  2. Пусть arg будет GetValue(ref).
  3. Вернуть список, единственным пунктом которого является arg.

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

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

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

Раздел 11.2.1 "Средства доступа к свойствам"

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

  1. Пусть baseReference будет результатом вычисления MemberExpression.
  2. Пусть baseValue будет GetValue(baseReference).
  3. Пусть propertyNameReference будет результатом вычисления Expression.
  4. Пусть propertyNameValue будет GetValue(propertyNameReference).
  5. Вызовите CheckObjectCoercible(baseValue).
  6. Пусть propertyNameString будет ToString(propertyNameValue).
  7. Если оцениваемая синтаксическая продукция содержится в коде режима строгого режима, то допустим, что строгий будет истинным, иначе пусть строгий будет ложным.
  8. Вернуть значение типа Reference, базовое значение которого равно baseValue, а ссылочное имя - propertyNameString, а флаг строгого режима - строгий.

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

По ссылке

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

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

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

Передача аргументов функции в 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;
}

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

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

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

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

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

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

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

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

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

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

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

Учить больше

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

Самое краткое объяснение, которое я нашел, было в руководстве по стилю 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

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

Цитирую спрашивающего:

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

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

Нет передачи по ссылке

Рассмотрим этот код, которому не важно, является ли он примитивом или объектом:

      function alter(arg) {  /* some magic happening here to `arg` */ }

function main() {
    var a, b;
    a = b = { x: 1 }; // Example value. It can be an object or primitive: irrelevant here
    console.log(a === b); // true
    alter(a);
    console.log(a === b); // false? (not possible)
}

Если функциябыл какой-то способ сделать этот второй вывод, то мы могли бы говорить о передаче по ссылке, как тогдабыл бы псевдонимом для. Но в JavaScript это просто невозможно. Понятие примитива здесь не имеет значения: в JavaScript нет передачи по ссылке.

Объекты

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

Назначение переменной и мутация объекта

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

  • Присвоение свойству objext изменяет объект, на который ссылаются как вызывающий, так и вызываемый объект.
  • Присвоение переменной параметра не приведет к изменению данных вызывающего объекта. В частности, если эта переменная параметра была объектом (ссылкой), эта ссылка перезаписывается, и поэтому функция отделяется от данных вызывающего объекта.
      function passByCopy ([...array], {...object})
{
   console .log ("copied objects", array, object)
}

passByCopy ([1,2,3], {a:1, b:2, c:3})

function passByReference (array, object)
{
   console .log ("same objects", array, object)
}

passByReference ([1,2,3], {a:1, b:2, c:3})

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

      function run()
{
    var test = [];
    test.push(1);

    console.log('before: '+test); // 1

    changeVariable(_.clone(test)); // (Note: I am using lodash _.clone() function)
 
    console.log('after: '+test); // 1 
}


function changeVariable(test2) {
  var test1 = test2;
  test1.push(2); 
  console.log('inside func:', test1);  // inside func: [1,2]
}   


run();    

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

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}
Другие вопросы по тегам