Javascript проходит по ссылке?

Javascript проходит по ссылкам или по значениям? Вот пример из Javascript: Хорошие части. Я очень смущен my параметр для функции прямоугольника. Это на самом деле undefinedи переопределен внутри функции. Там нет оригинальной ссылки. Если я удалю его из параметра функции, функция внутренней области не сможет получить к нему доступ.

Это закрытие? Но никакая функция не возвращается.

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};
var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};
myShape = shape({
    name: "Unhnown"
});
myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());

10 ответов

Решение

Примитивы передаются по значению, Объекты передаются по "копии ссылки".

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

function replace(ref) {
    ref = {};           // this code does _not_ affect the object passed
}

function update(ref) {
    ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
}

var a = { key: 'value' };
replace(a);  // a still has its original value - it's unmodfied
update(a);   // the _contents_ of 'a' are changed

Думайте об этом так:

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

var obj = { };

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

Мои 2 цента.... Не имеет значения, передает ли Javascript параметры по ссылке или по значению. Что действительно важно, так это назначение против мутации.

Я написал более подробное объяснение здесь ( Является ли JavaScript языком передачи по ссылке или языком передачи по значению?)

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

Поведение этого параметра внутри функции точно такое же, как и при поведении новой переменной с использованием знака равенства. Возьмите эти простые примеры.

var myString = 'Test string 1';

// Assignment - A link to the same place as myString
var sameString = myString;

// If I change sameString, it will not modify myString, 
// it just re-assigns it to a whole new string
sameString = 'New string';

console.log(myString); // logs 'Test string 1';
console.log(sameString); // logs 'New string';

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

function myFunc(sameString) {

    // Re assignment.. again, it will not modify myString
    sameString = 'New string';
}

var myString = 'Test string 1';

// This behaves the same as if we said sameString = myString
myFunc(myString);

console.log(myString); // Again, logs 'Test string 1';

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

var myObject = { name: 'Joe'; }

// Assignment - We simply link to the same object
var sameObject = myObject;

// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'

sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'

// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'

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

function myFunc(sameObject) {

    // We mutate the object, so the myObject gets the change too... just like before.
    sameObject.name = 'Jill';

    // But, if we re-assign it, the link is lost
    sameObject = { name: 'Howard' };
}

var myObject = { name: 'Joe'; }

// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'

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

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

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

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

Аргументы функции передаются либо по значению, либо по разделению, но никогда НИКОГДА по ссылке в Javascript!

Вызов по значению

Примитивные типы передаются по значению:

var num = 123, str = "foo";

function f(num, str) {
  num += 1;
  str += "bar";
  console.log("inside of f:", num, str);
}

f(num, str);
console.log("outside of f:", num, str);

Переназначения внутри области действия не видны в окружающей области.

Это также относится к String s, которые являются составным типом данных и все же неизменяемыми:

var str = "foo";

function f(str) {
  str[0] = "b"; // doesn't work, because strings are immutable
  console.log("inside of f:", str);
}

f(str);
console.log("outside of f:", str);

Call-по-Sharing

Объекты, то есть все типы, которые не являются примитивами, передаются путем совместного использования. Переменная, которая содержит ссылку на объект, на самом деле содержит просто копию этой ссылки. Если Javascript будет использовать стратегию оценки Call-by-reference, переменная будет содержать исходную ссылку. Это принципиальное различие между передачей и ссылкой.

Каковы практические последствия этого различия?

var o = {x: "foo"}, p = {y: 123};

function f(o, p) {
  o.x = "bar"; // mutation
  p = {x: 456}; // reassignment
  console.log("o inside of f:", o);
  console.log("p inside of f:", p);
}

f(o, p);

console.log("o outside of f:", o);
console.log("p outside of f:", p);

Мутирование означает изменение определенных свойств существующего Object, Эталонная копия, с которой связана переменная и которая ссылается на этот объект, остается неизменной. Таким образом, мутации видны в области видимости вызывающего.

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

Дополнительная информация о стратегии оценки в Ecmascript.

Как и в случае с C, в конечном итоге все передается по значению. В отличие от C, вы не можете на самом деле выполнить резервное копирование и передать местоположение переменной, потому что в ней нет только указателей на ссылки.

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

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

Пример с объектом:

var f1 = function(inputObject){
    inputObject.a=2;
}
var f2 = function(){
    var inputObject={"a":1};
    f1(inputObject); 
    console.log(inputObject.a);
}

вызов f2 приводит к выводу значения "a" как 2 вместо 1, так как ссылка передается и значение "a" в ссылке обновляется.

Пример с примитивом:

var f1 = function(a){
    a=2;
}
var f2 = function(){
    var a =1;
    f1(a); 
    console.log(a);
}

вызов f2 приводит к выводу значения "a" как 1.

С практической точки зрения, Альнитак корректен и облегчает понимание, но в конечном итоге в JavaScript все передается по значению.

Какова "ценность" объекта? Это ссылка на объект.

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

В интересах создания простого примера, который использует const...

const myRef = { foo: 'bar' };
const myVal = true;

function passes(r, v) {
  r.foo = 'baz';
  v = false;
}

passes(myRef, myVal);

console.log(myRef, myVal); // Object {foo: "baz"} true

"Глобальные" переменные javascript являются членами объекта окна. Вы можете получить доступ к ссылке в качестве члена объекта окна.

var v = "initialized";
function byref(ref) {
 window[ref] = "changed by ref";
}
byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// could also be called like... byref('v');
console.log(v); // outputs changed by ref

Обратите внимание, что приведенный выше пример не будет работать для переменных, объявленных внутри функции.

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

Тем не менее, я делаю немного по-другому:

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

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

var r;

funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);


function funcWithRefScalars(o) {  // o(amount, message)
  o.amount  *= 1.2;
  o.message = "20% increase";
}

В приведенном выше примере, null четко указывает выходной опорный параметр.

Выход:

240 - 20% Increase

На стороне клиента, console.log должен быть заменен alert,

★ ★ ★

Еще один метод, который может быть еще более читабельным:

var amount, message;

funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);

function funcWithRefScalars(amount, message) {  // o(amount, message)
   amount[0]  *= 1.2;
   message[0] = "20% increase";
}

Здесь вам даже не нужно создавать новые фиктивные имена, такие как r выше.

В примерах, где люди пытаются это продемонстрировать, я не вижу передачи по ссылке. Я вижу только передачу по значению.

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

в таком заявлении:

var a = {
  b:"foo", 
  c:"bar"
};

Значение 'a' - это не Объект, а (пока что) ссылка на него. Другими словами, объект не находится в переменной a, ссылка на него есть. Я думаю, что это то, что кажется трудным для программистов, которые в основном знакомы только с JavaScript. Но легко для людей, которые также знают, например, Java, C# и C.

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

a = { key: 'bevmo' };
testRetain(a);
document.write(' after function ');
document.write(a.key);


function testRetain (b)
 {
 document.write(' arg0 is ');
 document.write(arguments[0].key);
 b.key='passed by reference';
 var retain = b; //retaining the original address of the parameter

 //address of left set to address of right, changes address of parameter
 b={key: 'vons'}; //right is a new object with a new address
 document.write(' arg0 is ');
 document.write(arguments[0].key);

 //now retrieve the original address of the parameter for pass by reference
 b=retain; 
 document.write(' arg0 is ');
 document.write(arguments[0].key);
}

Результат: arg0 это bevmo arg0 это vons arg0 передается по ссылке после функции, переданной по ссылке

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

function test(value) {
  console.log('retrieve value');
  console.log(value());
}

// call the function like this
var value = 1;
test(() => value);
Другие вопросы по тегам