JavaScript 嵌套命名空间和状态变量

JavaScript nested namespaces and state variables

提问人:Psyfun 提问时间:7/2/2022 最后编辑:Psyfun 更新时间:7/6/2022 访问量:115

问:

我正在尝试使用带有匿名函数和闭包的模块模式实现嵌套命名空间。当涉及到状态变量的变化时,我错过了一些东西。例如,下面是一组简单的嵌套命名空间:

// namespace 1
var first = (function(){
    'use strict';
    var v = 1;
    function method(){
      console.log("method 1: " + v);
    }
    // namespace 2
    var second = (function(){
      var v = 2;
      function method(){
        console.log("method 2: " + v);
      }
      // namespace 3
      var third = (function(){
        var v = 3;
        function method(){
          console.log("method 3: " + v);
        }
        return {
          v: v,
          method: method
        };
    })();
    
    return {
      third: third,
      v: v,
      method: method
    };
  })();
  
  return {
    second: second,
    v: v,
    method: method
  };
})();

first.method();
first.v = 4;
first.method();
first.second.method();
first.second.third.method();

这将导致以下输出:

"method 1: 1"
"method 1: 1"
"method 2: 2"
"method 3: 3"

为什么改变不坚持下去?first.v

更新:所以,我现在明白了为什么这不能根据反馈。我现在已经切换到使用对象文字。我仍在努力寻找嵌套命名空间和范围的最佳方法。这是我上面代码的新版本:

var first = {
  
  v: 1,
  
  method: function (){
    console.log("method 1: " + this.v);
  },
  
    second: {
    
    v: 2,
    
    method: function (){
      console.log("method 2: " + this.v);
    },
    
    third: {
    
        v: 3,
      
      method: function (){
        console.log("method 3: " + this.v);
      }
    }
  }
}

first.method();
first.v = 4;
first.method();
first.second.method();
first.second.third.method();

更新2:因此,在查看不同的资源时,我再次更新了我的方法。这是我首先尝试走的路线,但我一直收到一个奇怪的错误,即函数不是函数。这是我使用相同示例的更新版本:

var first = window.first || {};
first.second = first.second || {};
first.second.third = first.second.third || {};

first.v = 1;
first.method = function (data) {
    console.log("method 1: " + this.v);
};

first.second.v = 2;
first.second.method = function (data) {
    console.log("method 2: " + this.v);
}
first.second.third.v = 3;
first.second.third.method = function (data) {
    console.log("method 3: " + this.v);
}
first.method();
first.v = 4;
first.method();
first.second.method();
first.second.third.method();

现在我只想确保我有正确的范围。使用 时,它应该引用当前对象的作用域而不是全局,对吗?this

JavaScript 嵌套 命名空间

评论

0赞 Psyfun 7/2/2022
@Scott 马库斯,感谢您的澄清。我现在明白为什么这没有按预期工作。它适用于函数,但不适用于持久状态。我正在切换到仅使用对象文字。请参阅更新。
0赞 Scott Marcus 7/2/2022
请在下面查看我的答案。在函数中嵌套函数不是如何创建命名空间,而是创建方法链的方式。若要创建命名空间,必须在希望命名空间从中发出的对象上创建新属性(即对象)。
0赞 Scott Marcus 7/2/2022
要保持状态,您需要创建对象的实例(使用运算符),这是一个不同的主题/概念。new

答:

-1赞 Scott Marcus 7/2/2022 #1

命名空间不是由嵌套函数创建的。它们是通过在现有对象上创建新属性,然后将新成员附加到这些属性来创建的:

(function(){
  // Just create members of the instance as normal
  // No nesting here. Within a function variables are
  // private, but "this" properties are available
  // and can persist state.
  this.foo = function(){console.log("foo"); };
  this.bar = function(){console.log("bar")};
  this.baz = function(){console.log("baz")}; 
  this.x = 42;
  // We can't change the state of the above variable directly
  // but we can create a function that works with that value
  // and modifies it.
  this.changeX = function(n){
    this.x += n;
  };
  
  // If we create a new property on the Global object 
  // that is an empty object, we'll be essentially 
  // creating a namespace to be accessed from the Global object
  window.namespace = {};
  
  // Then, create new properties on that object, which point
  // to state or behavior that you want to expose in the namespace
  window.namespace.foo = foo;
  window.namespace.bar = bar;
  window.namespace.x = x;
  window.namespace.changeX = changeX;
  
  // And to create nested namespaces, repeat by adding a new empty
  // object as a new property of the current namespace.
  window.namespace.ns2 = {};
  // And then associate internal state or behavior with that namespace
  window.namespace.ns2.baz = baz;
})();

// Since our namespace is attached to the Global object (window),
// we don't have to fully qualify it with window and can just
// access the namespace directly.
namespace.foo();
namespace.bar();
namespace.ns2.baz();
console.log(namespace.x);  // 42
namespace.changeX(-10);
console.log(namespace.x);  // 32

评论

0赞 Bergi 7/2/2022
IIFE 不是一个实例。在严格模式下,将未定义。this
0赞 Scott Marcus 7/2/2022
@Bergi 谢谢。更正了措辞。我知道这不是一个实例。我试图传达使用(不是在严格模式下,因为从未提到过)将区分局部变量和暴露的成员。this
0赞 Bergi 7/2/2022
你是想鼓励在这里使用草率模式吗? 在IIFE中不是指“暴露的成员”,而是指全局对象的成员,应绝对避免。this