【问题标题】:Javascript module pattern - losing scope of thisJavascript 模块模式 - 失去这个范围
【发布时间】:2012-02-17 02:38:12
【问题描述】:

一段时间以来一直在研究 js 模块模式,以满足“某些网站”不断升级的需求。本质上,我只需要一种对脚本进行分组/封装的好方法,以及有时对 OO 模式的需求。

我的结构可以正常工作,但我对它的某些部分不满意......所以我正在重构它。这是更新的模式:

(function (global) {
    var M = {
        VERSION : 1.0
    };

    global.M = M;

    //jQ document.ready()
    global.onload = function(){
        console.log('M VERSION: %s', M.VERSION);
        var main = new M.Main();
    };

    M.Main = function(){
        var _class1;
        var _class2;
        var _class3;

        function _init(){
            _class1 = new M.Class('foo','baz');
            console.log('_class1.param2 : %s', _class1.getParam() ); //works

            _class2 = new M.OtherClass('faz','boo');
            _class2.setParam('Boozaz');
            console.log('_class2.param2 : %s', _class2.getParam() ); //works

            _class3 = new M.Class('fuz','boz')
            console.log('_class3.param2 : %s', _class3.getParam() ); //works

            _class3.prototype = new M.Super();
            console.log('_class3.__param : %s', _class3.prototype.getProtected() ) //works
        }

        _init();
        return true;
    };

    M.Super = function(){
        var __param = 'jQ';
        M.Super.API = {
            getProtected : function(){ return __param }
        }
        return M.Super.API;
    }

    M.Class = function( p1, p2){
        var _param1;
        var _param2;

        function _init(){
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
            publicMethod : function(){  ...  }
        }

        publicMethod() //fails
        this.publicMethod() //fails, this scopes to DOM window
        M.Class.API.publicMethod() // works, but is kludgy

        _init();
        return M.Class.API;
    };

})(typeof window === 'undefined' ? this : window);

这会产生令人满意的 DOM 结构(通过 firebug 进行检查) - 但我在一个特定区域中失去了 this 的范围 = 在内部调用返回对象的“公共”方法。

publicMethod() //fails
this.publicMethod() //fails, this scopes to DOM window
M.Class.API.publicMethod() // works, but kludgy syntax

在该模式的前一次迭代中,“类”对象是自执行的,并且对this 的引用保持不变:

    M.Class = function( p1, p2){
        var _param1;
        var _param2;
        var _root; 

        function _init(){
            _root   = this; //this gets referenced for later usage
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            init     : function(){ _init();    },
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
        }

        console.log('getParam, internal :%s', _root.getParam() ) //success
        return M.Class.API;
    }();

  M.Class.init();

然而,在重构模式中,我希望通过new 实例化这些“类”,以获得对执行顺序的更多控制。

我已经阅读了很多很多关于 js 中的词法范围这个相当令人麻木的主题的文章......但没有得出任何结论。

我应该如何在更新的模块模式中维护this 的范围?

【问题讨论】:

  • 说 1000 次:这不是作用域,它是调用设置的特殊局部变量(或 ES5 bind)。
  • 不确定this 的范围是window 而不是M.Class。一个明显的解决方案(但对您来说可能不可行)是在 M.Class 中声明一个包装函数来为您调用 M.Class.API 的等效函数。如果您尝试设置 thispublicFunction 中引用的内容,请查看 call()apply()
  • 如何在M.Class 中使用M.Class?它应该是未定义的,不是吗?
  • @Nemoden 不,它应该在其中定义。函数可以引用自己。 jsfiddle.net/DgBaa/1

标签: javascript scope code-design


【解决方案1】:

这是每个人在编写库或模块时都会问的哲学问题之一:函数应该使用 this 还是变量名来引用容器对象?答案是:视情况而定。

如果您知道函数将始终使用正确的 this 值调用(例如,原型上的方法),请使用 this。但是,如果函数可能以任何其他方式调用,则使用变量名。如果您在稍后阶段决定更改名称,这是一个非常简单的搜索和替换练习。并且调用 myLib.utils.someFn 比调用 this.someFn 更清晰。如果您觉得输入太多,您可以随时回复到 var sF = myLib.utils.someFn 并从那里开始。

编辑

回答您的问题:

publicMethod() //fails

当前作用域内没有publicMethod标识符,会失败。

     this.publicMethod() //fails, this scopes to DOM window

如果调用是 M.Class(),则 this 是对 M 的引用。如果你得到 window,那么你是在以其他方式调用函数。

     M.Class.API.publicMethod() // works, but is kludgy  

因为您就是这样设置的。如果您不喜欢它,请以其他方式进行设置。

最后:

)(typeof window === 'undefined' ? this : window);

似乎是那些似乎在网络上扩散的神秘咒语之一。什么目的?如果意图是将全局对象的引用传递给函数,那么:

)(this);

在任何地方都足够了。上面的目的是确保函数具有对全局对象的引用,因为引用 window 可能会解析为其他对象。包括可能会或可能不会在全局对象中传递的逻辑似乎是一个倒退。在什么情况下最好引用全局对象的(可能重新分配的)window 属性而不是全局对象本身?

【讨论】:

  • function 有多个“实例”会有所不同。直接使用名称类似于(但不完全相同)在类上以 OO 语言声明静态成员。
  • 如果有多个实例,自然应该设置对象属性,以便 this 引用该实例。然而,这一切都可以追溯到函数的调用方式——你只能保证 this 的值在你控制调用的地方。
  • @RobG - 感谢您的回复。有趣和令人沮丧的是,在构建 js 时,哲学发挥了多大作用。做一件事的许多有效方法......无论如何,我完全明白你在说什么。我认为可能有意义的是在适用时使用这两种模式。这种架构中的一些“类”毕竟只需要一个实例。至于你的个人观点 - 我很惊讶在第二种情况下“this”解析为“Window”(发布的代码只是从我的实时代码中略微修改)也许我有一些电线交叉......
  • @RobG ...另外 - 你的代码中有什么不喜欢的神秘咒语。每个看到它的人(尤其是你的老板)都会对自己说“哇。你确实是个聪明的家伙”。哈哈。你的观点很好。我想我很久以前从一些帖子中刮掉了它,并且在设置初始对象时似乎是一个很好的故障保护。但你是对的——在那种情况下,“这个”到底指的是什么?干杯...
  • 哈!是的,人们喜欢这些东西,它们太性感了。直到有人问“为什么”。 :-)
【解决方案2】:

如果除了您通过M.Class.API 手动公开的方法之外,您的所有方法都是“私有的”怎么办?

function _getParam(){
  return _param2;
}

function _setParam(e){
  _param2 = e;
}

function publicMethod(){
  console.log("public method");
}

M.Class.API = {
  getParam : _getParam,
  setParam : _setParam,
  publicMethod : publicMethod
}

publicMethod();              // succeeds
this.publicMethod();         // still fails
M.Class.API.publicMethod();  // still works, still is kludgy

您还应该注意,使用 new 关键字调用该函数时,从函数返回匿名对象可能会产生意想不到的后果。见this Stack Overflow question

【讨论】:

  • 附加链接上的有趣点。出于这个原因,我很少在 JS 中进行自定义类型检查(而是更繁琐的道具检查,例如 'is obj typeof 'object' && obj.hasOwnProperty('someProp') && obj['someProp'] != 'undefined' ' . 无论如何 - 只调用“私有”方法的“公共”方法当然可以工作,并且可能包含在我的最终重构中......干杯!
猜你喜欢
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-24
  • 1970-01-01
  • 2014-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多