在 JavaScript 中通过引用传递变量

IT技术 javascript variables pass-by-reference
2021-02-04 04:33:12

如何在 JavaScript 中通过引用传递变量?

我有三个变量要对其执行多个操作,因此我想将它们放入 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 variables

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

6个回答

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 年 3 月)在 Reddit 上再次通过一篇类似于下面提到的我的博客文章在 Reddit 上大放异彩,尽管在这种情况下是关于 Java。我在阅读 Reddit 评论中的来回评论时突然想到,很大一部分混乱源于涉及“引用”一词的不幸冲突。术语“通过引用传递”和“通过值传递”早于在编程语言中使用“对象”的概念。它真的完全不是关于对象的。它是关于函数参数,特别是函数参数如何“连接”(或不连接)到调用环境。特别是,,它看起来与 JavaScript 中的非常相似。但是,您可以在调用环境中修改对象引用,这是您在 JavaScript 中无法做到的关键传递引用语言不会传递引用本身,而是传递对引用的引用

编辑-这是关于该主题的博客文章。(注意那篇文章的评论,它解释了 C++ 并没有真正通过引用传递。这是真的。然而,C++ 确实具有创建对普通变量的引用的能力,无论是在函数点显式调用以创建指针,或在调用其参数类型签名要求完成的函数时隐式调用。这些是 JavaScript 不支持的关键。)

您可以传递对对象或数组的引用,这允许您更改原始对象或数组,我认为这是 OP 实际询问的内容。
2021-03-20 04:33:12
你的博主对 C++ 的看法是错误的,它按引用传递的,他的帖子毫无意义。初始化后不能更改引用的值无关紧要;这与“通过引用传递”无关。他关于“通过引用传递”语义可以通过 C# 追溯到的声明应该敲响了警钟。
2021-03-24 04:33:12
@Pointy 这是一个可怕的答案。如果我需要帮助,我最不想要的就是有人教我语义学。“引用传递”只是意味着“该函数在某种程度上可以更改作为参数传入的变量的值。” (在问题的上下文中)它真的没有你想象的那么复杂。
2021-03-26 04:33:12
按值传递引用按引用传递不同,尽管在某些情况下可能会出现这种情况,例如这种情况。
2021-04-02 04:33:12
好吧,OP 使用了该术语,但实际代码似乎根本不涉及任何“传递”:-) 我不太确定他想做什么。
2021-04-03 04:33:12
  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 { prop: "laptop" }
console.log(object2); // output: Object { prop: "bike" }
console.log(number1); // ouput: 10

这不是“通过引用传递”的意思。该术语实际上与对象无关,而是与被调用函数中的参数与调用环境中的变量之间的关系。
2021-03-19 04:33:12
一切总是按值传递。对于非原语,传递的值是一个引用。分配给引用会更改该引用——很自然,因为它是一个值——因此无法更改最初引用的其他对象。通过改变所指向的对象,改变引用所指向的对象的行为与您预期的完全一样。这两种情况完全一致,并且都不会通过回到过去并改变它们传递到函数的方式来神奇地改变 JavaScript 的工作方式。
2021-03-25 04:33:12
这个答案澄清了前一个,尽管它有更多的选票(在撰写本文时为 287,它也是被接受的选票),但对于如何在 JS 中实际通过引用传递它还不够清楚。简而言之,此答案中的代码有效,而获得更多选票的答案却无效。
2021-03-31 04:33:12

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

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();
我喜欢最后一个解决方案(编辑版本),即使它没有通过引用传递变量。这是优势,因为您可以直接访问变量。
2021-03-13 04:33:12
@dkloke:是的,最终需要触摸窗口对象-就像jQuery使用window.$/window.jQuery,其他方法都在此之下。我说的是全局命名空间的污染,在那里你向它添加了很多变量,而不是将它们放在一个统一的命名空间下。
2021-03-16 04:33:12
@Phil 最好注意全局/窗口值,但在某些时候,我们在浏览器中所做的一切都是 window 对象的子项或后代。在 nodejs 中,一切都是 GLOBAL 的后代。在编译的对象语言中,如果不是显式的,那也是一个隐式的父对象,因为否则这样做会使堆管理更加复杂(为了什么?)。
2021-04-07 04:33:12
我找不到好词来表达这种方法有多糟糕;(
2021-04-08 04:33:12

简单对象

function foo(x) {
  // Function with other context
  // Modify `x` property, increasing the value
  x.value++;
}

// Initialize `ref` as object
var ref = {
  // The `value` is inside `ref` variable object
  // The initial value is `1`
  value: 1
};

// Call function with object value
foo(ref);
// Call function with object value again
foo(ref);

console.log(ref.value); // Prints "3"


自定义对象

目的 rvar

/**
 * Aux function to create by-references variables
 */
function rvar(name, value, context) {
  // If `this` is a `rvar` instance
  if (this instanceof rvar) {
    // Inside `rvar` context...

    // Internal object value
    this.value = value;

    // Object `name` property
    Object.defineProperty(this, 'name', { value: name });

    // Object `hasValue` property
    Object.defineProperty(this, 'hasValue', {
      get: function () {
        // If the internal object value is not `undefined`
        return this.value !== undefined;
      }
    });

    // Copy value constructor for type-check
    if ((value !== undefined) && (value !== null)) {
      this.constructor = value.constructor;
    }

    // To String method
    this.toString = function () {
      // Convert the internal value to string
      return this.value + '';
    };
  } else {
    // Outside `rvar` context...

    // Initialice `rvar` object
    if (!rvar.refs) {
      rvar.refs = {};
    }

    // Initialize context if it is not defined
    if (!context) {
      context = this;
    }

    // Store variable
    rvar.refs[name] = new rvar(name, value, context);

    // Define variable at context
    Object.defineProperty(context, name, {
      // Getter
      get: function () { return rvar.refs[name]; },
      // Setter
      set: function (v) { rvar.refs[name].value = v; },
      // Can be overrided?
      configurable: true
    });

    // Return object reference
    return context[name];
  }
}

// Variable Declaration

// Declare `test_ref` variable
rvar('test_ref_1');

// Assign value `5`
test_ref_1 = 5;
// Or
test_ref_1.value = 5;

// Or declare and initialize with `5`:
rvar('test_ref_2', 5);

// ------------------------------
// Test Code

// Test Function
function Fn1(v) { v.value = 100; }

// Test
function test(fn) { console.log(fn.toString()); console.info(fn()); }

// Declare
rvar('test_ref_number');

// First assign
test_ref_number = 5;
test(() => test_ref_number.value === 5);

// Call function with reference
Fn1(test_ref_number);
test(() => test_ref_number.value === 100);

// Increase value
test_ref_number++;
test(() => test_ref_number.value === 101);

// Update value
test_ref_number = test_ref_number - 10;
test(() => test_ref_number.value === 91);

感谢您的修复。现在它起作用了,但我对投票给你的答案感到谨慎,因为我咨询过的专业JavaScript程序员认为你的解决方案令人厌恶我自己意识到这是相当危险的,因为使变量不是它们看起来的样子。尽管如此,您的rvar解决方案无疑具有教育value。
2021-03-21 04:33:12
@Ant_222 代码已修复。
2021-03-24 04:33:12
您的示例似乎做出了一些您没有说明的假设。当我在在线JavaScript解释器中运行它时,出现错误:/workspace/Main.js:43 context = window; ReferenceError: window is not defined
2021-03-31 04:33:12

另一种通过引用传递任何(本地、原始)变量的方法是通过eval. 这也适用于“使用严格”。(注意:注意这eval对 JavaScript 优化器不友好,而且变量名周围缺少引号可能会导致不可预测的结果)

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

好主意,但我决定反对它,因为传递箭头函数的仅输出变量x=>value=x实际上更短,而双向输入-输出参数并不经常需要。请参阅我对另一种基于方法的重复问题的回答,在该eval方法中您传递变量名称并评估整个函数的结果。
2021-04-01 04:33:12