Javascript构造函数继承助手

时间:2013-11-04 21:18:00

标签: javascript oop inheritance

我正在尝试在JavaScript中为构造函数继承创建帮助方法。

我有以下简单的代码:

function extend(parent, fn) {
    return function () {
        parent.call(this);
        fn.call(this);
        fn.prototype.constructor = parent;
    }
}

function foo() {
    var a = 1;
    this.getA = function () { return a; };
}

var bar = extend(foo, function () {
    var b = 2;
    this.getB = function () { return b; };
  });

var obj = new bar();
console.log(obj.getA()); //1
console.log(obj.getB()); //2

console.log(obj instanceof bar); //true
console.log(obj instanceof foo); //false

1)如何使最后一行(obj instanceof foo)成立?

2)您如何看待这种解决方案,是不是太糟糕了? 是否存在其他类似的选择?

http://jsfiddle.net/jC6uz/

编辑:我最终得到了以下“扩展”实施:

function extend(parent, fn) {
    var Class = function () {
        parent.call(this);
        fn.call(this);
    }
    Class.prototype = parent.prototype;
    Class.prototype.constructor = Class;
    return Class;
}

但现在,如何在调用obj.constructor.name时返回“ bar ”? http://jsfiddle.net/jC6uz/1/

编辑2:支持构造函数参数的另一个版本(不是直接的,但仍然可以) http://jsfiddle.net/jC6uz/3/

2 个答案:

答案 0 :(得分:2)

根据MDN article in my comment,这就是你如何使用他们的技术,但适合你的风格:

function extend(parent, fn) {
    var newConstructor = function() {
        fn.call(this);
    }
    newConstructor.prototype = new parent();
    return newConstructor;
}

function foo() {
    var a = 1;
    this.getA = function () { return a; };
}

var bar = extend(foo, function () {
    var b = 2;
    this.getB = function () { return b; };
});

var obj = new bar();
console.log(obj.getA()); //1
console.log(obj.getB()); //2

console.log(obj instanceof bar); //true
console.log(obj instanceof foo); //true

工作演示:http://jsfiddle.net/jfriend00/efReT/


您的特定实现不允许的一件事是构造函数有参数。

仅供参考,我个人不喜欢在javascript中使用Class这个词。一旦你真正“了解”javascript如何工作,它就没有真正的类概念。它有对象和原型,我认为最好不要混淆或等同于具有不同类概念的任何一个。当我第一次从C ++来到JS时,我试图在JS中找到并行,所以我可以像以前编写的那样编写C ++,但这并不是你想要做的。你最好学习JS如何处理事情和编码,而不是试图创建类似于其他语言的类比,而不是真正的工作方式。这就是为什么我在实现中选择newConstructor而不是Class这样的词,因为它更能描述JS真正在做什么以及它是如何工作的。

答案 1 :(得分:1)

如果函数是构造函数,最好以大写形式启动它。名为foo的函数不会将自身标识为构造函数,因此最好将其命名为Foo。

当您设置Child的原型时,您不知道该实例,因此无法调用Parent.apply(this,arguements);。如果你知道Child 是一个Parent,那么你可以在Child的主体中添加Parent.appy(参见最后的链接)。

看起来你想用另一个动态扩展一个constuctor,最完整的方式(instanceof和constructor都按预期工作,而parent实例成员是Child的实例成员)将extend创建您的实例或添加初始化“父”实例成员的init函数。

如果您正在寻找类似于实现或多重继承的东西,用于对象可以做某事而不是 的东西,请查看该链接中的链接。结束模式混合。

var extend = function(source,target,arg){
  //source=Parent, target=Child
  //set prototype without createing a source
  //instance and without using Object.create
  var fn=function(){},ret;
  fn.prototype = source.prototype;
  target.prototype = new fn();
  //repair constructor
  target.prototype.constructor = target;
  //create instance
  ret = new target(arg);
  //add function to set source intance members
  ret.extend_init=function(arg){
    source.apply(this,arguments);
  };
  //set source intance members
  ret.extend_init(arg);
  //remove init
  delete ret.extend_init;
  return ret;
};
var Parent = function(arg){
  this.name=(arg && arg.name)? arg.name:undefined;
  this.age=(arg && arg.age)?arg.age:undefined;
};
Parent.prototype.whoAreYou = function(){
  return "I am "+this.name+" and I'm "+this.age+
    " years old.";
};
var Child = function(){
};

var t = extend(Parent,Child,{
  name: "t",
  age: 22});

console.log(t instanceof Child);//<=true
console.log(t instanceof Parent);//<=true
console.log(t.whoAreYou());//<=I am t and I'm 22 years old.

这会导致一些开销,所以如果你在循环中创建了很多这些实例,最好在循环之前设置原型,在循环中创建和初始化实例并在之后清理:

var extend = function(source,target){
  var fn=function(){},orgProto=target.prototype,
  thing;
  fn.prototype = source.prototype;
  //overwriting Child.prototype, usually you define inheritance
  //  first and add Child.prototype members after but when setting
  //  inheritance dynamic (not having Parent.apply(this,arguments in
  //  Childs body) the Child's prototype get overwritten
  target.prototype = new fn();
  //adding the Child.prototype members
  for(thing in orgProto){
    if(orgProto.hasOwnProperty(thing)){
      target.prototype[thing]=orgProto[thing];
    }
  }
  target.prototype.constructor = target;
  target.prototype.extend_init=function(){
    source.apply(this,arguments);
    return this;
  };
  return target;
};
var Parent = function(arg){
  this.name=(arg && arg.name)? arg.name:undefined;
  this.age=(arg && arg.age)?arg.age:undefined;
};
Parent.prototype.whoAreYou = function(){
  return "I am "+this.name+" and I'm "+this.age+
    " years old.";
};
var Child = function(){
};
Child.prototype.something=22;
//namesAndAges could be JSON data containing
// hundreds or even thousands of items
namesAndAges = [
  {name:"1",age:1},
  {name:"2",age:2},
  {name:"3",age:3},
  {name:"4",age:4},
  {name:"5",age:5}
  //, and many many more
];
var constr=extend(Parent,Child);
var persons=[];
for(var i = 0,len=namesAndAges.length;i<len;i++){
  //Child may have constructor parameters so we pass the parameter
  // object to both Child and Parent
  persons.push(new constr(namesAndAges[i])
    .extend_init(namesAndAges[i]));
};
delete constr.prototype.extend_init;
console.log(persons);

更多关于原型,继承,覆盖,调用超级,混合和this的价值:https://stackoverflow.com/a/16063711/1641941