澄清 JavaScript 变量范围

Clarification of JavaScript variable scope

本文关键字:范围 变量 JavaScript 澄清      更新时间:2023-09-26

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

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

假设我有一个函数,只需按一下按钮即可将输入字段的值增加 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