简体   繁体   English

在 JavaScript 中通过引用传递变量

[英]Pass variables by reference in JavaScript

How do I pass variables by reference in JavaScript?如何在 JavaScript 中通过引用传递变量?

I have three variables that I want to perform several operations to, so I want to put them in a for loop and perform the operations to each one.我有三个变量,我想对其执行多个操作,所以我想将它们放在一个 for 循环中并对每个变量执行操作。

Pseudocode:伪代码:

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

What is the best way to do this?做这个的最好方式是什么?

There is no "pass by reference" available in JavaScript. JavaScript 中没有可用的“通过引用传递”。 You can pass an object (which is to say, you can pass-by-value a reference to an object) and then have a function modify the object contents:您可以传递一个对象(也就是说,您可以按值传递对对象的引用),然后让函数修改对象内容:

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

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

alterObject(myObj);

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

You can iterate over the properties of an array with a numeric index and modify each cell of the array, if you want.如果需要,您可以使用数字索引迭代数组的属性并修改数组的每个单元格。

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}

It's important to note that "pass-by-reference" is a very specific term.重要的是要注意“传递引用”是一个非常具体的术语。 It does not mean simply that it's possible to pass a reference to a modifiable object.这不仅仅意味着可以传递对可修改对象的引用。 Instead, it means that it's possible to pass a simple variable in such a way as to allow a function to modify that value in the calling context.相反,这意味着可以以允许函数在调用上下文中修改该值的方式传递简单变量。 So:所以:

 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"

In a language like C++, it's possible to do that because that language does (sort-of) have pass-by-reference.在像 C++ 这样的语言中,可以这样做,因为该语言确实(排序)具有按引用传递。

edit — this recently (March 2015) blew up on Reddit again over a blog post similar to mine mentioned below, though in this case about Java.编辑- 最近(2015 年 3 月)在 Reddit 上再次通过一篇类似于下面提到的我的博客文章炸毁了它,尽管在这种情况下是关于 Java。 It occurred to me while reading the back-and-forth in the Reddit comments that a big part of the confusion stems from the unfortunate collision involving the word "reference".我在阅读 Reddit 评论中的来回评论时突然想到,很大一部分混乱源于涉及“引用”一词的不幸冲突。 The terminology "pass by reference" and "pass by value" predates the concept of having "objects" to work with in programming languages.术语“通过引用传递”和“通过值传递”早于在编程语言中使用“对象”的概念。 It's really not about objects at all;这真的与对象无关。 it's about function parameters, and specifically how function parameters are "connected" (or not) to the calling environment.它是关于函数参数,特别是函数参数如何“连接”(或不连接)到调用环境。 In particular, note that in a true pass-by-reference language — one that does involve objects — one would still have the ability to modify object contents , and it would look pretty much exactly like it does in JavaScript.特别要注意的是,在真正的按引用传递语言中——一种确实涉及对象的语言——仍然可以修改对象内容,它看起来与 JavaScript 中的非常相似。 However, one would also be able to modify the object reference in the calling environment, and that's the key thing that you can't do in JavaScript.但是,您可以在调用环境中修改对象引用,这是您在 JavaScript 中无法做到的关键。 A pass-by-reference language would pass not the reference itself, but a reference to the reference .传递引用语言不会传递引用本身,而是传递对引用的引用

edithere is a blog post on the topic.编辑- 这是关于该主题的博客文章。 (Note the comment to that post that explains that C++ doesn't really have pass-by-reference. That is true. What C++ does have, however, is the ability to create references to plain variables, either explicitly at the point of function invocation to create a pointer, or implicitly when calling functions whose argument type signature calls for that to be done. Those are the key things JavaScript doesn't support.) (注意那篇文章的评论,它解释了 C++ 并没有真正通过引用传递。这是真的。但是,C++ 确实具有创建对普通变量的引用的能力,无论是在函数点显式调用以创建指针,或在调用其参数类型签名要求完成的函数时隐式调用。这些是 JavaScript 不支持的关键内容。)

  1. Primitive type variables like strings and numbers are always passed by value.像字符串和数字这样的原始类型变量总是按值传递。
  2. Arrays and Objects are passed by reference or by value based on these conditions:数组和对象根据以下条件按引用或按值传递:
  • if you are setting the value of an object or array it is Pass by Value.如果您要设置对象或数组的值,则它是按值传递。

     object1 = { prop: "car" }; array1 = [1,2,3];
  • if you are changing a property value of an object or array then it is Pass by Reference.如果您要更改对象或数组的属性值,则它是按引用传递。

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

Code代码

 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

Workaround to pass variable like by reference:通过引用传递变量的解决方法:

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

inc('a');

alert(a); // 2

And yup, actually you can do it without access a global variable:是的,实际上你可以在不访问全局变量的情况下做到这一点:

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

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

    return init;
})();

inc();

Simple Object简单对象

 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"


Custom Object自定义对象

Object rvar对象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);

Yet another approach to pass any (local, primitive) variables by reference is by wrapping variable with closure "on the fly" by eval .另一种通过引用传递任何(本地、原始)变量的方法是通过eval用闭包“即时”包装变量。 This also works with "use strict".这也适用于“使用严格”。 (Note: be aware that eval is not friendly to JavaScript optimizers, and also missing quotes around variable name may cause unpredictive results) (注意:请注意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);
})()

Live sample: https://jsfiddle.net/t3k4403w/实时示例: https : //jsfiddle.net/t3k4403w/

I personally dislike the "pass by reference" functionality offered by various programming languages.我个人不喜欢各种编程语言提供的“通过引用传递”功能。 Perhaps that's because I am just discovering the concepts of functional programming, but I always get goosebumps when I see functions that cause side effects (like manipulating parameters passed by reference).也许那是因为我刚刚发现函数式编程的概念,但是当我看到会引起副作用的函数(例如操作通过引用传递的参数)时,我总是会起鸡皮疙瘩。 I personally strongly embrace the "single responsibility" principle.我个人强烈拥护“单一责任”原则。

IMHO, a function should return just one result/value using the return keyword.恕我直言,一个函数应该使用 return 关键字只返回一个结果/值。 Instead of modifying a parameter/argument, I would just return the modified parameter/argument value and leave any desired reassignments up to the calling code.我不会修改参数/参数,而是返回修改后的参数/参数值,并将任何所需的重新分配留给调用代码。

But sometimes (hopefully very rarely), it is necessary to return two or more result values from the same function.但有时(希望很少),有必要从同一个函数返回两个或多个结果值。 In that case, I would opt to include all those resulting values in a single structure or object.在这种情况下,我会选择将所有这些结果值包含在单个结构或对象中。 Again, processing any reassignments should be up to the calling code.同样,处理任何重新分配应该由调用代码决定。

Example:例子:

Suppose passing parameters would be supported by using a special keyword like 'ref' in the argument list.假设通过在参数列表中使用像“ref”这样的特殊关键字来支持传递参数。 My code might look something like this:我的代码可能如下所示:

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

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

Instead, I would actually prefer to do something like this:相反,我实际上更愿意做这样的事情:

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

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

When I would need to write a function that returns multiple values, I would not use parameters passed by reference either.当我需要编写一个返回多个值的函数时,我也不会使用通过引用传递的参数。 So I would avoid code like this:所以我会避免这样的代码:

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

Instead, I would actually prefer to return both new values inside an object, like this:相反,我实际上更喜欢在对象内返回两个新值,如下所示:

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

These code examples are quite simplified, but it roughly demonstrates how I personally would handle such stuff.这些代码示例相当简化,但它粗略地展示了我个人将如何处理这些东西。 It helps me to keep various responsibilities in the correct place.它帮助我将各种职责放在正确的位置。

Happy coding.快乐编码。 :) :)

I've been playing around with syntax to do this sort of thing, but it requires some helpers that are a little unusual.我一直在玩弄语法来做这种事情,但它需要一些有点不寻常的助手。 It starts with not using 'var' at all, but a simple 'DECLARE' helper that creates a local variable and defines a scope for it via an anonymous callback.它从根本不使用“var”开始,而是一个简单的“DECLARE”助手,它创建一个局部变量并通过匿名回调为其定义一个范围。 By controlling how variables are declared, we can choose to wrap them into objects so that they can always be passed by reference, essentially.通过控制变量的声明方式,我们可以选择将它们包装到对象中,以便它们本质上始终可以通过引用传递。 This is similar to one of the Eduardo Cuomo's answer above, but the solution below does not require using strings as variable identifiers.这类似于上面 Eduardo Cuomo 的答案之一,但下面的解决方案不需要使用字符串作为变量标识符。 Here's some minimal code to show the concept.这里有一些最小的代码来展示这个概念。

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

There's actually a pretty 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"]

Actually it is really easy.其实这真的很容易。 The problem is understanding that once passing classic arguments, you are scoped into another, read-only zone.问题在于,一旦传递经典参数,您就会被限制在另一个只读区域中。

The solution is to pass the arguments using JavaScript's object-oriented design.解决方案是使用 JavaScript 的面向对象设计传递参数。 It is the same as putting the arguments in a global/scoped variable, but better...它与将参数放在全局/范围变量中相同,但更好......

function action(){
  /* Process this.arg, modification allowed */
}

action.arg = [["empty-array"], "some string", 0x100, "last argument"];
action();

You can also promise stuff up to enjoy the well-known chain: Here is the whole thing, with promise-like structure您还可以承诺享受知名连锁店的东西:这是整个事情,具有类似承诺的结构

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

Or better yet...或者更好...

action.setArg(["empty-array"],"some string",0x100,"last argument").call()

JavaScript can modify array items inside a function (it is passed as a reference to the object/array). 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);

Here's another example:这是另一个例子:

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]

JavaScript not being strong type. JavaScript 不是强类型。 It allows you to resolve problems in many different ways, as it seem in this question.它允许您以许多不同的方式解决问题,就像在这个问题中一样。

However, for a maintainability point of view, I would have to agree with Bart Hofland.但是,从可维护性的角度来看,我不得不同意 Bart Hofland 的观点。 A function should get arguments to do something with and return the result.函数应该获取参数以执行某些操作并返回结果。 Making them easily reusable.使它们易于重复使用。

If you feel that variables need to be passed by reference, you may be better served building them into objects, IMHO.如果你觉得变量需要通过引用传递,你可能会更好地将它们构建到对象中,恕我直言。

Putting aside the pass-by-reference discussion, those still looking for a solution to the stated question could use:撇开逐引用讨论不谈,那些仍在寻找所述问题的解决方案的人可以使用:

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

I know exactly what you mean.我确切地知道你的意思。 The same thing in Swift will be no problem.同样的事情在 Swift 中不会有问题。 The bottom line is use let , not var .底线是使用let ,而不是var

The fact that primitives are passed by value, but the fact that the value of var i at the point of iteration is not copied into the anonymous function is quite surprising to say the least.原语是按值传递的,但迭代时var i的值没有复制到匿名函数中这一事实至少可以说是相当令人惊讶的。

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

I like to solve the lack of by reference in JavaScript like this example shows.我喜欢解决 JavaScript 中缺少引用的问题,就像这个例子所示。

The essence of this is that you don't try to create a by reference .其本质是您不要尝试通过引用创建一个。 You instead use the return functionality and make it able to return multiple values.您可以使用返回功能并使其能够返回多个值。 So there isn't any need to insert your values in arrays or objects.所以没有必要在数组或对象中插入你的值。

 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>

If you want to pass variables by reference, a better way to do that is by passing your arguments in an object and then start changing the value by using window :如果要通过引用传递变量,更好的方法是在对象中传递参数,然后使用window开始更改值:

window["varName"] = value;

Example:例子:

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

Using Destructuring here is an example where I have 3 variables, and on each I do the multiple operations:这里使用解构是一个例子,我有 3 个变量,每个变量我都做多个操作:

  • If value is less than 0 then change to 0,如果值小于 0,则更改为 0,
  • If greater than 255 then change to 1,如果大于 255,则更改为 1,
  • Otherwise dived the number by 255 to convert from a range of 0-255 to a range of 0-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

As we don't have javascript pass by reference functionality, the only way to do this is to make the function return the value and let the caller assign it:由于我们没有通过引用传递 javascript 功能,唯一的方法是让函数返回值并让调用者分配它:

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

(This is in case you need assignment inside the function, if only mutation is necessary, then passing the object and mutating it should be enough) (这是在你需要在函数内部赋值的情况下,如果只需要改变,那么传递对象并改变它应该就足够了)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM