为什么匿名函数中的私有变量可以从 javascript 中的函数外部访问

Why private variables in an anonymous function are accessible from outside the function in javascript?

本文关键字:函数 javascript 访问 外部 为什么 变量      更新时间:2023-09-26

看了一本《基本JS设计模式》一书后,我无法理解这段代码中私有变量的行为:

var SingletonTester = (function () {
  // options: an object containing configuration options for the singleton
  // e.g var options = { name: "test", pointX: 5};
  function Singleton( options )  {
    // set options to the options supplied
    // or an empty object if none are provided
    options = options || {};
    // set some properties for our singleton
    this.name = "SingletonTester";
    this.pointX = options.pointX || 6;
    this.pointY = options.pointY || 10;
  }
  // our instance holder
  var instance;
  // an emulation of static variables and methods
  var _static  = {
    name:  "SingletonTester",
    // Method for getting an instance. It returns
    // a singleton instance of a singleton object
    getInstance:  function( options ) {
      if( instance  ===  undefined )  {
        instance = new Singleton( options );
      }
      return  instance;
    }
  };
  return  _static;
})();
var singletonTest  =  SingletonTester.getInstance({
  pointX:  5
});
// Log the output of pointX just to verify it is correct
// Outputs: 5
console.log( singletonTest.pointX );

这是一本书中关于单例模式的一个例子。
有一个匿名函数,它返回一个包含"name"成员的对象和"getInstance"方法,用于返回"Singleton"函数的实例。
我的问题是了解存储在SingletonTester中的对象如何访问"实例"私有var。我的意思是,在匿名函数完成其工作后,SingletonTester 变量应该只保存对象:

{
    name:  "SingletonTester",
    // Method for getting an instance. It returns
    // a singleton instance of a singleton object
    getInstance:  function( options ) {
      if( instance  ===  undefined )  {
        instance = new Singleton( options );
 }

并且此对象不知道实例是什么。"单例"函数的实例化也是如此。它如何知道在匿名函数范围内定义的"单例"是什么函数?

你正在经历的是JavaScript中的词法范围。基本上,每当您执行一个函数时,您都可以访问该函数的作用域。除此之外,您还可以访问定义函数的词法范围。

我们来看一个例子:

var someObject = (function () {
  var privateVariable = 18;
  var getValue = function () {
    return privateVariable;
  };
  return {
    getValue: getValue
  };
})();

对象 'someObject' 有一个方法 'getValue',它在执行时返回值 18。但这怎么可能呢?以下是 JavaScript 引擎在尝试执行 someObject.getValue() 时的工作方式:

  1. JavaScript引擎对someObject对象执行属性查找,并看到它有自己的getValue属性。
  2. 找到函数对象 (getValue) 后,它就会执行函数。
  3. 当函数被执行时,JavaScript引擎会尝试查找privateVariable的值。
  4. 但是,在"getValue"函数的范围内,privateVariable并不存在。
  5. 因此,JavaScript 引擎会查看 PARENT 作用域(即声明 getValue 函数的作用域)。
  6. 此作用域对应于匿名函数的作用域。在此范围内,privateVariable 的值为 18。

这就是 JavaScript 中的词汇范围。关于您的示例,这就是为什么 SingletonTester 的实例可以访问"实例"变量的原因。

我强烈推荐这本书来了解更多信息:范围和闭包