我已经知道如何使这段代码正常工作,但我的问题更多的是为什么它会像这样工作,以及我做的正确。
我可以用来展示我的问题的最简单的例子是:
假设我有一个函数,只需按一下按钮就可以将输入字段的值增加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
方法在value
方法中执行时没有正确返回incrementValue
var。
要解决这个问题,我必须将scopeTest.parseValue( element, value );
参数设置为value
变量,如下所示:
value = scopeTest.parseValue( element, value );
比代码有效。
但我的问题是为什么?为什么这个额外的变量赋值步骤是必要的,为什么return语句不够?我也在用我的函数/方法做正确的事,或者这只是JavaScript的工作方式?
这里的工作示例=> http://jsfiddle.net/Husar/zfh9Q/11/
答案 0 :(得分:2)
因为 parseValue 的值参数只是一个引用。是的,您可以更改对象,因为您有一个引用,但是如果您分配给引用它现在指向另一个对象。
原始版本未更改。是的,返回“足够”,但是您将新对象保存在变量中,其生命周期在下一行代码处结束。
人们说JavaScript通过引用传递对象,但是从字面上理解这可能会令人困惑。 JavaScript中的所有对象句柄都是引用。此引用本身不是通过引用传递的,也就是说,您没有获得双间接指针。因此,您可以通过形式参数更改对象本身,但不能更改调用站点的引用本身。
答案 1 :(得分: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;
答案 2 :(得分:1)
鉴于: 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
的局部变量,所以它使用全局。
答案 3 :(得分:1)
这主要是范围问题。 pass-by- *问题很难讨论,因为sender变量和被调用函数变量具有相同的名称。无论如何我都会尝试。
变量具有可见范围。您可以将其视为存储内容的位置。此范围由您的函数的位置定义。意味着它在源代码中的位置(在全局范围内或在函数范围内)。它是在您编写源代码时定义的,而不是之后调用函数的方式。
范围可以嵌套。在您的示例中,有四个范围。全局范围和每个函数都有一个范围。函数的作用域都具有全局作用域作为父作用域。父范围意味着每当您尝试访问名称/变量时,首先在函数范围中搜索它,如果未找到,则搜索将继续到父范围,直到找到名称/变量或已达到全局范围(在这种情况下,你会收到一个无法找到的错误。)
允许多次定义相同的名称。我认为这是你困惑的根源。您眼睛的名称“值”始终相同,但在脚本中存在三次。每个函数都定义了它:parseValue和incrementValue作为参数,init作为local var。这种效果称为阴影。这意味着名称为'value'的所有变量始终存在,但如果您查找名称,则会先找到一个名称,从而使另一个变得不可见/阴影。
在这种情况下,“value”在所有三个函数中都被视为相似,因为局部var和参数的范围是相同的。这意味着只要您输入其中一个方法,就可以输入功能范围。通过输入范围,名称“value”将添加到范围链中,并在执行函数时首先找到。反之亦然。如果保留函数作用域,则会从作用域链中删除“value”并使其不可见并被丢弃。
这里非常令人困惑,因为你调用的函数带有一个名为“value”的参数“value”,但它们仍然意味着不同的东西。不同的是需要将值从一个“值”传递到另一个“值”。会发生什么是外部“值”的值被复制到内部“值”。这意味着传递价值。被复制的值可以是对象的引用,这是大多数人认为它是通过引用传递的。我很抱歉,如果这听起来令人困惑,但这里有太多的价值命名。
该值从外部函数复制到被调用函数,并且只在被调用函数内部存在。如果函数结束,您对其所做的每个更改都将被丢弃。唯一的可能性是返回你的“副作用”。这意味着您的工作将在函数被丢弃之前不久被复制回变量
对于其他替代方案确实是留下参数并与范围链一起工作,例如全球范围。但我强烈建议你不要这样做。它似乎很容易使用,但它产生了许多微妙的错误,这将使你的生活更加艰难。最好的办法是确保变量具有最窄的范围(使用它们的位置)并传递每个函数参数的值和返回值。
答案 4 :(得分:-1)