6

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

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这里无法访问?您会推荐另一种方法而不是 using$.extend吗?

4

4 回答 4

7
$.extend(this, foo);

this不是您从下面的函数返回的对象(实际上它不可能是,因为它是在此调用之后创建的),而是全局对象 - 请查看MDN 对this关键字的介绍。

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

  • 创建对象后,将所有属性复制foo您的对象上:bar

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

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

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

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

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

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

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


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

于 2013-09-15T20:44:43.747 回答
5

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

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

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

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

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

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小提琴

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

于 2013-09-15T20:40:50.027 回答
1

您的代码的问题在于 $.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
于 2013-09-15T20:48:44.363 回答
0

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

   var foo = (function(){

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

    return {

        doFooStuff: doFooStuff
      }

   })();

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

如果您删除匿名函数减速,它将起作用。

于 2013-09-15T20:38:08.313 回答