"公共函数如果需要打补丁则无法被覆盖"这句话在Addy的揭示模块模式描述中是什么意思?

11
这种模式的缺点是,如果一个私有函数引用了一个公共函数,那么如果需要修补程序,该公共函数就无法被覆盖。这是因为私有函数将继续引用私有实现,而该模式仅适用于公共成员,而不适用于函数。
有人能举个例子吗? 链接到上面提到的揭示模式

这意味着像 myRevealingModule.increment = newFunction; 这样的操作并不会改变模块的内部工作方式。myRevealingModule.start 仍然会调用内部的私有增量函数。这可能是好的或者不好的,具体取决于上下文。 - Felix Kling
因此,在这种情况下,“a patch”被称为Monkey Patching,或者为什么类似的补丁是必要的——是为了修复错误还是只是为了更改对象的行为? - nonopolarity
3个回答

15

将使用对象字面量创建的对象与使用Revealing Module模式创建的对象进行比较。

这是一个使用对象字面量创建的对象示例。

function makeGreeter(name){
  return {
    getName: function(){ return name;},
    sayHello: function(){console.log("Hello, " + this.getName());}
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello; // "Hello, Danny"
greeter.getName = function(){ return "George";}
greeter.sayHello(); // "Hello, George"

当你在返回对象上重写公共方法getName时,依赖于getNamesayHello方法会受到更改的影响。这是因为在对象字面量风格中,对公共函数的引用是通过this即返回的对象来完成的。

然而,当你使用揭示模块模式时,

function makeGreeter(name){
  var getName = function(){ return name;},
    sayHello = function(){console.log("Hello, " + getName());};
  return {
    getName: getName,
    sayHello: sayHello
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello; // "Hello, Danny"
greeter.getName = function(){ return "George";}
greeter.sayHello(); // "Hello, Danny"

RMP问候者不会调用公共getName方法的覆盖,因为当RMP函数引用其他函数(包括公共和私有函数)时,它们引用私有闭包副本而不是附加到返回对象的公共函数。

正因为如此,我认为揭示模块模式是反模式。


2
但实际上,这样的“补丁”起初并不被认为是非常干净的,对吧? - nonopolarity
2
这个例子有点牵强,因为它本身似乎并不能反映出我在考虑使用“私有”方法时的想法。 - bigmadwolf
2
然而,当我阅读上面的代码时,并没有什么大惊小怪的事情,也没有像通常那样“等等...这只是JavaScript表现得很奇怪...”,或者可能是我花太多时间在JS上了,但是你上面的代码确实做到了我所期望的,也许你可以用更好的例子来解释一下?我不明白这个例子证明了除了创建无法被覆盖的私有方法之外的任何东西。 - bigmadwolf
2
你的例子失败是因为它是模式的一个糟糕实现,而不是因为它是RMP。调整你的例子以按预期方式运行几乎是微不足道的,同时仍允许你拥有私有方法和常量,从而产生更清晰的API。 - bigmadwolf
2
朋友,解决问题的方法可谓千奇百怪,就像这个情况下的RMP一样。不仅仅是最后返回值容易操作这么简单,还有其他的差异或优势。 - bigmadwolf
显示剩余9条评论

1

@I-Lin Kuo给出的答案看起来不错,但是有一种情况会造成混淆。

function makeGreeter(name) {
return {
    getName: function() {
        return name;
    },
    sayHello: function() {
        console.log("Hello," + this.getName());
    }
  }
}

var greeter = makeGreeter("Danny");
greeter.sayHello(); //"Hello,Danny"
greeter.getName = function() {
    return "George";
}
greeter.sayHello(); //"Hello,George"

应该使用 greeter.sayHello() 而不是 greeter.sayHello。这会造成很多混淆。


1
@ScottBeeson 是的,下次一定会考虑这个建议。 - Chaitanya Babar

1
我会将getName绑定到这个位置,它似乎指向在RMP中返回的内容。
function makeGreeter(name){
    this.getName = function(){ return name;};
    var _sayHello = function(){console.log("Hello, " + this.getName());};
    return {
            getName: getName,
            sayHello: _sayHello
    }
}

我更喜欢这个,尽管它是这样的:
function makeGreeter(name){
    this.getName = function(){ return name;};
    var _sayHello = function(){console.log("Hello, " + this.getName());};
    var API = {
        getName: getName,
        sayHello: _sayHello
    };
    return API;
}

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