2

我已经知道如何使这段代码工作,但我的问题更多是关于为什么它会这样工作,以及我做的事情是否正确。

我可以做的最简单的例子来展示我的问题是这样的:

假设我有一个函数可以在按下按钮时将输入字段的值增加 10。

var scopeTest = {

    parseValue : function( element, value ) {
        value = parseInt( element.val(), 10 );
        //Why does this not return the value?
        return value;
    },


    incrementValue : function( element, button, value ) {
        button.on('mousedown', function (e) {
            //Execute the parseValue function and get the value
            scopeTest.parseValue( element, value ); 
            //Use the parsed value      
            element.val( value + 10 );    
            e.preventDefault(); 
        });               
    },


    init : function () { 
        var element = $('#test-input'),
            button = $('#test-button'),
            value = ''; 

        this.incrementValue( element, button, value );
    }

};

scopeTest.init();

上面的代码不起作用,因为在方法内部执行时该parseValue方法没有正确返回valuevar incrementValue

为了解决这个问题,我必须像这样将scopeTest.parseValue( element, value );参数设置为value变量:

value = scopeTest.parseValue( element, value );

比代码有效。

但我的问题是为什么?为什么这个额外的变量赋值步骤是必要的,为什么 return 语句还不够?此外,我使用我的函数/方法做的一切都是正确的,或者这只是 JavaScript 的工作方式?

这里的工作示例=> http://jsfiddle.net/Husar/zfh9Q/11/

4

5 回答 5

2

因为parseValue的value参数只是一个参考。是的,您可以更改对象,因为您有一个引用,但如果您分配给该引用,它现在指向不同的对象。

原版不变。是的,返回是“足够的”,但是您将新对象保存在一个变量中,该变量的生命周期在下一行代码处结束。

人们说 JavaScript 通过引用传递对象,但过于从字面上理解可能会令人困惑。JavaScript 中的所有对象句柄都是引用。这个引用本身并不是通过引用传递的,也就是说,你没有得到一个双间接指针。因此,您可以通过形式参数更改对象本身,但不能更改调用站点的引用本身。

于 2012-04-15T19:22:48.727 回答
1

这不是范围界定问题,而是按引用传递和按值传递之间的混淆。

在 JavaScript 中,所有数字都是按值传递的,这意味着:

var value = 10;
scopeTest.parseValue( element, value );
// value still == 10

对象和数组通过引用传递,意思是:

function foo( obj ){
    obj.val = 20;
}

var value = { val: 10 }
foo( value );
// value.val == 20;
于 2012-04-15T19:24:05.483 回答
1

正如其他人所说,这是pass-by-ref vs pass-by-val

给定: function foo (){return 3+10;} foo();
会发生什么?该操作已执行,但您没有在任何地方设置该值。

鉴于: result = foo();
操作已执行,但您已存储该值以供将来使用。


这只是范围问题

  • var param = 0;  
    function foo( param ) { 
       param = 1; 
    } 
    foo(param);
    console.log(param);                  // still retains value of 0
    

为什么?

有一个param是全局的,但在函数内部调用了参数的名称param,因此该函数不会使用全局。而是param仅应用本地实例 ( this.param)。如果您这样做,则完全不同:

  • var param = 0;  
    function foo() {                     // notice no variable 
       param = 1;                        // references global
    } 
    foo(param);
    console.log(param);                  // new value of 1
    

这里没有调用局部变量param,所以它使用全局变量。

于 2012-04-15T19:39:55.720 回答
1

这主要是范围问题。pass-by-* 问题讨论起来很奇怪,因为发送者变量和被调用函数变量具有相同的名称。反正我会试试的。

变量具有可见的范围。您可以将其视为存储内容的地方。此范围由函数的位置定义。意味着它在您的源代码中的位置(在全局范围内或在函数范围内)。它是在您编写源代码时定义的,而不是您之后如何调用函数。

范围可以嵌套。在您的示例中,有四个范围。全局作用域,每个函数都有一个作用域。您的函数的作用域都具有作为父作用域的全局作用域。父范围意味着每当您尝试访问名称/变量时,首先在函数范围内搜索它,如果未找到,则搜索继续到父范围,直到找到名称/变量或达到全局范围(在这种情况下,您会收到无法找到的错误)。

允许多次定义同一个名称。我认为这是你困惑的根源。你眼中的“价值”这个名字总是一样的,但它在你的脚本中出现了三次。每个函数都定义了它:parseValue 和 incrementValue 作为参数,init 作为局部变量。这种效果称为阴影。这意味着名称为“值”的所有变量始终存在,但如果您查找名称,则较早找到名称,从而使另一个不可见/隐藏。

在这种情况下,“值”在所有三个函数中的处理方式都是相似的,因为本地 var 和参数的范围是相同的。这意味着只要您输入其中一种方法,您就进入了函数范围。通过输入作用域,名称“值”被添加到作用域链中,并在执行函数时首先被找到。反之亦然。如果离开函数范围,则“值”将从范围链中删除并变得不可见并被丢弃。

这里非常令人困惑,因为您调用一个带有参数“值”的函数,该函数带有名称为“值”的东西,但它们仍然意味着不同的东西。不同的是,需要将值从一个“值”传递给另一个。发生的情况是外部“值”的值被复制到内部“值”。这就是按值传递的含义。被复制的值可以是对对象的引用,大多数人认为它是通过引用传递的。如果这听起来令人困惑,我很抱歉,但这里有太多的价值命名。

该值从外部函数复制到被调用函数,并且只存在于被调用函数内部。如果函数结束你对它所做的每一个更改都将被丢弃。唯一的可能是回报你的“副作用”。这意味着您的工作将在函数被丢弃之前不久被复制回变量

其他替代方案确实是离开参数并使用范围链,例如全局范围。但我强烈建议你不要这样做。它似乎很容易使用,但它会产生许多微妙的错误,这将使您的生活更加艰难。最好的办法是确保变量具有最窄的范围(使用它们的地方)并传递每个函数参数的值和返回值。

于 2012-04-16T07:53:33.133 回答
-1

你可以看看它。

http://snook.ca/archives/javascript/javascript_pass

于 2012-04-15T19:24:45.743 回答