JavaScript静态/单例模式 - this vs _this vs 对象名称

4
这是一个关于性能和最佳实践的问题。
假设我有一个js对象,它封装了大量的辅助方法。该对象被视为静态类,意味着它从未被实例化,而且所有的方法都是基本上的辅助方法。 在使用事件和jQuery时,对象的this作用域会不断变化,由于它具有相当多的方法,我想知道什么是最佳实践 - 在每个方法的开头将this保存到_this中,还是直接使用对象名称MyObject
多年来,对于Singleton / Static对象,我都在两者之间进行选择,但我认为必须有一种最佳实践,并且现在是采用最佳方法的时候了。
我的当前研究显示,使用_this而不是直接调用MyObject带来的好处主要有以下两点:
  • 如果对象的名称更改,_this始终有效
  • 可能更快(虽然没有看到性能测试结果),因为浏览器保持在同一范围内,无需每次查找MyObject的范围。
使用MyObject的优点:
  • 写的代码更少。
  • 垃圾收集管理?(分配的变量较少)
  • 对于某些开发人员来说,可能更易读(其中多个this适用)
  • 更容易重构代码,因为MyObject始终有效
我想知道是否有一种全局保存_this的方法(当然仅在对象的范围内),并避免在每个方法的开头进行分配。如果没有 - 我是否遗漏了其他优缺点或者是否直接调用对象名称被认为是不良实践。
这是一个简化的对象供参考(真正的对象有更多的方法)。
    var MyObject = {
       init: function() {

         this.registerEvents();
         //other stuff

       },
       registerEvents: function() {

         this.registerOnSomeEvent();
         //registering more events..
         //...

       },

       registerOnSomeEvent: function() {
          var _this = this;
          jQuery('#someid').click(function() {
             _this.doSomething();//option 1
             MyObject.doSomething();//option 2
          });
       },

       doSomething: function() {
         //doing something...
       }
    }

MyObject.init();

感谢您的帮助!
2个回答

5
您可以将整个对象封装在闭包中,以达到无需在每个函数中指定_this的目的:
window.MyObject = (function () {
    var self = {
        init: function() {

            self.registerEvents();
            //other stuff

        },
        registerEvents: function() {

            self.registerOnSomeEvent();
            //registering more events..
            //...

        },

        registerOnSomeEvent: function() {
            jQuery('#someid').click(function() {
                self.doSomething();//option 1
                self.doSomething();//option 2
            });
        },

        doSomething: function() {
            //doing something...
        }
    };

    self.init();

    return self;
})();

我喜欢这个解决方案,但由于我们再次使用 self 作为对象名称,所以性能上真的比直接调用 MyObject 有很大区别吗? - dev7
1
@Yani 说实话,除非你每秒访问那个变量1000次甚至百万次,否则这并不重要。你应该优先考虑可读性和减少错误。 - Esteban Felix
1
@Yani,性能上没有实质性的差别。与对全局变量的嵌入式引用相比,这种方法的主要优点在于self不会改变,而全局变量可能会改变。 - Pointy
感谢 @EstebanFelix 的澄清。 - dev7
也感谢 @Pointy。 - dev7

3

我认为你的问题在于,你费尽心思地模拟了一些本应该直接做的事情。

const mySingleton = (function () {
    // Instance stores a reference to the Singleton 
    var instance;
    function init() {
        // Singleton
        // Private methods and variables
        var privateVariable = "Im also private";
        var privateRandomNumber = Math.random();

        return {
            // Public methods and variables
            publicMethod: function () {
                console.log( "The public can see me!" );
            },
            publicProperty: "I am also public",
            get randomNumber() {
                return privateRandomNumber;
            }
         };
         function privateMethod(){
            console.log( "I am private" );
         }

     };
     return {
         // Get the Singleton instance if one exists
         // or create one if it doesn't
         get instance() {
             if ( !instance ) instance = init();
             return instance;
         }
     };   
})();

如果您不想使用this上下文,不想使用继承,并且永远只有一个实例,那么不要将这些东西作为对象的方法编写,而是在单例模式中声明为私有方法(这是一种揭示模块模式,但只有一个实例)。
因为现在你基本上正在制作完全相同的东西,但是你正在暴露所有内容,并且毫无目的地大量使用this。根据设计,this并不是常量,请不要将其用作常量。

你说得对,因为我来自服务器端语言,所以我试图模仿静态类的行为。我不知道你描述的+1是可能的。然而,这并没有回答如何处理事件和jQuery,因为作用域会改变的问题。 - dev7
作用域永远不会改变。使用这种模式,任何东西都将100%引用相同的内容。上下文(this)会改变,但作用域永远不会改变。但是您根本不需要担心this,因为在此模式中根本不会使用它。 - Winchestro
1
如果你还没有的话,可以查看http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/。这是一本免费在线书籍,详细介绍了(当然)JavaScript设计模式,包括揭示模块模式。 - Gary Storey
@GaryStorey 猜猜我现在打开了什么来检查我是否说了些蠢话(结果是我确实说错了,单例模式有点不同,我会更新我的答案=) - Winchestro
@Winchestro,感谢您的回答。我之前使用过一个稍微不同版本的这个模式,但我不确定如何在jQuery事件中调用私有/公共方法。您会简单地使用mySingleton.methodName()还是instance.methodName() - dev7
@Yani,您只需要将引用传递给事件处理程序中的函数即可。yourElement.on("something", yourSingleton.instance.doSomething)(按照您编写的方式,它将调用方法,但您希望将其传递。) - Winchestro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接