Javascript中的内部函数范围

And*_*897 4 javascript closures

文章中,我不明白下面的语句:

注意,内部函数不能调用外部函数的arguments对象,但是,即使它可以直接调用外部函数的参数.

有人可以帮我理解上面提到的两点

  1. 内部函数不能调用外部函数的参数对象
  2. 内部函数可以直接调用外部函数的参数.

T.J*_*der 7

理解这两个陈述的关键是:

  1. 了解什么arguments

  2. 理解"阴影"

那么让我们谈谈arguments,然后讨论阴影,然后我认为这两个陈述试图说的是相当清楚的.

arguments

arguments对象作为函数中作用域的一部分提供:它实际上是一个自动定义的变量,它具有调用函数的参数的伪数组:

function foo() {
    console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
Run Code Online (Sandbox Code Playgroud)

当我说自动定义时,我的意思是它非常像你有这个:

// CONCEPTUAL, not real
function foo() {
    var arguments = /*...magic code to get the arguments we were called with */;
    console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
Run Code Online (Sandbox Code Playgroud)

...这只是JavaScript引擎为您完成的.

arguments对象很像一个数组 - 正如我们在上面看到的那样,它有length,并且您可以使用通常的[]表示法访问各个参数:

function foo() {
    var n;
    console.log("My arg count: " + arguments.length);
    for (n = 0; n < arguments.length; ++n) {
        console.log(arguments[n]);
    }
}
foo(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

输出:

My arg count: 3
1
2
3

您甚至可以arguments在同一函数中使用命名参数和伪数组:

function foo(a) {
    console.log("a = " + a);
    console.log("arguments[0] = " + arguments[0]);
}
foo("Hi there");
Run Code Online (Sandbox Code Playgroud)

输出:

a = Hi there
arguments[0] = Hi there

这是一个伪阵列,因为虽然它有length[]索引,它不是一个真正的数组,没有所有的功能正常阵列具有(如slice,forEach等).

在松散模式下,它还具有非常令人惊讶的属性,它命名参数相关联,因此您会得到类似这样的奇怪内容:

function foo(a) {
    console.log("before: a = " + a);
    arguments[0] = a * 2;
    console.log("after:  a = " + a);
}
foo(10);
Run Code Online (Sandbox Code Playgroud)

输出:

befer: a = 10
after: a = 20

诡异,嗯?在严格模式下,该链接不存在(因为发动机执行此操作的成本很高).

好的,那就是arguments.什么是"阴影"的东西?

阴影

函数为变量创建"范围",并且存在于其中,因为您可以在其他函数中包含函数,所以可以在范围内包含范围.其他函数内的函数可以访问包含它们的作用域,因此:

function outer() {
    var a = 10;

    // `a` exists here

    function inner() {
        var b = 20;

        // both `a` and `b` exist here

        console.log("a = " + a + ", b = " + b);
    }

    // only `a` exists here

    inner();
}
Run Code Online (Sandbox Code Playgroud)

a存在于范围outer,其inner可以访问.b只存在于inner.

只要我们使用不同的名称,那就没问题,但假设我们使用a了两个名称:

function outer() {
    var a = 10;

    // `a` exists here

    function inner() {
        var a = 20;

        // `a` exists here -- but which one is it?

        console.log("a = " + a);
    }

    inner();
}
Run Code Online (Sandbox Code Playgroud)

在那里,因为inner有自己的a变量,它不能访问outera了- innera 阴影(隐藏)outera.

对于变量,命名参数,在范围内创建的函数,以及在范围内赋予名称含义的任何内容,都是如此.

那么阴影与这两个陈述有什么关系呢?我们来看看......

"1.内部函数不能调用外部函数的参数对象"

"呼叫"在这里是错误的词; "访问"会更有意义.

还记得我们上面的概念示例,其中arguments基本上是一个自动声明的变量吗?

// CONCEPTUAL, not real
function foo() {
    var arguments = /*...magic code to get the arguments we were called with */;
    console.log("My arg count: " + arguments.length);
}
foo(1, 2, 3); // My arg count: 3
Run Code Online (Sandbox Code Playgroud)

现在您已了解阴影,您可能会看到它的发展方向.这段代码:

function outer() {
    function inner() {
        console.log("Inner arg count: " + arguments.length);
    }
    console.log("Outer arg count: " + arguments.length);
    inner();
}
outer(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

节目

Outer arg count: 3
Inner arg count: 0

...因为在里面inner,arguments指的arguments是内部函数的对象,而不是外部函数.它就像它们都声明了一个具有相同名称的局部变量:inner 阴影中的变量(隐藏)中的变量outer.

现在,"内部函数不能调用外部函数的参数对象"并不是真的.内部函数可以访问外部函数的arguments对象,但不能访问该名称 - 如果outer将其赋值给变量,则没有问题:

function outer() {
    var outerArgs = arguments;
    function inner() {
        console.log("Outer's arg count from inner: " + outerArgs.length);
    }
    console.log("Outer arg count: " + arguments.length);
    inner();
}
outer(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)

节目

Outer arg count: 3
Outer's arg count from inner: 3

"2.内部函数可以直接调用外部函数的参数."

这个陈述是不完整的:提供的inner访问outer参数不会影响它们.所以这很好:inner

function outer(a) {
    function inner() {
        console.log("Outer's a = " + a);
    }
    inner();
}
outer(10);
Run Code Online (Sandbox Code Playgroud)

输出:

Outer's a = 10

......但是在这里,a被遮蔽,所以inner只看到自己的a,而不是outer:

function outer(a) {            // <== declares a named argument `a`
    function inner(a) {        // <== ALSO declares a named argument `a`
        console.log("a = " + a);
    }
    inner();
}
outer(10);
Run Code Online (Sandbox Code Playgroud)

输出:

a = undefined

实际上,这两个陈述都只涉及范围和阴影.


Eth*_*own 3

在每个函数的主体中,都有一个特殊的类似数组的对象,arguments它包含调用函数时使用的所有实际参数。例如:

function f() {
    console.log(arguments);
}

f('one', 2, 3.0);
Run Code Online (Sandbox Code Playgroud)

大多数语言会要求您声明这些参数,但 JavaScript 并不关心您使用了多少个参数。

由于在任何给定时间(最多)范围内只有一个arguments变量,因此您无法arguments从封闭函数访问该变量是有道理的:

function f() {
    function g() {
        console.log(arguments);  // will log ['blue'], not ['red']
    }
    g('blue');
}

f('red');
Run Code Online (Sandbox Code Playgroud)