Передать переменные по ссылке в Javascript

Как передать переменные по ссылке в JavaScript? У меня есть 3 переменные, с которыми я хочу выполнить несколько операций, поэтому я хочу поместить их в цикл for и выполнять операции с каждой из них.

псевдокод:

myArray = new Array(var1, var2, var3);
for (var x = 0; x < myArray.length; x++){
    //do stuff to the array
    makePretty(myArray[x]);
}
//now do stuff to the updated vars

Каков наилучший способ сделать это?

Ответы

Ответ 1

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

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

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

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"

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

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}

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

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

 var x = 1, y = 2;
 swap(x, y);

 alert("x is " + x + ", y is " + y); // "x is 1, y is 2"

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

изменить &— это недавно (март 2015) снова взорвалось на Reddit из-за сообщения в блоге, похожего на мое, упомянутое ниже, хотя в данном случае о Java. При чтении взад-вперед в комментариях Reddit мне пришло в голову, что большая часть путаницы проистекает из неудачного столкновения, связанного со словом "ссылка". Термины "передача по ссылке" и "передача по значению" предшествуют концепции наличия "объектов" для работы в языках программирования. Это действительно не об объектах вообще; это о параметрах функции и, в частности, о том, как параметры функции "связаны" (или нет) с вызывающей средой. В частности, обратите внимание, что на истинном языке передачи по ссылке &— тот, который включает объекты &— Можно по-прежнему иметь возможность изменять содержимое объекта, и это будет выглядеть почти так же, как в JavaScript. Однако можно было бы также изменить ссылку на объект в вызывающей среде, и это главное, что вы не можете сделать в JavaScript. Язык передачи по ссылке будет передавать не саму ссылку, а ссылку на ссылку.

изменить &— здесь есть запись в блоге на эту тему. (Обратите внимание на комментарий к этому сообщению, в котором объясняется, что C++ на самом деле не имеет передачи по ссылке. Это правда. Что, однако, C++ имеет, является способностью создавать ссылки на простые переменные, либо явно в точке вызова функции для создания указателя, либо неявно при вызове функций, чья сигнатура типа аргумента требует этого сделать. Это ключевые вещи, которые JavaScript не поддерживает. )

Ответ 2

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

    • если вы устанавливаете значение объекта или массива, это Pass by Value.

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

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

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

Код

function passVar(obj1, obj2, num) {
    obj1.prop = "laptop"; // will CHANGE original
    obj2 = { prop: "computer" }; //will NOT affect original
    num = num + 1; // will NOT affect original
}

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

passVar(object1, object2, number1);
console.log(object1); //output: Object {item:"laptop"}
console.log(object2); //output: Object {item:"bike"}
console.log(number1); //ouput: 10

Ответ 3

Обходной путь для передачи переменной, например, по ссылке:

var a = 1;
inc = function(variableName) {
  window[variableName] += 1;
};

inc('a');

alert(a); // 2


ИЗМЕНИТЬ

yup, на самом деле вы можете сделать это без глобального доступа

inc = (function () {
    var variableName = 0;

    var init = function () {
        variableName += 1;
        alert(variableName);
    }

    return init;
})();

inc();

Ответ 4

Простой объект

var ref = { value: 1 };

function Foo(x) {
    x.value++;
}

Foo(ref);
Foo(ref);

alert(ref.value); // Alert: 3

Пользовательский объект

Объект rvar

function rvar (name, value, context) {
    if (this instanceof rvar) {
        this.value = value;
        Object.defineProperty(this, 'name', { value: name });
        Object.defineProperty(this, 'hasValue', { get: function () { return this.value !== undefined; } });
        if ((value !== undefined) && (value !== null))
            this.constructor = value.constructor;
        this.toString = function () { return this.value + ''; };
    } else {
        if (!rvar.refs)
            rvar.refs = {};
        if (!context)
            context = window;
        // Private
        rvar.refs[name] = new rvar(name, value);
        // Public
        Object.defineProperty(context, name, {
            get: function () { return rvar.refs[name]; },
            set: function (v) { rvar.refs[name].value = v; },
            configurable: true
        });

        return context[name];
    }
}

Объявление переменной

rvar('test_ref');
test_ref = 5; // test_ref.value = 5

Или:

rvar('test_ref', 5); // test_ref.value = 5

Тестовый код

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
console.log("rvar('test_ref_number');");
console.log("test_ref_number = 5;");
console.log("function Fn1 (v) { v.value = 100; }");
console.log('test_ref_number.value === 5', test_ref_number.value === 5);
console.log(" ");

Fn1(test_ref_number);
console.log("Fn1(test_ref_number);");
console.log('test_ref_number.value === 100', test_ref_number.value === 100);
console.log(" ");

test_ref_number++;
console.log("test_ref_number++;");
console.log('test_ref_number.value === 101', test_ref_number.value === 101);
console.log(" ");

test_ref_number = test_ref_number - 10;
console.log("test_ref_number = test_ref_number - 10;");
console.log('test_ref_number.value === 91', test_ref_number.value === 91);

console.log(" ");
console.log("---------");
console.log(" ");

rvar('test_ref_str', 'a');
console.log("rvar('test_ref_str', 'a');");
console.log('test_ref_str.value === "a"', test_ref_str.value === 'a');
console.log(" ");

test_ref_str += 'bc';
console.log("test_ref_str += 'bc';");
console.log('test_ref_str.value === "abc"', test_ref_str.value === 'abc');

Результат тестовой консоли

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
test_ref_number.value === 5 true

Fn1(test_ref_number);
test_ref_number.value === 100 true

test_ref_number++;
test_ref_number.value === 101 true

test_ref_number = test_ref_number - 10;
test_ref_number.value === 91 true

---------

rvar('test_ref_str', 'a');
test_ref_str.value === "a" true

test_ref_str += 'bc';
test_ref_str.value === "abc" true 

Ответ 5

Еще один подход к передаче любых (локальных, примитивных) переменных по ссылке заключается в переносе переменной с замыканием "на лету" в eval. Это также работает с "использовать строгий". (Примечание: имейте в eval что eval не подходит для оптимизаторов JS, также пропущенные кавычки вокруг имени переменной могут привести к непредсказуемым результатам)

"use strict"

//return text that will reference variable by name (by capturing that variable to closure)
function byRef(varName){
    return "({get value(){return "+varName+";}, set value(v){"+varName+"=v;}})";
}

//demo

//assign argument by reference
function modifyArgument(argRef, multiplier){
    argRef.value = argRef.value * multiplier;
}

(function(){

var x = 10;

alert("x before: " + x);
modifyArgument(eval(byRef("x")), 42);
alert("x after: " + x);

})()

Живой пример https://jsfiddle.net/t3k4403w/

Ответ 6

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

function Wrapper(val){
    this.VAL = val;
}
Wrapper.prototype.toString = function(){
    return this.VAL.toString();
}

function DECLARE(val, callback){
    var valWrapped = new Wrapper(val);    
    callback(valWrapped);
}

function INC(ref){
    if(ref && ref.hasOwnProperty('VAL')){
        ref.VAL++; 
    }
    else{
        ref++;//or maybe throw here instead?
    }

    return ref;
}

DECLARE(5, function(five){ //consider this line the same as 'let five = 5'
console.log("five is now " + five);
INC(five); // increment
console.log("five is incremented to " + five);
});

Ответ 7

Там на самом деле довольно хорошее решение:

function updateArray(context, targetName, callback) {
    context[targetName] = context[targetName].map(callback);
}

var myArray = ['a', 'b', 'c'];
updateArray(this, 'myArray', item => {return '_' + item});

console.log(myArray); //(3) ["_a", "_b", "_c"]

Ответ 8

на самом деле это очень просто,

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

решения - передавать аргументы с использованием объектно-ориентированного дизайна JavaScript,

это то же самое, что и перенос аргументов в глобальную переменную, но лучше...

function action(){
  /* process this.arg, modification allowed */
}

action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();

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

function action(){
  /* process this.arg, modification allowed */
  this.arg = ["a","b"];
}

action.setArg = function(){this.arg = arguments; return this;}

action.setArg(["empty-array"],"some string",0x100,"last argument")()

или еще лучше.. action.setArg(["empty-array"],"some string",0x100,"last argument").call()

Ответ 9

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

ИМХО, функция должна возвращать только один результат/значение, используя ключевое слово return. Вместо изменения параметра/аргумента я бы просто вернул измененное значение параметра/аргумента и оставил любые желаемые переназначения до вызывающего кода.

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

Пример:

Предположим, что передаваемые параметры будут поддерживаться с помощью специального ключевого слова, например, "ref" в списке аргументов. Мой код может выглядеть примерно так:

//The Function
function doSomething(ref value) {
    value = "Bar";
}

//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar

Вместо этого я бы предпочел сделать что-то вроде этого:

//The Function
function doSomething(value) {
    value = "Bar";
    return value;
}

//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar

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

//The Function
function doSomething(ref value) {
    value = "Bar";

    //Do other work
    var otherValue = "Something else";

    return otherValue;
}

//The Calling Code
var value = "Foo";
var otherValue = doSomething(value);
console.log(value); //Bar
console.log(otherValue); //Something else

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

//The Function
function doSomething(value) {
    value = "Bar";

    //Do more work
    var otherValue = "Something else";

    return {
        value: value,
        otherValue: otherValue
    };
}

//The Calling Code:
var value = "Foo";
var result = doSomething(value);
value = result.value; //Reassignment
console.log(value); //Bar
console.log(result.otherValue);

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

Удачного кодирования. :)

Ответ 10

Я точно знаю, что вы имеете в виду. То же самое в Swift не будет проблемой. Суть в том, чтобы использовать let не var.

Тот факт, что примитивы передаются по значению, но тот факт, что значение var i в точке итерации не копируется в анонимную функцию, довольно удивительно, если не сказать больше.

for (let i = 0; i < boxArray.length; i++) {
  boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}

Ответ 11

Javascript может изменять элементы массива внутри функции (он передается как ссылка на объект/массив).

function makeAllPretty(items) {
   for (var x = 0; x < myArray.length; x++){
      //do stuff to the array
      items[x] = makePretty(items[x]);
   }
}

myArray = new Array(var1, var2, var3);
makeAllPretty(myArray);

Вот еще один пример:

function inc(items) {
  for (let i=0; i < items.length; i++) {
    items[i]++;
  }
}

let values = [1,2,3];
inc(values);
console.log(values);
// prints [2,3,4]

Ответ 12

JS не является сильным типом, он позволяет решать проблемы разными способами, как показано в этом шаге.

Однако, с точки зрения ремонтопригодности, я бы согласился с Бартом Хофландом. Функция должна получить аргументы, чтобы что-то сделать и вернуть результат. Делая их легко многоразовыми.

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

Ответ 13

Если оставить в стороне обсуждение по ссылке, те, кто все еще ищет решение поставленного вопроса, могут использовать:

const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));