我正在尝试在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)您如何看待这种解决方案,是不是太糟糕了? 是否存在其他类似的选择?
编辑:我最终得到了以下“扩展”实施:
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/
答案 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