4

我正在尝试为 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;
}

但是现在,调用时如何返回“ barobj.constructor.name
http://jsfiddle.net/jC6uz/1/

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

4

2 回答 2

2

根据我评论中的 MDN 文章,这是您使用他们的技术的方法,但适合您的风格:

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/


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

仅供参考,我个人不喜欢Class在 javascript 中使用这个词。一旦你真正“了解”了 javascript 的工作原理,它就没有真正的类概念。它有对象和原型,我认为不将它们中的任何一个与类的不同概念混淆或等同起来最终会更清晰。当我第一次从 C++ 来到 JS 时,我试图在 JS 中找到相似之处,这样我就可以像以前写 C++ 一样编写,但这并不是你真正想要做的。您最好学习 JS 如何做事并在其优势范围内进行编码,而不是尝试创建与其他一些并非真正如何工作的语言类似的语言。这就是为什么我在我的实现中选择一个词 likenewConstructor而不是Class,因为它更能描述 JS 真正在做什么以及它是如何工作的。

于 2013-11-05T00:44:02.047 回答
1

如果函数是构造函数,最好以大写字母开头。名为 foo 的函数不会将自己标识为构造函数,因此最好将其命名为 Foo。

当您设置 Child 的原型时,您不知道实例,因此无法调用Parent.apply(this,arguements);. 如果您知道 ChildParent,那么您可以将 Parent.appy 添加到 Child 的正文中(请参阅最后的链接)。

看起来您想用另一个动态扩展一个构造函数,最完整的方法(instanceof 和构造函数都按预期工作,父实例成员是子实例成员)是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

于 2013-11-05T03:44:26.647 回答