【问题标题】:JS Module Pattern's public method as callback victim. (this-issue)JS 模块模式的公共方法作为回调受害者。 (这个问题)
【发布时间】:2013-07-25 05:38:24
【问题描述】:

我花了一天的大部分时间阅读有关模块模式及其“this”范围的信息。最终我找到了解决问题的方法,尽管我觉得有更好的做事方式。

实际代码超过 200 行,但我将其归结为以下内容: objA 有一个方法 (publicA),objB 希望通过回调调用该方法。使事情复杂化的细节是 publicA 需要 publicA_helper 的帮助来完成它的工作。 (http://jsfiddle.net/qwNb6/2/)

var objA = function () {
    var privateA = "found";
    return {
        publicA: function () {
            console.log("privateA is " + this.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

var objB = function () {
    return {
        callback: function (callback) {
            callback();
        }
    }
}();

objA.publicA(); // privateA is found
objB.callback(objA.publicA); // TypeError: Object [object global]

很公平——我已经了解到调用者的上下文往往会影响“this”的值。因此,我添加了一些措施以在 objA 中保留“this”,但这些措施似乎都不起作用。我试过了 var objA = (){}.call({})thingy,设置var self = this;(相应地调用self.publicA_helper())。没有运气。

最后,我添加了一个私有变量var self;,以及一个公共方法:

init: function() {self = this;},

...通过确保在将objA.publicA 传递给objB.callback 之前调用objA.init();,事情确实有效。

我无法强调有更好的方法来做到这一点。我错过了什么?

【问题讨论】:

标签: javascript callback closures this module-pattern


【解决方案1】:

广义的解决方案非常简单。

将模块的所有方法写成私有的,然后公开那些需要公开的。

我所有的模块都是这样写的:

var objA = function () {
    var privateA = "found";
    var A = function () {
        console.log("privateA is " + A_helper());
    },
    var A_helper = function () {
        return privateA;
    }
    return {
        publicA: A
        //A_helper need not be exposed
    };
}();

因此,所有方法都在同一范围内,每个方法都可以直接访问同一模块中的所有其他方法,并且避免了模棱两可的 this 前缀。

objB.callback(objA.publicA); 现在将按预期工作。

fiddle

【讨论】:

  • 感谢 Beetroot - 由于其详细程度,我授予了 Bergi 答案。这种模式在可读性和灵活性方面都具有魅力——我可以轻松查看和更改暴露的成员。我不明白为什么我会想要回到我的旧方式,即在 return 子句中实际定义公共方法......(?)
  • 好的,但您真正想要的 Bergi 答案部分是最后一部分,与我的相同,2 小时前发布!
【解决方案2】:

我试过var objA = (){}.call({})这个东西,

怎么样?您想在要使用自定义this 调用的回调上使用call,而不是在模块闭包上使用。应该是

var objB = {
    callback: function (callback, context) {
        callback.call(context);
    }
};

objB.callback(objA.publicA, objA);

我试过设置var self = this;

self 变量应该在一个闭包中并且指向对象上的方法被存储。这只是this,当您的模块 IEFE 将在您的模块上被调用时 - 它不是。或者如果它是一个构造函数 - 它不是。您可以使用call 进行更改:

var objA = function () {
    var privateA = "found",
        self = this;
    this.publicA = function () {
        console.log("privateA is " + self.publicA_helper());
    };
    this.publicA_helper = function () {
        return privateA;
    };
    return this;
}.call({});

但这很难看。在您的情况下, self 变量只需要指向您作为模块返回的对象文字:

var objA = function () {
    var privateA = "found",
        self;
    return self = {
        publicA: function () {
            console.log("privateA is " + self.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

顺便说一句,由于您正在创建一个单例,因此您不需要显式的 self,您可以只引用包含您的模块的变量(只要它没有改变):

var objA = function () {
    var privateA = "found";
    return {
        publicA: function () {
            console.log("privateA is " + objA.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

另一种方法是简单地将所有函数设为私有,然后公开其中一些 - 通过在本地范围内引用它们,您将不会遇到任何问题。

var objA = function () {
    var privateA = "found";
    function publicA() {
        console.log("privateA is " + helper());
    }
    function helper() {
        return privateA;
    }
    return self = {
        publicA: publicA,
        publicA_helper: helper // remove that line if you don't need to expose it
    };
}();

【讨论】:

  • 感谢 Bergi 的详尽回答。你的最后一种模式很难反驳,完全避免“这个”(见我对甜菜根-甜菜根的评论)。关于您对单身人士的评论:这实际上是我最初使用的方法让我来到这里。我只是有一种感觉,一次又一次地调用 objA.publicA 意味着要遍历范围链(因为直到到达全局上下文才找到 objA)。我还想要一个通用的模式,即不是“仅限单例”。
【解决方案3】:

原因是当您调用回调时上下文发生了变化。不是一个通用的解决方案,但表明代码通过在调用回调时指定上下文来工作。

var objA = function () {
  var privateA = "found";
  return {
    publicA: function () {
        console.log("privateA is " + this.publicA_helper());
    },
    publicA_helper: function () {
        return privateA;
    }
  };
}();

var objB = function () {
    return {
        callback: function (callback) {
          callback.call(objA);
        }
   }
}();

objA.publicA(); // privateA is found
objB.callback(objA.publicA); // privateA is found

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-11
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    相关资源
    最近更新 更多