3

如果函数具有范围,则它们应该在该范围内执行,但在这里我认为它不同。请参阅代码

  function foo() {
    var privateVal = "Private Val";
    this.publicVal = "Public Val";

    var privateAlert = function (str) {
      alert(str + this.publicVal);
      alert(str + privateVal);
    }

    this.Run = function () //see here
    {
      privateAlert("Private Call: ");

      this.publicAlert = privateAlert;
      this.publicAlert("Public Call: ");

      privateAlert = this.publicAlert;
      privateAlert("Private Call: ");
      this.publicAlert("Public Call: ");
    }
  }

  var bar = new foo();
  bar.Run();

当创建新对象时,run() 成为对象的私有方法或仅属于 var bar 的方法。该方法不应该能够从其中执行 privateAlert() 函数,因为函数具有范围它只能从它已声明的函数中执行,但不能从现在属于某个其他对象的方法中执行。请澄清这一点。

4

3 回答 3

3

重要的是函数在哪里被实例化。在这种情况下,“运行”在“foo”构造函数中被实例化,并且在这种情况下,私有方法绝对是可见的。换句话说,它成为围绕“运行”功能的闭包的一部分。

可以这样想:“Run”函数的代码出现在“foo”构造函数中。“运行”中的代码可以“看到”“foo”中的所有局部变量,如果“foo”在另一个函数中,它也可以看到所有这些变量。因为该定义仅在调用“foo”时生效,所以所有这些局部变量将始终可用于“Run”中的代码,在以后调用它的任何时候。这就是术语“关闭”的含义。

JavaScript 中的范围和可见性与使用类的静态语言(如 Java、C++ 或 C#)的工作方式大不相同。在这些语言中,您显式地构造对象,然后通过这些对象引用开始调用函数。在 JavaScript 中,闭包有点像自动运行的对象,它们隐式地包裹在从函数调用中“逃脱”的函数周围。您不能直接访问这些对象,但它们仍然是真实的。

于 2013-01-03T16:09:39.240 回答
0

我猜 Raffaele 已经回答了你的大部分问题。但是,为了您的方便,让我尝试以不同的方式表达它并添加一些代码供您遵循。

我认为,您可以在这里学习两件事,以了解您的代码为何如此行事。

  1. JavaScript 具有后期绑定this。只有当您调用一个方法时,运行时才决定“打开”它将被调用的对象。默认是全局对象(例如,window在浏览器中、global在 node.js 中等)。要获得this除全局之外的另一个,您需要将该函数分配为所需对象的属性,就像您通过this.publicAlert = privateAlert;. 之后,如果您privateAlert()直接调用(在 global 上this)或者如果您在本地 this via 上调用它this.publicAlert(),即使它是相同的函数,它也会有所不同。

  2. 正如在另一个答案中已经指出的那样。Javascript 没有public,private等,您需要按照您使用智能范围界定的方式“建模”私有变量。在您的示例privateVal中,从某种意义上说,这是私有的,没有人可以从外部读取或写入它。这同样适用于privateAlert。只有通过构造函数中的语句Foorun您可以访问它们的方法。

我扩充了您的示例以向您展示发生了什么(ps: usingconsole.log通常比 更可取alert):

function Foo() {
    var privateVal = "Private Val";
    var that = this;
    this.publicVal = "Public Val";

    var privateAlert = function (str) {
        console.log("---" + str + "---")
        console.log("isLocalThis? ", this == that)
        console.log("isGlobalThis?", this == globalThis)
        console.log("private:     ", privateVal)
        console.log("public:      ", this.publicVal || "publicVal not available on 'this'")
    }

    this.run = function () {
        console.log("scope of run:", that, bar, privateAlert)
        console.log("isLocalThis? ", this == that)
        console.log("isGlobalThis?", this == globalThis)

        privateAlert("Private Call: ");

        this.publicAlert = privateAlert;
        this.publicAlert("Public Call: ");

        privateAlert = this.publicAlert;
        privateAlert("Private Call: ");
        this.publicAlert("Public Call: ");
    }
}

var bar = new Foo();
bar.run();

顺便说一句:consolealert是全局的属性this(即window)。

于 2013-01-03T18:18:51.553 回答
0

范围涉及名称系统,而没有提及访问权限。例如,在 Java 中,有三个关键字称为访问修饰符public和. Javascript 没有定义任何访问修饰符,因此所有名称都是公开的。privateprotected

让我们这样说:要从文件系统中读取文件,您需要两件事:

  1. 首先,文件名。当您请求操作系统给您一个文件指针时,它首先检查文件是否存在,可能是在从相对文件名和当前目录构造绝对路径名之后,然后,仅当文件存在时
  2. 它检查您是否有权从中读取,最后为您提供流的处理程序。

Javascript作用域确定名称在当前上下文中是否有意义的规则。如果名称有含义,您可以随时访问它,因为 Javascript 没有定义非公共修饰符。

在您的情况下,您请求访问bar.Run. Javascript 解释器使用当前上下文(我们假设它是全局对象)向“javascript 文件系统”询问“调用”对象.bar.Run(第一个点是有意的,表示根)。一旦找到名称,就意味着它存在,并且您将始终能够访问它(在这种情况下调用函数)。顺便说一句,没有什么能比得上javascript 文件系统,它只是在没有技术术语的情况下使用直观的类比来传达这个想法。

同样,检查范围规则以解析整个代码中的名称。在内部this.Run,名称privateAlert可以解析,这是唯一重要的规则:全局可以看到bar,全局可以访问bar.Run,分配给它的函数可以访问privateAlert。这是链条

于 2013-01-03T16:27:53.943 回答