Javascript是否通过引用传递?

时间:2012-10-27 21:50:11

标签: javascript reference pass-by-reference pass-by-value

Javascript是通过引用传递还是传递值?以下是 Javascript:The Good Parts 的示例。我对矩形函数的my参数非常困惑。它实际上是undefined,并在函数内重新定义。没有原始参考。如果我从函数参数中删除它,则内部区域功能无法访问它。

是关闭吗?但是没有返回任何函数。

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};
var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};
myShape = shape({
    name: "Unhnown"
});
myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});
console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());

13 个答案:

答案 0 :(得分:592)

基元按值传递,对象通过“引用副本”传递。

具体来说,当您传递一个对象(或数组)时,您(无形地)传递对该对象的引用,并且可以修改该对象的内容,但是如果您尝试覆盖引用它不会影响调用者持有的引用副本 - 即引用本身按值传递:

function replace(ref) {
    ref = {};           // this code does _not_ affect the object passed
}

function update(ref) {
    ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
}

var a = { key: 'value' };
replace(a);  // a still has its original value - it's unmodfied
update(a);   // the _contents_ of 'a' are changed

答案 1 :(得分:55)

这样想:

每当你在ECMAscript中创建一个对象时,这个对象就形成了一个神秘的 ECMAscript通用位置,在那里没有人能够获得。你得到的只是在这个神秘的地方找到该对象的引用

var obj = { };

即使obj只是对象的引用(位于该特殊的奇妙位置),因此,您只能传递此引用。实际上,访问 obj 的任何代码都将修改远离遥远的对象

答案 2 :(得分:37)

My 2 Cents .... Javascript是否通过引用或值传递参数无关紧要。真正重要的是分配与变异。

我在这里写了一个更长,更详细的解释(Is JavaScript a pass-by-reference or pass-by-value language?

当你传递任何东西时(无论是对象还是原始),所有javascript都会在函数内部分配一个新变量...就像使用等号(=)

该参数在函数内的行为方式与您使用等号分配新变量时的行为完全相同。请看这些简单示例。

var myString = 'Test string 1';

// Assignment - A link to the same place as myString
var sameString = myString;

// If I change sameString, it will not modify myString, 
// it just re-assigns it to a whole new string
sameString = 'New string';

console.log(myString); // logs 'Test string 1';
console.log(sameString); // logs 'New string';

如果我将myString作为参数传递给函数,它的行为就好像我只是将它分配给一个新变量。现在,让我们做同样的事情,但是使用函数而不是简单的赋值

function myFunc(sameString) {

    // Re assignment.. again, it will not modify myString
    sameString = 'New string';
}

var myString = 'Test string 1';

// This behaves the same as if we said sameString = myString
myFunc(myString);

console.log(myString); // Again, logs 'Test string 1';

在将对象传递给函数时可以修改对象的唯一原因是因为您没有重新分配...而是可以更改或改变对象....再次,它的工作方式相同。

var myObject = { name: 'Joe'; }

// Assignment - We simply link to the same object
var sameObject = myObject;

// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'

sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'

// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'

如果我将myObject作为参数传递给函数,它就像我只是将它分配给一个新变量一样。同样,具有完全相同的行为,但具有功能。

function myFunc(sameObject) {

    // We mutate the object, so the myObject gets the change too... just like before.
    sameObject.name = 'Jill';

    // But, if we re-assign it, the link is lost
    sameObject = { name: 'Howard' };
}

var myObject = { name: 'Joe'; }

// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'

每次将变量传递给函数时,您都会“分配”参数的名称,就像使用等号(=)一样。

始终记住,等号(=)表示分配。 将参数传递给函数也意味着赋值。 它们是相同的,2个变量以完全相同的方式连接。

修改变量影响另一个变量的唯一时间是基础对象发生变异时。

在对象和基元之间进行区分是没有意义的,因为它的工作方式与没有函数的方式相同,只是使用等号分配给新变量。

答案 3 :(得分:17)

与C一样,最终,一切都按价值传递。与C不同,您实际上无法备份并传递变量的位置,因为它没有指针只是引用。

它所拥有的引用都是对象,而不是变量。有几种方法可以实现相同的结果,但必须手动完成,而不仅仅是在呼叫或声明网站上添加关键字。

答案 4 :(得分:15)

函数参数按值或按共享传递,但在Javascript中不会通过引用从不传递!

通话按值

原始类型按值传递:

var num = 123, str = "foo";

function f(num, str) {
  num += 1;
  str += "bar";
  console.log("inside of f:", num, str);
}

f(num, str);
console.log("outside of f:", num, str);

功能范围内的

重新分配在周围范围内不可见。

这也适用于String s,它们是复合数据类型但不可变:

var str = "foo";

function f(str) {
  str[0] = "b"; // doesn't work, because strings are immutable
  console.log("inside of f:", str);
}

f(str);
console.log("outside of f:", str);

通话逐共享

对象,也就是说所有非基元的类型都是通过共享传递的。保存对象引用的变量实际上只包含此引用的副本。如果Javascript将采用逐个引用的评估策略,则该变量将保留原始引用。这是副共享和参考之间的关键区别。

这种区别的实际后果是什么?

var o = {x: "foo"}, p = {y: 123};

function f(o, p) {
  o.x = "bar"; // mutation
  p = {x: 456}; // reassignment
  console.log("o inside of f:", o);
  console.log("p inside of f:", p);
}

f(o, p);

console.log("o outside of f:", o);
console.log("p outside of f:", p);

变异表示修改现有Object的某些属性。变量绑定到的引用副本以及引用此对象的引用副本保持不变。因此,在调用者的范围内可以看到突变。

重新分配表示替换绑定到变量的引用副本。由于它只是一个副本,因此保存相同引用副本的其他变量不受影响。因此,在呼叫者的范围内,重新分配是不可见的,就像参考呼叫评估策略一样。

有关Ecmascript中evaluation strategies的更多信息。

答案 5 :(得分:6)

JavaScript是按值传递的。 对于原语,传递原始值。 对于Objects,传递Object的引用“value”。

对象示例:

var f1 = function(inputObject){
    inputObject.a=2;
}
var f2 = function(){
    var inputObject={"a":1};
    f1(inputObject); 
    console.log(inputObject.a);
}

调用f2会导致将“a”值打印为2而不是1,因为传递了引用并且更新了引用中的“a”值。

原语示例:

var f1 = function(a){
    a=2;
}
var f2 = function(){
    var a =1;
    f1(a); 
    console.log(a);
}

调用f2会导致将“a”值打印为1。

答案 6 :(得分:4)

实际上,Alnitak是正确的并且易于理解,但最终在JavaScript中,一切都是通过价值传递的。

对象的“价值”是什么?它是对象参考。

当您传入一个对象时,您将获得该值的副本(因此是Alnitak描述的“引用副本”)。如果更改此值,则不会更改原始对象,而是更改该引用的副本。

答案 7 :(得分:4)

为了创建一个使用const ...

的简单示例
const myRef = { foo: 'bar' };
const myVal = true;

function passes(r, v) {
  r.foo = 'baz';
  v = false;
}

passes(myRef, myVal);

console.log(myRef, myVal); // Object {foo: "baz"} true

答案 8 :(得分:3)

“全局”javascript变量是window对象的成员。您可以作为窗口对象的成员访问引用。

var v = "initialized";
function byref(ref) {
 window[ref] = "changed by ref";
}
byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// could also be called like... byref('v');
console.log(v); // outputs changed by ref

注意,上面的例子不适用于在函数中声明的变量。

答案 9 :(得分:1)

如果没有纯粹主义,我认为在Javascript中通过引用模拟标量参数的最佳方法是使用对象,就像之前的回答所说的那样。

然而,我有点不同:

我在函数调用中进行了对象分配,因此可以看到函数调用附近的引用参数。它增加了源可读性

在函数声明中,我将属性设置为注释,原因完全相同:可读性。

var r;

funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);


function funcWithRefScalars(o) {  // o(amount, message)
  o.amount  *= 1.2;
  o.message = "20% increase";
}

在上面的示例中,null清楚地表示输出参考参数。

退出:

240 - 20% Increase

在客户端,console.log应替换为alert

★★★

另一种更具可读性的方法:

var amount, message;

funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);

function funcWithRefScalars(amount, message) {  // o(amount, message)
   amount[0]  *= 1.2;
   message[0] = "20% increase";
}

在这里,您甚至不需要创建新的虚拟名称,例如上面的r

答案 10 :(得分:0)

原语按值传递。但是,如果您只需要读取一个原始值(并且在调用函数时不知道该值),则可以传递函数,该函数在需要时检索该值。

function test(value) {
  console.log('retrieve value');
  console.log(value());
}

// call the function like this
var value = 1;
test(() => value);

答案 11 :(得分:0)

在人们试图证明通过引用的示例中,我看不到通过引用。我只看到传递值

对于持有对象引用的变量,引用是这些变量的,因此引用将被传递,然后被传递值

在这样的声明中:

var a = {
  b:"foo", 
  c:"bar"
};

'a'的值不是对象,而是(到目前为止)对它的引用。换句话说,对象不在变量a中,而是对其的引用。我认为这对于主要只熟悉JavaScript的程序员来说似乎很难。但是对于也知道例如Java,C#和C。

答案 12 :(得分:0)

对象始终按引用传递,而基元始终按值传递,只需将参数保留在对象的相同地址即可。这是一些代码来说明我的意思(在https://js.do/之类的JavaScript沙箱中尝试) 不幸的是,您不仅保留了参数的地址,还保留了所有原始成员值。

a = { key: 'bevmo' };
testRetain(a);
document.write(' after function ');
document.write(a.key);


function testRetain (b)
 {
 document.write(' arg0 is ');
 document.write(arguments[0].key);
 b.key='passed by reference';
 var retain = b; //retaining the original address of the parameter

 //address of left set to address of right, changes address of parameter
 b={key: 'vons'}; //right is a new object with a new address
 document.write(' arg0 is ');
 document.write(arguments[0].key);

 //now retrieve the original address of the parameter for pass by reference
 b=retain; 
 document.write(' arg0 is ');
 document.write(arguments[0].key);
}

结果: arg0是bevmo arg0是vons arg0在引用传递函数之后通过引用传递