在Javascript中通过引用传递变量

时间:2011-10-12 18:18:09

标签: javascript variables pass-by-reference

如何在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

这样做的最佳方式是什么?

17 个答案:

答案 0 :(得分:372)

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 ++这样的语言中,可以这样做,因为语言 (sort-of)具有传递引用。

编辑 - 最近(2015年3月)再次通过类似我下面提到的博客帖子在Reddit上爆炸,尽管在这种情况下是关于Java的。我在Reddit的评论中反复阅读时发现,混淆的很大一部分源于涉及“参考”一词的不幸碰撞。术语“通过引用传递”和“通过值传递”早于在编程语言中使用“对象”的概念。它根本不是关于物体的;它是关于函数参数,特别是函数参数如何“连接”(或不连接)到调用环境。特别要注意的是,在一个真正的传递引用语言 - 涉及对象的语言 - 一个人仍然能够修改对象内容,它会看起来与JavaScript中的完全一样。但是,能够在调用环境中修改对象引用,这是无法在JavaScript中执行的关键操作。传递引用语言不会传递引用本身,而是传递引用

编辑 - here is a blog post on the topic.(注意那篇文章的评论,解释说C ++实际上并没有传递引用。这是事实。但是,C ++有什么作用,能够创建对普通变量的引用,或者在函数调用时显式创建指针,或者在调用参数类型签名调用的函数时隐式 。这些是关键JavaScript不支持的东西。)

答案 1 :(得分:90)

  1. 字符串和数字等基本类型变量总是按值传递。
  2. 数组和对象通过引用或基于以下条件的值传递:

    • 如果要设置对象或数组的值,则为“按值传递”。

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

    • 如果要更改对象或数组的属性值,则它是Pass by Reference。

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

  3. <强>代码

    &#13;
    &#13;
    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
    &#13;
    &#13;
    &#13;

答案 2 :(得分:24)

通过引用传递变量的解决方法:

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

inc('a');

alert(a); // 2


修改

是的,实际上你可以在没有访问全局

的情况下做到这一点
inc = (function () {
    var variableName = 0;

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

    return init;
})();

inc();

答案 3 :(得分:11)

简单对象

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 

答案 4 :(得分:5)

通过引用传递任何(本地,原始)变量的另一种方法是通过使用闭包来包装变量&#34;在运行中#34;按eval。这也适用于&#34;使用严格&#34;。 (注意:请注意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/

答案 5 :(得分:2)

我一直在玩语法来做这类事情,但它需要一些有点不寻常的助手。它首先不使用'var',而是一个简单的'DECLARE'帮助器,它创建一个局部变量并通过匿名回调为它定义一个范围。通过控制变量的声明方式,我们可以选择将它们包装到对象中,以便它们总是可以通过引用传递。这类似于上面的Eduardo Cuomo的回答之一,但下面的解决方案不需要使用字符串作为变量标识符。这里有一些显示概念的最小代码。

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

答案 6 :(得分:2)

实际上有一个很好的解决方案:

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

答案 7 :(得分:1)

我个人不喜欢&#34;传递参考&#34;各种编程语言提供的功能。也许那是因为我只是发现了函数式编程的概念,但是当我看到导致副作用的函数时(例如通过引用传递参数),我总是得到鸡皮疙瘩。我个人非常强烈地接受单一责任&#34;原理

恕我直言,函数应该使用return关键字返回一个结果/值。我只返回修改后的参数/参数值,而不是修改参数/参数,并将任何所需的重新分配留给调用代码。

但有时(希望很少),有必要从同一个函数返回两个或多个结果值。在这种情况下,我会选择将所有结果值包含在单个结构或对象中。同样,处理任何重新分配应该取决于调用代码。

示例:

假设使用特殊关键字支持传递参数,例如&#39; ref&#39;在参数列表中。我的代码可能看起来像这样:

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

这些代码示例非常简单,但它大致演示了我个人如何处理这些内容。它帮助我将各种责任保持在正确的位置。

快乐的编码。 :)

答案 8 :(得分:1)

实际上它很容易,

问题是理解一旦传递经典参数,你就会被限制在另一个只读区域。

解决方案是使用JavaScript的面向对象设计传递参数

它与将args放在全局/范围变量中相同,但更好......

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 :(得分:0)

我确切地知道你的意思。 Swift中的同样事情也没问题。 底线是使用let而不是var

基元通过值传递但事实上迭代点的var i的值没有复制到匿名函数这一事实至少可以说令人惊讶。

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

答案 10 :(得分:0)

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]

答案 11 :(得分:0)

JS不是强类型,它可以让您以许多不同的方式解决问题,如本教程所述。

但是,从可维护性的角度来看,我必须同意Bart Hofland的观点。函数应该让args做一些事情并返回结果。使它们易于重用。

如果您认为需要通过引用传递变量,则最好将其构建为对象恕我直言。

答案 12 :(得分:0)

撇开通过引用的讨论,仍在寻找所述问题的解决方案的人可以使用:

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

答案 13 :(得分:0)

我喜欢解决JavaScript缺少通过引用的问题,如本示例所示。

其实质是您不要尝试通过引用创建 。相反,您可以使用返回功能,并使其能够返回多个值。因此,无需在数组或对象中插入值。

var x = "First";
var y = "Second";
var z = "Third";

log('Before call:',x,y,z);
with (myFunc(x, y, z)) {x = a; y = b; z = c;} // <-- Way to call it
log('After call :',x,y,z);


function myFunc(a, b, c) {
  a = "Changed first parameter";
  b = "Changed second parameter";
  c = "Changed third parameter";
  return {a:a, b:b, c:c}; // <-- Return multiple values
}

function log(txt,p1,p2,p3) {
  document.getElementById('msg').innerHTML += txt + '<br>' + p1 + '<br>' + p2 + '<br>' + p3 + '<br><br>'
}
<div id='msg'></div>

答案 14 :(得分:0)

如果要通过引用传递变量,一种更好的方法是在对象中传递参数,然后使用window开始更改值:

window["varName"] = value;

示例:

// Variables with first values
var x = 1, b = 0, f = 15;


function asByReference (
    argumentHasVars = {},   // Passing variables in object
    newValues = [])         // Pass new values in array
{
    let VarsNames = [];

    // Getting variables names one by one
    for(let name in argumentHasVars)
        VarsNames.push(name);

    // Accessing variables by using window one by one
    for(let i = 0; i < VarsNames.length; i += 1)
        window[VarsNames[i]] = newValues[i]; // Set new value
}

console.log(x, b, f); // Output with first values

asByReference({x, b, f}, [5, 5, 5]); // Passing as by reference

console.log(x, b, f); // Output after changing values

答案 15 :(得分:0)

此处使用 Destructuring 是一个示例,其中我有 3 个变量,并且对每个变量执行多项操作:

  • 如果值小于 0,则更改为 0,
  • 如果大于 255,则更改为 1,
  • 否则将数字减少 255 以将范围从 0-255 转换为范围 0-1。
let a = 52.4, b = -25.1, c = 534.5;
[a, b, c] = [a, b, c].map(n => n < 0 ? 0 : n > 255 ? 1 : n / 255);
console.log(a, b, c); // 0.20549019607843136 0 1

答案 16 :(得分:0)

由于我们没有 javascript 通过引用传递功能,所以唯一的方法是让函数返回值并让调用者分配它:

所以“makePretty(myArray[x]);”应该是“myArray[x] = makePretty(myArray[x]);”

(这是为了防止你需要在函数内部赋值,如果只需要变异,那么传递对象并变异就足够了)