当我将 jquery dom 操作方法放入命名空间时,“this”没有按预期解决?

Posted

技术标签:

【中文标题】当我将 jquery dom 操作方法放入命名空间时,“this”没有按预期解决?【英文标题】:"this" doesn't resolve as expected when I put my jquery dom manipulation methods in to a namepsace? 【发布时间】:2011-08-21 17:45:59 【问题描述】:

迄今为止,我已经设法避免在我的大多数 javascript 开发中使用命名空间,但通过阅读像 this one 这样的有用文章,我开始看到了曙光

我正在按照 Maeagers 技术 found here 创建我的命名空间,如下所示 -

var newAndImproved = 

//div to put loaded content into
targetDiv: "",

//loads the initial content based on an anchor tag having a class named 'chosen'
loadInitialPage: function() 
    var chosenLink = $("#subnavigation a.chosen");
    newAndImproved.loadPartial(chosenLink);
,

loadPartial: function(clickedLink) 
    var theUrlToLoad = $(clickedLink).attr("href");
    $(newAndImproved.targetDiv).load(theUrlToLoad, function(response, status, xhr) 
        if (status == "error") 
            var msg = "Sorry but there was an error: ";
            $(targetDiv).html(msg);
        
    );
,

removeClassFromSubNav: function() 
    $("#subnavigation a").removeClass('chosen');
,

addChosenClassToNav: function(chosenLink) 
    $(chosenLink).addClass('chosen');
,

bindMethodsToNavigation: function() 
    $("#subnavigation a").bind('click', function(event) 
        var chosenLink = event.target;
        newAndImproved.removeClassFromSubNav();
        newAndImproved.loadPartial(chosenLink);
        newAndImproved.addChosenClassToNav(chosenLink);
        return false;
    );


;

我这样称呼那个命名空间 -

$(document).ready(function() 
newAndImproved.targetDiv = $("#subcontent");
newAndImproved.loadInitialPage();
newAndImproved.bindMethodsToNavigation();
);

我确定您已经注意到我在其内部引用命名空间,而不仅仅是使用“this”。我认为这是不正确的。但是当我使用“this”时,绑定部分将不起作用。

奇怪的是,loadInitialPage 方法在使用 'this' 时会起作用。

知道我做错了什么吗?

提前致谢。

【问题讨论】:

@Rudie - “命名空间”是一种发明,是一种使用 Javascript 对象来隔离或组织函数的模式。因为命名空间(根据上面说明的编码模式定义)只是一个对象,实际上“命名空间”中有一个this 该死,你是对的......我删除了我令人困惑的评论。 【参考方案1】:

您需要使用that 技巧。 this 在新范围内重新分配,包括您正在绑定的匿名函数。

考虑这段代码:

function say(x) alert(x); 

var myscope = 

    label : "myscope (toplevel)",

    func1 : function() 
        say("enter func1");
        var label = "inner func1";
        say("  this.label: " + this.label);
        say("  label: " + label);
    ,

    func2 : function() 
        var label = "inner func2";
        say("enter func2");
        this.func1();
    
;

myscope.func2();

如果你运行它,它会表现得很好,并且对this.xxx 的引用都会如你所愿地成功。但是,如果您添加这样的 func3:

    func3 : function() 
        setTimeout( function()
            var label = "anonymous func";
            say("enter anon func");
            this.func1();
        , 2);
    

...它不会像你想象的那样工作。因为匿名函数没有在明确指定的范围内定义,所以其中的this 指的是***对象window。并且没有func1 作为窗口的子级(没有名为func1 的***对象)。

出于同样的原因,您在调用bind() 时使用的匿名函数中的this 将失败。在这种情况下,您可以直接引用“命名空间”对象,也可以使用闭包(有些人称之为“that 技巧”):

    bindMethodsToNavigation: function() 
        var that = this;
        $("#subnavigation a").bind('click', function(event) 
            var chosenLink = event.target;
            that.removeClassFromSubNav();
            that.loadPartial(chosenLink);
            that.addChosenClassToNav(chosenLink);
            return false;
        );
    

bindMethodsToNavigation范围内的this指的是newAndImproved。匿名函数中的this 将引用window。使用闭包可以让你引用你想要的东西。 (社论评论:我个人觉得 that 这个名字很可爱,而且完全没有帮助。我喜欢使用缩写名称,在你的情况下,可能类似于 nai 来指代 newAndImproved

顺便说一句,你也可以这样做:

    clickHandler : function(event) 
        var chosenLink = event.target;
        this.removeClassFromSubNav();
        this.loadPartial(chosenLink);
        this.addChosenClassToNav(chosenLink);
        return false;
    ,

    bindMethodsToNavigation: function() 
        $("#subnavigation a").bind('click', clickHandler);
    ,

...如果它们都是newAndImproved 对象的成员,那么clickHandler 中的this 将被解析为newAndImproved 对象。这里的关键是clickHandler 不是匿名的***函数;它属于newAndImproved,当在其中使用this 时,它会正确解析。更重要的是,您可以在clickHandler 中放弃使用this;这不是不正确的,但它是不必要的。有些人喜欢添加它以强调那些以后可能会阅读代码的人。


编辑

关于这一点的另一个注意事项 - 关于是否使用匿名函数或命名函数作为事件处理程序的决定......匿名函数非常方便和容易,每个人都使用它们。我发现在 Firebug 或 IE Javascript 调试器中,当我查看堆栈跟踪时,我会得到一个很长的匿名函数列表,并且很难弄清楚函数链是什么。尤其是 AJAX 和异步处理程序被触发以响应 UI 事件等等。为了在调试时提供一些额外的可见性,我有时使用命名函数(放入命名空间),调用堆栈将显示一些命名 fns 以及穿插一些匿名 fns。这有时会有所帮助。 不过,这只是一件小事。

【讨论】:

感谢 Cheeso,它工作得很好并且解释得很好。作为一个附带问题,您认为我应该将此代码包装到一个单独的名称空间中还是应该将其全部粘贴到 $(document).ready() 中?我认为我根本不会重复使用它。但是我可以看到的一个好处是我可以使用命名空间来使用 firebugs 控制台调用函数,而当它在 $(document).ready() 中时我无法做到这一点。 嘿,我认为这是偏好和风格的问题。 Javascript 相当松散,每个人似乎都有自己的约定。见证与其他一切不同的 jQuery。就我个人而言,我不喜欢用我将使用一次的函数来混淆顶层范围,所以我倾向于使用“命名空间”类型的组织。也许 Rudie 会称我为命名空间“fanboy”。【参考方案2】:

我根本不认为需要命名空间。这看起来像是一个非常具体的实现(而不是可重用的类)。这不是更简单吗:

$(document).ready(function() 
    var targetDiv = $("#subcontent"),
    loadInitialPage = function() 
        ...
    ,
    loadPartial = function(clickedLink) 
        ...
    ,
    removeClassFromSubNav: function() 
        ...
    ,
    addChosenClassToNav = function(chosenLink) 
        ...
    ,
    bindMethodsToNavigation = function() 
        ...
    ;

    loadInitialPage();
    bindMethodsToNavigation();
);

更简单,因为您现在可以引用所有没有命名空间的函数。不需要this.namespacename.

为什么要将命名空间/函数声明与 document.ready 东西分开?

编辑You should read this.不止几行,但可读性很强,很有用。

编辑document.ready里面放很多东西没问题,因为只执行一次。

【讨论】:

没错,考虑到简单的场景,拥有命名空间并没有真正的好处。但是,想象一下他定义了不同的 UI 逻辑包;一种可能简单,另一种可能更复杂。在这种情况下,您希望有多个对象来隔离这些东西。我的意思是,可以想象命名空间可能有用的情况,即使在$(document).ready() 中也是如此。 我认为每个库都应该有 1 个命名空间,但更多的是自命不凡、不必要且可读性较差的 IMO。当我创建一个新类时,我将它放在我的个人或应用程序的命名空间中。我制作的大多数函数(到目前为止)都是这些类的非常具体的实现,因此不必对它们进行命名空间。 edit 我认为最终归结为可读性:随心所欲。在性能方面,它的影响很小(现在使用 JS 引擎)。 @Rudie - 我同意您的评论,即我提供的代码中可能不需要名称空间,并且迄今为止我已经避免在简单的情况下这样做。但是,我假设我没有通过避免命名空间来使用最佳实践。我可能不得不回到基础,看看我应该在何时何地使用它们。 我认为这可能很聪明。即使是命名空间迷也不会一直使用它们。就个人而言,我很少在 JS 中使用它们。然而,我确实利用了 JS 的灵活性:函数是一个函数、一个对象、一个命名空间和一个类。有时这很好知道 =) 是的。 newAndImproved 是一个对象。 jQuery 就是一个很好的例子。 jQuery 是一个函数(jQuery('a.foo'))和一个命名空间(jQuery.settings.bar)和一个类(new jQuery(Sizzle('a.foo')),它是一个对象,因为 JS 中的一切都是。

以上是关于当我将 jquery dom 操作方法放入命名空间时,“this”没有按预期解决?的主要内容,如果未能解决你的问题,请参考以下文章

RequireJS + jQuery 命名空间问题

提升序列化和命名空间

jQuery插件开发:jQuery类方法

jQuery复习—DOM无关的jQuery实用函数

jQuery mobile:当我将选择菜单附加到不同页面上的另一个 DOM 节点时,为啥会中断?

jquery中都有哪些dom操作