Передать переменные по ссылке в Javascript - PullRequest
256 голосов
/ 12 октября 2011

Как передать переменные по ссылке в JS?У меня есть 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

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

Ответы [ 13 ]

380 голосов
/ 12 октября 2011

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

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

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

alterObject(myObj);

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

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

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

 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 ++, это можно сделать, потому что у этого языка есть (своего рода) переход по ссылке.

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

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

90 голосов
/ 28 октября 2014
  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
24 голосов
/ 22 мая 2013

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

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

inc('a');

alert(a); // 2


EDIT

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

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

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

    return init;
})();

inc();
11 голосов
/ 31 января 2014

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

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 голосов
/ 16 января 2017

Еще один подход к передаче любых (локальных, примитивных) переменных по ссылке заключается в переносе переменной с закрытием "на лету" на 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/

2 голосов
/ 15 июля 2017

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

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"]
2 голосов
/ 14 мая 2014

Я играл с синтаксисом, чтобы делать подобные вещи, но для этого нужны некоторые немного необычные помощники. Он начинается с того, что вообще не использует '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);
});
1 голос
/ 22 апреля 2018

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

ИМХО, функция должна возвращать только один результат / значение, используя ключевое слово 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);

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

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

1 голос
/ 05 мая 2015

на самом деле это действительно легко,

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

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

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

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()

0 голосов
/ 14 июля 2019

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

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