8

我了解“js”中的函数具有词法范围(即,函数在定义时而不是在执行时创建它们的环境(范围)。)

function f1() {
    var a = 1;
    f2();
}

function f2() {
    return a;
}
f1(); // a is not defined

当我只运行“f()”时,它会返回内部函数。我明白了,这就是'return'的作用!

function f() {
    var b = "barb";
    return function() {
        return b;
    }
}
console.log(b); //ReferenceError: b is not defined

为什么会出现“ReferenceError:b 未定义?” 但是上面的内部函数不能访问它的空间、f() 的空间等。因为'b' 被返回到全局空间,console.log() 不会工作吗?

但是,当我将 'f()' 分配给一个新变量并运行它时:

 var x = f(); 
 x();// "barb"
 console.log(b); //ReferenceError: b is not defined

这将返回“b”,即“barb”,但是当您再次运行 console.log() 时,您将得到 'ReferenceError: 'b' is not defined';自从它被返回后,'b' 现在不是在全局范围内了吗?那么为什么 'x()' 不像 'f()' 一样返回内部函数呢?

4

4 回答 4

57

你,我的朋友,完全糊涂了。您的第一句话本身就是错误的:

函数在定义时而不是在执行时创建它们的环境(范围)

其实恰恰相反。定义函数不会创建范围。调用函数会创建一个作用域。

什么是范围?

简单来说,作用域就是变量的生命周期。你看,每一个变量都是诞生、生和死的。作用域的开始标志着变量的诞生时间,作用域的结束标志着它死亡的时间。

一开始只有一个范围(称为程序范围或全局范围)。在这个范围内创建的变量只有在程序结束时才会消失。它们被称为全局变量。

例如,考虑这个程序:

const x = 10;       // global variable x

{                   // beginning of a scope
    const x = 20;   // local variable x
    console.log(x); // 20
}                   // end of the scope

console.log(x);     // 10

在这里,我们创建了一个名为 的全局变量x。然后我们创建了一个块作用域。在这个块范围内,我们创建了一个局部变量x。由于在我们登录时局部变量会影响全局变量,x因此我们得到20. x当我们登录时回到全局范围10(本地x现在已经死了)。

块作用域和函数作用域

现在编程中有两种主要的作用域类型——块作用域和函数作用域。

上一个示例中的作用域是块作用域。这只是一段代码。由此得名。块作用域立即执行。

另一方面,函数作用域是块作用域的模板。顾名思义,函数作用域属于函数。但是,更准确地说,它属于函数调用。在调用函数之前,函数作用域不存在。例如:

const x = 10;

function inc(x) {
    console.log(x + 1);
}

inc(3);         // 4
console.log(x); // 10
inc(7);         // 8

正如您所看到的,每次调用函数时都会创建一个新范围。这就是你得到输出的原因4108

最初,JavaScript 只有函数作用域。它没有块作用域。因此,如果你想创建一个块作用域,那么你必须创建一个函数并立即执行它:

const x = 10;         // global variable x

(function () {        // beginning of a scope
    const x = 20;     // local variable x
    console.log(x);   // 20
}());                 // end of the scope

console.log(x);       // 10

这种模式称为立即调用函数表达式 (IIFE)。当然,现在我们可以使用constand创建块范围的变量let

词法作用域和动态作用域

函数作用域又可以有两种类型——词法作用域和动态作用域。你看,在一个函数中有两种类型的变量:

  1. 自由变量
  2. 绑定变量

在范围内声明的变量绑定到该范围。未在作用域内声明的变量是免费的。这些自由变量属于其他范围,但属于哪个范围?

词法作用域

在词法作用域中,自由变量必须属于父作用域。例如:

function add(x) {         // template of a new scope, x is bound in this scope
    return function (y) { // template of a new scope, x is free, y is bound
        return x + y;     // x resolves to the parent scope
    };
}

const add10 = add(10);    // create a new scope for x and return a function
console.log(add10(20));   // create a new scope for y and return x + y

与大多数编程语言一样,JavaScript 具有词法作用域。

动态范围

与词法作用域相反,在动态作用域中,自由变量必须属于调用作用域(调用函数的作用域)。例如(这也不是 JS - 它没有动态范围):

function add(y) {   // template of a new scope, y is bound, x is free
    return x + y;   // x resolves to the calling scope
}

function add10(y) { // template of a new scope, bind y
    var x = 10;     // bind x
    return add(y);  // add x and y
}

print(add10(20));   // calling add10 creates a new scope (the calling scope)
                    // the x in add resolves to 10 because the x in add10 is 10

就是这样。简单吧?

问题

您的第一个程序的问题是 JavaScript 没有动态范围。它只有词法作用域。看到错误了吗?

function f1() {
    var a = 1;
    f2();
}

function f2() {
    return a;
}

f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)

您的第二个程序非常混乱:

function f() {
    var b = "barb";

    return function() {
        return b;
    }
}

console.log(b); //ReferenceError: b is not defined

以下是错误:

  1. 你从来没有打电话fb因此,永远不会创建变量。
  2. 即使您调用f该变量b也将是本地的f

这是你需要做的:

function f() {
    const b = "barb";

    return function() {
        return b;
    }
}

const x = f();

console.log(x());

当你调用x它时返回b。然而,这并不能成为b全球性的。要b全球化,您需要这样做:

function f() {
    const b = "barb";

    return function() {
        return b;
    }
}

const x = f();
const b = x();
console.log(b);

希望这有助于您了解范围和功能。

于 2013-06-24T17:15:50.907 回答
5

你会得到,“ReferenceError: b is not defined”,因为“b”没有在你的console.log()调用位置定义。该函数内部有一个“b” ,但外部没有。您关于“b 正在返回到全局空间”的断言是错误的。

当您调用“f()”函数返回的函数时,它将返回该闭包变量“b”引用的值的副本。在这种情况下,“b”总是那个字符串,所以函数返回那个字符串。它不会导致符号“b”成为全局变量。

于 2013-06-24T15:40:30.660 回答
2

但是上面的内部函数不能访问它的空间、f() 的空间等吗?

是的,它有。它访问b变量并从函数返回其值

作为那个'b'正在返回到全局空间

不。从函数返回值不是“使变量在调用者范围内可用”。调用函数(with f())是一个表达式,其结果是函数返回的值(在你的例子中,未命名的函数对象)。然后可以将该值分配给某个地方(到x),可以访问它的属性,也可以丢弃它。

但是,该变量b在声明它的范围内保持私有。它没有在您调用的范围内 [getting] 定义console.log,这就是您收到错误的原因。

你想要的似乎是

var x = f();
var b = x(); // declare new variable b here, assign the returned value
console.log( b ); // logs "barb"
于 2013-06-24T15:41:06.373 回答
2
    function f1() {
          var a = 1;
          f2();
          }

    function f2() {
       return a;
    }
    f1(); // a is not defined
  1. f2(); 不知道 a,因为您从未将 'a' 传递给它,(这是在定义函数时创建的作用域)。如果函数 f2() 在 f1() 中定义,则可以访问它; [函数可以访问“定义”而不是“调用”的同一范围内的变量]

    function f() {
       var b = "barb";
       return function(){
                         return b;
                        }
    }
    console.log(b); 
    
  2. 首先你需要调用 f(); 执行 f() 后;它将返回另一个需要执行的函数。IE

    var a=f();
    a();
    

    这将导致 "barb" ,在这种情况下,您将返回一个函数而不是 var b;

    function f() {
       var b = "barb";
       return b;
                 };
    
    console.log(f());
    

    这将在屏幕上打印倒钩

于 2014-02-03T14:24:30.087 回答