【问题标题】:Creating inheritance on revealing modular pattern objects在显示模块化模式对象上创建继承
【发布时间】:2013-09-19 23:10:20
【问题描述】:

我正在尝试在对象之间创建某种继承:

var foo = (function(){

    function doFooStuff(){
        console.log(arguments.callee.name);
    }

    return {
        doFooStuff: doFooStuff
    }

})();

var bar = (function(){

    $.extend(this, foo);

    function doBarStuff(){
        console.log(arguments.callee.name);
        doFooStuff();
    }

    return {
        doBarStuff: doBarStuff,
    }

})();

bar.doBarStuff();
bar.doFooStuff(); //<-- Uncaught TypeError: 
                  //Object #<Object> has no method 'doFooStuff' 

http://jsfiddle.net/wRXTv/

为什么这里不能访问doFooStuff?您会推荐使用 $.extend 以外的其他方法吗?

【问题讨论】:

  • 当你做$.extend(this, foo); 你想做什么? this 代表全局对象(在浏览器中通常是 window)。您的意思是像使用 new function(){ 的构造函数一样调用第二个 IIFE 吗?
  • How to implement inheritance in JS Revealing prototype pattern? 的可能重复项(尽管这仅适用于原型继承模式)
  • @BenjaminGruenbaum 哦,我以为它指的是bar。不,我不想创建一个新实例,我只想要“静态”对象。
  • @Johan 请参阅@Bergi 的答案,以获取有关this 工作原理的更详细说明的链接。

标签: javascript jquery


【解决方案1】:
$.extend(this, foo);

this 不是您从下面的函数返回的对象(事实上它不可能是,因为它是在此调用之后创建的),而是全局对象 - 检查 MDN's introduction to the this keyword

对于你想做的,有两种方法:

  • Copy all propertiesfoo 到您的bar 对象创建后:

    var bar = (function() {
        …
        return {…};
    })();
    $.extend(bar, foo);
    

    您也可以直接在返回的对象上执行此操作:

        return $.extend({…}, foo);
    

    此模式的一个变体允许您覆盖foo 属性。将foo 复制到一个空对象中,然后将您的bar 属性写入其中:

        return $.extend({}, foo, {…});
    
  • 使用原型继承。 Create 一个从foo 继承其属性的对象,然后将您的bar 属性写入它:

        return $.extend(Object.create(foo), {…});
    

    现在,当foo 之后发生更改时,您仍然可以访问bar 上的这些新属性(除非它们被自己的属性所遮蔽)。请注意,旧环境中可能不支持 Object.create,但您可以轻松地对其进行填充。


正如@raina77ow 所指出的,您的doBarStuff 函数也存在缺陷。 doFooStuff 函数不在您的函数范围内,您无法更改它。您将永远无法从 foo 模块访问私有声明的函数和变量,只能访问那些是公共的 - 如果您确实需要它们,请考虑不同的模式或应用程序设计。但是,doFooStuff 是导出的(公共)foo 对象上的一个属性,bar 继承自该对象(不管上述哪种方式)。因此,您也可以将其作为bar 的方法访问,通常使用this.doFooStuff()

【讨论】:

  • 谢谢伯吉。您能否简要解释一下$.extend(Object.create(foo)...$.extend(foo... 之间的区别?
  • $.extend 确实将它看到的第二个(以及更多)参数的属性复制到它的第一个参数,创建一个 snapshot 并留下两个具有相似属性的区别对象指向相同的值。相反,Object.create 执行 true inheritance,生成的对象本身没有任何属性,而是在其原型对象 live 上查找它们。
  • 请注意,我从未扩展 foo,我仅将其用作源参数。写出:var bar = {}; $.extend(bar, foo); $.extend(bar, {…}) vs var bar = Object.create(foo); $.extend(bar, {…});
【解决方案2】:

您尝试使用显示模块,就好像它是构造函数一样。因此尝试扩展this,由于许多原因是错误的。我想,最明显的是,该函数既没有用作构造函数(没有new),也没有改变它的上下文。简单来说,this 在这里只是指向一个全局对象。

但这不是唯一的问题。考虑一下你的那部分代码:

function doBarStuff(){
  console.log(arguments.callee.name);
  doFooStuff();
}

即使您设法扩展了this,这里的doFooStuff 也不在范围内。 ) 请记住,范围解析不涉及上下文对象。

那么解决办法是什么?好吧,我经常在类似的情况下使用聚合:

var foo = (function(){
    function doFooStuff(){
        console.log(arguments.callee.name);
    }
    return {
        doFooStuff: doFooStuff
    }
})();

var bar = (function(){
    var _super = foo;
    function doBarStuff(){
        console.log(arguments.callee.name);
        _super.doFooStuff();
    }
    // static parent: further changes on `foo` won't affect `bar`,
    // as $.extend takes the parent's current state
    return $.extend({}, _super, {
        doBarStuff: doBarStuff,
    });
    // dynamic parent: further changes on `foo` will affect `bar`,
    // as extended object now has `foo` in its prototype chain
    return $.extend(Object.create(_super), {
        doBarStuff: doBarStuff,
    });
})();

JS Fiddle.

是的,它是聚合,而不是继承。但那又怎样?我仍然能够获得主要奖励 - 删除代码重复并且我能够控制子模块中父函数的使用。

【讨论】:

  • 我明白了。不过,这不会合并 foobar
  • 检查更新(和小提琴)。是你要找的吗?
  • 很好,没有考虑在 return 语句处扩展。为什么将空对象作为第一个参数传递给extend
  • 因为否则 foo 对象(在函数内由 _super 引用)也将被更改。请注意,在这种情况下,foo 的进一步更改不会影响 bar(它会根据 current foo 状态返回一个新对象)。如果您想要其他方式,请使用return $.extend(Object.create(_super), {..}),如@Bergi 示例中所示。
【解决方案3】:

您的代码的问题在于 $.extend(this,foo) 中的 this 指的是 window 对象,而 不是 foo。匿名函数在窗口的上下文中运行。

我建议在 javascript 中使用 John Resig 实现经典继承。 http://ejohn.org/blog/simple-javascript-inheritance/.

摘录:

var Person = Class.extend({
 init: function(isDancing){
 this.dancing = isDancing;
 },
dance: function(){
 return this.dancing;
 }
});

var Ninja = Person.extend({
 init: function(){
  this._super( false );
 },
dance: function(){
 // Call the inherited version of dance()
 return this._super();
},
 swingSword: function(){
  return true;
 }
});

var p = new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class

【讨论】:

  • 如何使用 John Resig 的库作为普通实现?
  • 嗯,John Resig 这些天来相当普通。 )
  • -1。 OP 想要一个单例模块模式,Class 不是他需要的。在 JavaScript 中,可以在没有类(或“类”模式)的情况下进行继承,这里应该这样做。
  • @BenjaminGruenbaum:我的意思是它不依赖于任何其他库,如 jQuery。我猜这组词不正确。
  • @SelvamPalanimalai:是的,真正的vanilla 方法是不使用任何库——你的代码依赖于Class...
【解决方案4】:

这是因为这是一个匿名函数。这部分你的函数减速:

   var foo = (function(){

    function doFooStuff(){
        console.log(arguments.callee.name);
    }

    return {

        doFooStuff: doFooStuff
      }

   })();

您是否看到您正在立即调用此函数调用并且最终无法在您的 return 语句中访问 doFooStuff

如果你去掉匿名函数减速,它会起作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 2023-03-21
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多