javascript 范围内行为怪异

Scope in javascript acting weird

对象在 javascript 中与其引用一起传递。应该反映该对象从任何地方发生的变化。 在这种情况下,预期输出为 {} for console.log(a)

function change(a,b) {
    a.x = 'added';
    a = b;//assigning a as {} to b
}
a={}
b={}
change(a,b);
console.log(a); //expected {} but output {x:'added'}
console.log(b)

这里发生了什么?据我所知,这不应该是因为功能范围。 谢谢

函数上下文中的变量 'a' 与函数外部的 'a' 变量不同。此代码在语义上等同于您的代码:

function change(foo,bar) {
    foo.x = 'added';
    foo = bar;//assigning foo as {} to bar
}
a={}
b={}
change(a,b);
console.log(a); //expected {} but output {x:'added'}
console.log(b)

很明显,在这种情况下,'foo' 变量仅存在于函数内部,并且执行 foo = bar 不会更改 a,因为引用是按值传递的。

你说得对,对象是通过引用传递的,并且函数中对对象所做的任何更改都会反映到所有地方。这就是为什么在函数中添加 x 属性 修改了它之外的对象。

你缺少的是 a = b; 行没有修改对象 ,它修改了 对对象的引用。如果需要设置引用,可以将两个对象都传递到另一个容器对象/数组中:

function change(container) {
    container.a.x = 'added';
    container.a = container.b;//assigning a as {} to b
}
var container = { a: {}, b: {}};
change(container);
console.log(container.a);
console.log(container.b)

如果您添加另一行,您可以更清楚地了解正在发生的事情:

function change(a,b) {
    a.x = 'added';
    a = b;
    a.x = 'added as well';
};
a={};
b={};
change(a,b);
console.log(a);  //{x:'added'}
console.log(b);  //{x:'added as well'}

当您执行 a = b 时,您正在将局部变量 a 分配给 b 持有的引用。

这应该有助于解决您的问题:

var obj = {}, anotherObj = {};
// in case if they are not global, make them global or define parent scope to be able to modify inside the function
window.obj = obj;
window.anotherObj = anotherObj;
function swap(a, b) {
  window[a].newProp = 'XYZ';
  window[a] = window[b]; // now obj is gone, because replaced with anotherObj
}
swap('obj','anotherObj');
console.log(obj); // now it would give Object {}

好的,所以您已经发现 JavaScript 对象具有引用语义,因此修改引用对象会对原始范围内的同一对象产生影响。

您还需要意识到 = 不是这些规则的一部分;它不仅执行赋值,还会将引用重新绑定到新对象。

在幕后,可以这么说,这基本上就是您的原始参考资料的形成方式。

Object are passed with their reference in javascript.

不,他们不是。 ECMAScript/JavaScript 严格按值传递。 (更确切地说,是共享调用,它是传值的一种特殊情况。)

What is happening here?

这只是正常的按值传递。

您的困惑源于您错误地认为 ECMAScript/JavaScript 是传递引用,而实际上它不是。

ECMAScript 使用按值传递,或者更准确地说,是按值传递的一种特殊情况,其中传递的值始终 一个指针。这种特殊情况有时也称为共享调用、对象共享调用或对象调用。

Java(对象)、C#(引用类型默认)、Smalltalk、Python、Ruby 或多或少使用的约定相同曾经创造的面向对象语言。

注意:某些类型(例如Numbers)实际上是直接按值传递,不是通过中间指针传递.但是,由于它们是不可变的,因此在这种情况下,按值传递和按对象共享调用之间没有明显的行为差异,因此您可以通过简单地处理 一切 作为对象共享调用。只需将这些特殊情况解释为您无需担心的内部编译器优化。

这里有一个简单的例子,你可以运行确定ECMAScript(或任何其他语言,翻译后)的参数传递约定:

function isEcmascriptPassByValue(foo) {
  foo.push('More precisely, it is call-by-object-sharing!');
  foo = 'No, ECMAScript is pass-by-reference.';
  return;
}

var bar = ['Yes, of course, ECMAScript *is* pass-by-value!'];

isEcmascriptPassByValue(bar);

console.log(bar);
// Yes, of course, ECMAScript *is* pass-by-value!,
// More precisely, it is call-by-object-sharing!

如果您熟悉 C#,这是了解值类型和引用类型的按值传递和按引用传递之间差异的一种很好的方式,因为 C# 支持所有 4 种组合:传递-值类型按值传递 ("traditional pass-by-value"),引用类型按值传递(按共享调用、按对象调用、按对象共享调用,如 ECMAScript)、传递-引用类型引用,值类型引用传递。

(实际上,即使您了解 C#,这也不难理解。)

// In C#, struct defines a value type, class defines a reference type
struct MutableCell
{
    public string value;
}

class Program
{
    // the ref keyword means pass-by-reference, otherwise it's pass-by-value
    // You must explicitly request pass-by-reference both at the definition and the call
    static void IsCSharpPassByValue(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
    {
        foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
        foo = new string[] { "C# is not pass-by-reference." };

        bar.value = "For value types, it is *not* call-by-sharing.";
        bar = new MutableCell { value = "And also not pass-by-reference." };

        baz = "It also supports pass-by-reference if explicitly requested.";

        qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
    }

    static void Main(string[] args)
    {
        var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };

        var corge = new MutableCell { value = "For value types it is pure pass-by-value." };

        var grault = "This string will vanish because of pass-by-reference.";

        var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };

        // the first two are passed by value, the other two by reference
        IsCSharpPassByValue(quux, corge, ref grault, ref garply);

        Console.WriteLine(quux[0]);
        // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.

        Console.WriteLine(corge.value);
        // For value types it is pure pass-by-value.

        Console.WriteLine(grault);
        // It also supports pass-by-reference if explicitly requested.

        Console.WriteLine(garply.value);
        // Pass-by-reference is supported for value types as well.
    }
}