Передача переменных по ссылке в Javascript


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

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

10 228

10 ответов:

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

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

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

alterObject(myObj);

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

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

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

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

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

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

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

      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

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

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

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

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 

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

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

там на самом деле довольно sollution:

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"]

на самом деле это очень легко,

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

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

это то же самое, что поместить args в глобальную переменную/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()

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

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

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

удачи в кодировании. :)

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

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

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