提问人:BFTrick 提问时间:10/13/2011 最后编辑:Peter MortensenBFTrick 更新时间:11/20/2023 访问量:586863
在 JavaScript 中按引用传递变量
Pass variables by reference in JavaScript
问:
如何在 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
最好的方法是什么?
答:
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 上炸开了锅,这篇博文类似于下面提到的一篇博文,尽管在这种情况下是关于 Java 的。在阅读 Reddit 评论的来回时,我突然想到,很大一部分混淆源于涉及“参考”一词的不幸碰撞。术语“按引用传递”和“按值传递”早于编程语言中具有“对象”的概念。这真的与物体无关;它是关于函数参数的,特别是函数参数如何“连接”(或不连接)到调用环境。特别要注意的是,在真正的引用传递语言中——一种确实涉及对象的语言——人们仍然能够修改对象内容,而且它看起来与在 JavaScript 中几乎完全相同。但是,人们也可以在调用环境中修改对象引用,这是您在 JavaScript 中无法做到的关键事情。引用传递语言不会传递引用本身,而是传递对引用的引用。
编辑 — 这是一篇关于该主题的博客文章。 (请注意该帖子的评论,该评论解释了 C++ 实际上没有通过引用。这是真的。然而,C++ 确实具有创建对普通变量的引用的能力,无论是在函数调用点显式创建指针,还是在调用其参数类型签名要求执行此操作的函数时隐式引用。这些是 JavaScript 不支持的关键内容。
评论
通过引用传递变量的解决方法:
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();
评论
简单对象
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);
评论
/workspace/Main.js:43 context = window; ReferenceError: window is not defined
rvar
我一直在尝试语法来做这种事情,但它需要一些有点不寻常的帮助程序。它首先根本不使用“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);
});
评论
let five = 5;
class
INC(ref)
- 字符串和数字等基元类型变量始终按值传递。
- 数组和对象基于以下条件通过引用或值传递:
如果要设置对象或数组的值,则为“按值传递”。
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
评论
实际上,这真的很容易。问题在于,一旦传递了经典参数,你就会被限制在另一个只读区域中。
解决方案是使用 JavaScript 的面向对象设计传递参数。这与将参数放在全局/范围变量中相同,但更好......
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()
评论
this.arg
仅适用于实例。这是行不通的。action
class
this
class
另一种通过引用传递任何(局部、原始)变量的方法是将变量包装起来,并“动态”地使用闭包。这也适用于“use strict”。(注意:请注意,这对 JavaScript 优化器不友好,并且变量名称周围缺少引号可能会导致不可预测的结果)eval
eval
"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
实际上有一个漂亮的解决方案:
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"]
评论
我个人不喜欢各种编程语言提供的“引用传递”功能。也许那是因为我刚刚发现函数式编程的概念,但是当我看到引起副作用的函数(例如操作引用传递的参数)时,我总是起鸡皮疙瘩。我个人强烈拥护“单一责任”原则。
恕我直言,函数应该只使用 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);
这些代码示例非常简化,但它大致演示了我个人如何处理这些东西。它帮助我将各种责任放在正确的位置。
祝您编码愉快。:)
评论
findStuff
foundArray
var foundArray = findStuff(inArray); if (foundArray.length > 0) { /* process foundArray */ }
findStuff
我完全明白你的意思。在 Swift 中做同样的事情不会有问题。底线是使用,而不是。let
var
事实上,基元是按值传递的,但迭代点的值没有被复制到匿名函数中,至少可以说是相当令人惊讶的。var i
for (let i = 0; i < boxArray.length; i++) {
boxArray[i].onclick = function() { console.log(i) }; // Correctly prints the index
}
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]
撇开参考传递讨论不谈,那些仍在寻找所述问题解决方案的人可以使用:
const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));
评论
我喜欢通过引用来解决 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>
评论
{ a: a, b: b, c: c }
{ a, b, c }
with
with
{}
如果要通过引用传递变量,更好的方法是在对象中传递参数,然后使用以下方法开始更改值: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
评论
window
globalThis
这里使用解构是一个示例,我有 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
由于我们没有 javascript 通过引用传递功能,因此执行此操作的唯一方法是让函数返回值并让调用者分配它:
所以
"makePretty(myArray[x]);"
应该是
"myArray[x] = makePretty(myArray[x]);"
这是为了防止您需要在函数内部进行赋值,如果只需要突变,那么传递对象并改变它就足够了
评论
这可能无法回答上述确切问题,但对于许多出现主题的情况,功能性更强的设计是一种富有成效的途径,实际上可以降低复杂性,而不是添加它:
result = new Array(var1, var2, var3)
.map(makePretty)
评论
makePretty(var1); makePretty(var2); makePretty(var3); ...
arr = [var1, var2, var3]; for (var i = 0, len = arr.length; i < len; i++) { arr[i] = makePretty(arr[i]); }
makePretty