澄清 JavaScript 变量范围
Clarification of JavaScript variable scope
我已经知道如何使这段代码工作,但我的问题更多的是关于它为什么这样工作,以及我做对了。
我可以做的最简单的例子来展示我的问题:
假设我有一个函数,只需按一下按钮即可将输入字段的值增加 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
方法在 incrementValue
方法中执行时没有正确返回 value
var。
显然要解决它,我必须将scopeTest.parseValue( element, value );
参数设置为value
变量,如下所示:
value = scopeTest.parseValue( element, value );
比代码工作。
但我的问题是为什么?为什么这个额外的变量赋值步骤是必要的,为什么 return 语句还不够? 此外,我正在用我的函数/方法做所有正确的事情,或者这只是JavaScript的工作方式?
这里的工作示例=> http://jsfiddle.net/Husar/zfh9Q/11/
因为要解析的值参数只是一个引用。是的,您可以更改对象,因为您有一个引用,但是如果您指定给引用,它现在指向不同的对象。
原始版本保持不变。是的,返回是"足够的",但是您将新对象保存在一个变量中,该变量的生存期在下一行代码处结束。
人们说 JavaScript 通过引用传递对象,但从字面上理解可能会令人困惑。 JavaScript 中的所有对象句柄都是引用。此引用本身不是通过引用传递的,也就是说,您不会获得双间接指针。 因此,您可以通过形式参数更改对象本身,但不能更改调用站点的引用本身。
这主要是一个范围问题。传递 by-* 问题讨论起来很奇怪,因为发送方变量和被调用函数变量具有相同的名称。无论如何我都会尝试。
变量具有可见的范围。您可以将其视为存储某些内容的地方。此作用域由函数的位置定义。表示它在源代码中的位置(在全局范围内或在函数范围内)。它是在编写源代码时定义的,而不是在之后调用函数时定义的。
作用域可以嵌套。在您的示例中,有四个作用域。全局作用域和每个函数都有一个作用域。函数的作用域都将全局作用域作为父作用域。父作用域意味着,每当您尝试访问名称/变量时,都会首先在函数作用域中搜索它,如果未找到,则搜索将继续到父作用域,直到找到名称/变量或到达全局作用域(在这种情况下,您会收到找不到它的错误)。
允许多次定义相同的名称。我认为这是你困惑的根源。眼睛的名称"value"总是相同的,但它在您的脚本中存在三次。每个函数都定义了它:parseValue 和 incrementValue 作为参数,init 作为本地变量。这种效果称为阴影。这意味着所有名称为"value"的变量始终存在,但是如果您查找名称,则会更早地找到一个变量,从而使另一个不可见/阴影。
在这种情况下,"value"在所有三个函数中的处理方式相似,因为本地变量和参数的作用域相同。这意味着只要您输入其中一个方法,您就进入了函数范围。通过输入作用域,名称"value"将添加到作用域链中,并在执行函数时首先找到。事实恰恰相反。如果保留函数作用域,则"值"将从作用域链中删除,并且不可见并丢弃。
这里非常令人困惑,因为您调用一个函数,该函数将参数"值"与名称为"value"的东西一起使用,但它们的含义仍然不同。由于不同,需要将值从一个"值"传递到另一个"值"。发生的情况是,外部"值"的值被复制到内部"值"中。这就是按值传递的含义。被复制的值可以是对对象的引用,这是大多数人认为它是按引用传递的。如果这听起来令人困惑,我很抱歉,但这里有太多的价值命名。
该值从外部函数复制到被调用函数,并且仅存在于被调用函数内部。如果函数结束,您对其所做的每个更改都将被丢弃。唯一的可能性是返回您的"副作用"。这意味着您的工作将在函数被丢弃之前不久被复制回变量
对于其他替代方案,确实是保留参数并使用范围链,例如全局范围。但我强烈建议你不要那样做。它似乎易于使用,但它会产生许多微妙的错误,这将使您的生活更加困难。最好的办法是确保变量具有最窄的范围(使用它们的位置),并传递每个函数参数的值和返回值。
这不是范围问题,而是按引用传递和按值传递之间的混淆。
在 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;
正如其他人所说,这是一个通过参考与通过瓦尔。
给定: 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
的局部变量,所以它使用全局变量。
你可以看看它。
http://snook.ca/archives/javascript/javascript_pass
- ngDialog-弹出窗口未更新范围变量
- AngularJS范围变量Unwatch
- 将外部控制器的范围变量设置为角度
- 对于使用传递的数据计算的局部范围变量,角度绑定在自定义指令中不起作用
- 如何使用EnquireJS使AngularJS范围变量依赖于媒体查询
- 具有范围变量的控制器不工作
- 范围变量返回长度错误
- 将 NodeJS 模块范围变量作为对象访问
- Angularjs 更新 setTimeout 中的范围变量不起作用
- 递归承诺调用 - 内存范围变量问题
- AngularJS:从指令设置范围变量
- Angularjs:访问范围变量数组并计算平均值
- 范围变量更新,但不更新视图
- 从html调用angularjs控制器中的一个函数,但未定义范围变量
- 如何在编辑范围变量时(暂时)避免摘要循环
- 防止Angular范围变量通过引用自动绑定到服务私有成员
- 使用 AngularJS 从指令设置范围变量
- 集合回调忽略范围变量
- 使用forEach修改角度范围变量
- 因果报应测试中未定义的预期范围变量