在Javascript中通过引用传递变量

BFT*_*ick 269 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
Run Code Online (Sandbox Code Playgroud)

做这个的最好方式是什么?

Poi*_*nty 392

JavaScript中没有"通过引用传递".您可以传递一个对象(也就是说,您可以按值传递对对象的引用),然后让函数修改对象内容:

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

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

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"
Run Code Online (Sandbox Code Playgroud)

现在,就你而言,就我所知,你还没有传递任何东西.您可以使用数字索引迭代数组的属性,并根据需要修改数组的每个单元格.

值得注意的是,"传递参考"是一个非常具体的术语.它并不仅仅意味着可以将引用传递给可修改的对象.相反,它意味着可以以允许函数在调用上下文中修改该值的方式传递一个简单变量.所以:

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}
Run Code Online (Sandbox Code Playgroud)

在像C++语言,有可能这样做,因为这种语言(排序的)有传址引用.

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

编辑 - 这是关于该主题的博客文章.(注意那篇文章的评论,解释说C++实际上没有传递引用.这是事实.但是,C++所具有的是能够在函数点明确地创建对纯变量的引用.调用创建指针,或者在调用参数类型为trace的函数时隐式调用.这些是JavaScript不支持的关键内容.)

  • 您可以传递对象或数组的引用,这些对象或数组允许您更改我认为是OP实际询问的原始对象或数组. (6认同)
  • 我是唯一一个认为“通过引用传递”的最佳答案是由一个叫 Pointy 的人回答的太巧合的人吗?:) (5认同)
  • 通过值*传递引用*与通过引用*传递*不同,尽管在某些情况下可能会出现这种情况. (4认同)
  • 你的博主是关于C++的错误,*做*传递引用,而他的帖子没有任何意义.初始化后无法更改引用的值是无关紧要的; 这与'通过引用传递'无关.他的声明"The"通过引用传递"语义可以追溯到C#"应该响起警钟. (4认同)
  • @Pointy这是一个可怕的答案.如果我需要帮助,我最不想要的就是有人用语义教我."传递引用"只是意味着,"函数在某种程度上可以改变作为参数传递的变量的值." (在问题的背景下)它真的没有你想象的那么复杂. (4认同)
  • @crownlessking 如果你花一些时间阅读这里的问题,你可能会发现很多人根本不清楚语义。这个答案已经被点赞了 271 次,所以它不可能“那么”糟糕。 (3认同)
  • 好吧,OP 使用了术语,但实际代码似乎根本不涉及任何“传递”:-) 我不太确定他想做什么。 (2认同)
  • 我认为这里有很多观点,但想补充一下。通过引用传递的思想并不是一个被对象和类的存在所取代或修改的概念。它是因为它而存在的东西。在通过引用传递之前,您必须清理自己的内存,并且指针管理成为编程的基础。托管语言(具有 GC 和“安全”和“不安全”内存使用的语言)使用“通过引用”的概念来传达托管内存空间中指针的概念。 (2认同)
  • @jfriend00“通过引用传递”通常用于指代对象的传递方式,但并不完全正确。在 JS 中试试这个:`(object)=&gt;{object.x=5; 对象={}; object.x=6;}`。`object.x` 在外部作用域中将是 5。 (2认同)

Muk*_*mar 96

  1. 原始类型变量(如字符串和数字)始终按值传递.
  2. 数组和对象通过引用或基于以下条件的值传递:

    • 如果要设置对象或数组的值,则为"按值传递".

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

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

      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
Run Code Online (Sandbox Code Playgroud)

  • 这不是"通过引用传递"的含义.该术语实际上与对象无关,而是与被调用函数中的参数和调用环境中的变量之间的关系有关. (18认同)
  • _Everything_始终按值传递.对于非基元,传递的值是引用.分配给引用会更改该引用 - 当然,因为它是一个值 - 因此无法更改最初引用的其他对象.通过改变指向的对象,对引用所指向的对象进行变换就像您期望的那样.这两种情况都是完全一致的,并且它们都不会通过回溯并改变它们传递给函数的方式来神奇地改变JavaScript的工作方式. (11认同)
  • 这个答案澄清了前一个答案,即使它有更多的选票(在撰写本文时为287,也是已被接受的选票),但对于如何通过JS中的引用实际传递它还不够明确.简而言之,**这个答案中的代码有效,收集更多选票的答案没有.** (5认同)
  • 很好的答案。避免在语义、术语和“OP 的含义”上来回吹毛求疵(现在出现在一些 SO 答案中,以便使它们完整并帮助寻找赞成票等),并给出了非常清楚的例子什么有效,什么无效。在这种情况下,为什么问题以及一种或另一种方式之间的语义差异不太重要,因为由于 Javascript 的编写方式,您一开始就无法以其他方式处理它。 (3认同)
  • 这些答案确实说明了可以做什么和不能做什么来满足OP的需求。我认为我们都可以同意这个术语很糟糕,而不是争论“通过引用传递”的含义,并希望在不久的将来创造一个更好的术语。虽然 @Pointy 的回答让我了解了更多概念,但它帮助我完成了工作 (2认同)

小智 24

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

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

inc('a');

alert(a); // 2
Run Code Online (Sandbox Code Playgroud)


编辑

是的,实际上你可以在没有访问全局的情况下做到

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

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

    return init;
})();

inc();
Run Code Online (Sandbox Code Playgroud)

  • 我找不到好的词来表达这种方法有多糟糕;( (2认同)

Edu*_*omo 11

简单对象

var ref = { value: 1 };

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

Foo(ref);
Foo(ref);

alert(ref.value); // Alert: 3
Run Code Online (Sandbox Code Playgroud)

自定义对象

宾语 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

变量声明

rvar('test_ref');
test_ref = 5; // test_ref.value = 5
Run Code Online (Sandbox Code Playgroud)

要么:

rvar('test_ref', 5); // test_ref.value = 5
Run Code Online (Sandbox Code Playgroud)

测试代码

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');
Run Code Online (Sandbox Code Playgroud)

测试控制台结果

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 
Run Code Online (Sandbox Code Playgroud)


Pav*_*Mur 5

另一种通过引用传递任何(本地,原始)变量的方法是通过包装带有"动态"闭包的变量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);

})()
Run Code Online (Sandbox Code Playgroud)

实时样本https://jsfiddle.net/t3k4403w/


Bar*_*and 5

我个人不喜欢各种编程语言提供的“按引用传递”功能。也许这是因为我刚刚发现函数式编程的概念,但当我看到导致副作用的函数(例如操作通过引用传递的参数)时,我总是会起鸡皮疙瘩。我个人强烈拥护“单一责任”原则。

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

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

例子:

假设通过在参数列表中使用特殊关键字(如“ref”)来支持传递参数。我的代码可能看起来像这样:

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

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

相反,我实际上更愿意做这样的事情:

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

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

当我需要编写一个返回多个值的函数时,我也不会使用通过引用传递的参数。所以我会避免这样的代码:

//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
Run Code Online (Sandbox Code Playgroud)

相反,我实际上更愿意在对象内返回两个新值,如下所示:

//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);
Run Code Online (Sandbox Code Playgroud)

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

快乐编码。:)