javascript中的范围很奇怪

Sum*_*ama 19 javascript scope function pass-by-reference

在javascript中传递对象及其引用.意味着应该反映任何地方对象的变化.在这种情况下,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)
Run Code Online (Sandbox Code Playgroud)

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

Jes*_*han 38

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

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'}
Run Code Online (Sandbox Code Playgroud)

当你在做的时候,a = b你将局部变量分配给持有a的引用b.

  • 是的,这个例子与尼古拉的回答实际上解决了我的问题. (2认同)

Nik*_*off 22

你是对的,通过引用传递对象,并且对函数中的对象所做的任何更改都将反映在任何地方.这正是为什么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)
Run Code Online (Sandbox Code Playgroud)

  • 但是对象不是*通过引用传递的!`a = b`只修改*local*引用(变量)! (5认同)
  • "通过引用传递对象是对的" - 不,它们不是.JavaScript是严格按值传递的,OP的代码很好地证明了这一点.如果JavaScript是通过引用传递的,那么OP的代码就可以工作,而且他从来没有问过这个问题. (5认同)
  • @Bergi:这个链接就是答案:)它准确地解释了OP的问题. (2认同)

Bri*_*128 13

函数上下文中的变量"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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下很明显,'foo'变量只存在于函数内部,并且随着引用按值传递,foo = bardo不会改变a.

  • 这个得到它.`function change(a,b)`创建局部变量*a*和*b*,因此赋值`a = b`赋值给本地*a*变量,而不是全局变量. (2认同)

Jör*_*tag 10

在javascript中传递对象及其引用.

不,他们不是.ECMAScript/JavaScript严格按值传递.(更准确地说,是分享呼叫,这是传递值的特殊情况.)

这里发生了什么?

这只是正常的传值.

您的困惑源于您错误地认为ECMAScript/JavaScript是传递引​​用的事实,而事实上并非如此.

ECMAScript使用pass-by-value,或者更确切地说,是一个传递值的特殊情况,其中传递的值始终是指针.这种特殊情况有时也称为共享呼叫,对象共享呼叫或逐个呼叫.

它与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!
Run Code Online (Sandbox Code Playgroud)

如果您熟悉C#,那么理解值类型和引用类型的pass-by-value和pass-by-reference之间的差异是一种非常好的方法,因为C#支持所有4种组合:value for value-value for value types("传统的按值传递"),引用类型的值传递(按共享调用,按对象调用,在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.
    }
}
Run Code Online (Sandbox Code Playgroud)


Lig*_*ica 8

好的,所以你已经发现JavaScript对象具有引用语义,因此修改引用对原始范围中的同一对象有影响.

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

可以这么说,这基本上就是你原始参考文献的形成方式.

  • @JörgWMittag:这几乎就像你没有读过我说过的任何内容.你正在应用你自己的个人狭义的术语定义,并使用它来做出具体的事实陈述,但你不能这样做:两者的普遍接受的含义并不能完全涵盖JavaScript的语义.阅读我链接到你的文章. (2认同)