Javascript 带有原型和基类的JS OO模式
这是OOJS的好模式吗? 我要寻找的是一种在JavaScript中解决继承问题的简单方法Javascript 带有原型和基类的JS OO模式,javascript,Javascript,这是OOJS的好模式吗? 我要寻找的是一种在JavaScript中解决继承问题的简单方法 function MySuperClass(arg) { this.arg1 = arg; } function MyBaseClass(arg) { this.base = MySuperClass; this.base(arg); this.arg2 = arg; } MyBaseClass.prototype = new MySuperClass(); function
function MySuperClass(arg)
{
this.arg1 = arg;
}
function MyBaseClass(arg)
{
this.base = MySuperClass;
this.base(arg);
this.arg2 = arg;
}
MyBaseClass.prototype = new MySuperClass();
function MySpecificClass(arg)
{
this.base = MyBaseClass;
this.base(arg);
this.arg3 = arg;
}
//ensures inheritance of all properties
MySpecificClass.prototype = new MyBaseClass();
var myFirstInstance = new MySpecificClass("test");
var mySecondInstance = new MySpecificClass("test2");
注意:请参见ES2015更新的结尾 ES5及更早版本 这里有几个问题
MySuperClass
函数需要一个参数,但在调用它来创建MyBaseClass.prototype
时,您不能给它一个参数base
属性对于MyBaseClass
中的代码将无法正常工作,因为MyBaseClass
希望它是MySuperClass
,但它不是,因为MySpecificClass
已覆盖它MyBaseClass
和MySpecificClass
)是非常聪明的,因为只需两级层次结构就很容易做到这一点,但对于三级以上的层次结构,这要复杂得多。:-)
如果您想在JavaScript中详细讨论如何处理继承、调用超类方法等,我已经和您讨论过了。阅读本文并查看工具包源代码(超出了本文的范围)可能有助于理解原型链如何工作以及如何使用它
这里有一个例子,不使用任何工具箱,也不试图使超级调用变得简单。为了清楚起见,我用了三代人的术语父母
、孩子
、和孙子
:
// A parent (base) "class"
function Parent(a) {
this.a = a;
}
Parent.prototype.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
Parent.prototype.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
// A child "subclass"
function Child(a, b) {
// Chain to "superclass" constructor
Parent.call(this, a);
// Do our own init
this.b = b;
}
// Create the prototype objct that `new Child` will assign to instances
// by creating a blank object backed by `Parent.prototype`. Also set
// the `constructor` property on the object; JavaScript defines that it
// will refer back to the function on the default prototype objects, so
// we do that for consistency despite nothing in JavaScript actually
// _using_ `constructor`.
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// Add things to `Child.prototype`
Child.prototype.one = function() {
Parent.prototype.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
Child.prototype.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
// A grandchild "subclass"
function GrandChild(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
Child.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
}
// Again create a blank object to be the prototype `new GrandChild`
// assigns, again set `constructor`
GrandChild.prototype = Object.create(Child.prototype);
GrandChild.prototype.constructor = GrandChild;
// Add things to it
GrandChild.prototype.one = function() {
Child.prototype.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
GrandChild.prototype.three = function() {
Child.prototype.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
用法:
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"
测试instanceof,尽管如果您经常使用instanceof,您可能希望了解duck键入:
// Some things that should be true
console.log("p instanceof Parent? " + (p instanceof Parent));
console.log("c instanceof Parent? " + (c instanceof Parent));
console.log("c instanceof Child? " + (c instanceof Child));
console.log("gc instanceof Parent? " + (gc instanceof Parent));
console.log("gc instanceof Child? " + (gc instanceof Child));
console.log("gc instanceof GrandChild? " + (gc instanceof GrandChild));
// And some things that *shouldn't* be true:
console.log("p instanceof Child? (should be false) " + (p instanceof Child));
console.log("p instanceof GrandChild? (should be false) " + (p instanceof GrandChild));
console.log("c instanceof GrandChild? (should be false) " + (c instanceof GrandChild));
如果您未处于启用ES5的环境中,则可以将此垫片用于对象。创建(注意:不是一个完整的垫片,仅足以启用上述内容):
您可以了解为什么工具箱脚本使生活变得更轻松。你有好几种选择。以下是使用my toolkit进行上述操作的情况:
// A parent (base) "class"
var Parent = Lineage.define(function(p) {
p.initialize = function(a) {
this.a = a;
};
p.one = function() {
console.log("I'm Parent#one: a = " + this.a);
};
p.two = function() {
console.log("I'm Parent#two: a = " + this.a);
};
});
// A child "subclass"
var Child = Lineage.define(Parent, function(p, pp) {
p.initialize = function(a, b) {
// Chain to "superclass" constructor
pp.initialize.call(this, a);
// Do our own init
this.b = b;
};
p.one = function() {
pp.one.call(this);
console.log("I'm Child#one: b = " + this.b);
};
p.three = function() {
console.log("I'm Child#three: b = " + this.b);
};
});
// A grandchild "subclass"
var GrandChild = Lineage.define(Child, function(p, pp) {
p.initialize = function(b, c) {
// Chain to "superclass" constructor
// Note that GrandChild has a fixed value for Parent's `a`
pp.initialize.call(this, "GrandChildFixedA", b);
// Do our own init
this.c = c;
};
p.one = function() {
pp.one.call(this);
console.log("I'm GrandChild#one: c = " + this.c);
};
p.three = function() {
pp.three.call(this);
console.log("I'm GrandChild#three: c = " + this.c);
};
});
用法是一样的
ES2015及更高版本
从ES2015(又称“ES6”)开始,JavaScript获得了类
和超级
关键字,这大大简化了上述内容,现在可以与transpiling一起使用
class Parent {
constructor(a) {
this.a = a;
}
one() {
console.log("I'm Parent#one: a = " + this.a);
}
two() {
console.log("I'm Parent#two: a = " + this.a);
}
}
class Child extends Parent {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm Child#one: a = " + this.a);
}
three() {
console.log("I'm Child#three: a = " + this.a);
}
}
class GrandChild extends Child {
constructor(a) {
super(a);
}
one() {
super.one();
console.log("I'm GrandChild#one: a = " + this.a);
}
three() {
super.three();
console.log("I'm GrandChild#three: a = " + this.a);
}
}
// Usage
var p = new Parent("ParentA");
console.log("Calling p.one");
p.one(); // "I'm Parent#one: a = ParentA"
console.log("Calling p.two");
p.two(); // "I'm Parent#two: a = ParentA"
var c = new Child("ChildA", "ChildB");
console.log("Calling c.one");
c.one(); // "I'm Parent#one: a = ChildA" then "I'm Child #one: b = ChildB"
console.log("Calling c.two");
c.two(); // "I'm Parent#two: a = ChildA"
console.log("Calling c.three");
c.three(); // "I'm Child#three: b = ChildB"
var gc = new GrandChild("GrandChildB", "GrandChildC");
console.log("Calling gc.one");
gc.one(); // "I'm Parent#one: a = GrandChildFixedA" then "I'm Child #one: b = GrandChildB" then "I'm GrandChild#one: c = GrandChildC"
console.log("Calling gc.two");
gc.two(); // "I'm Parent#two: a = GrandChildA"
console.log("Calling gc.three");
gc.three(); // "I'm Child#three: b = GrandChildB" then "I'm GrandChild#three: c = GrandChildC"
我正在使用这种方法:
var func1 = function(parameter1, parameter2) {
// do your stuff here
}
var func2 = function(parameter1, parameter2, parameter3) {
// call the constructor of func1 with actual 'this'
func1.call(this, parameter1, parameter2);
// do your specific task here
}
func2.prototype = func1.prototype;
func2.prototype.constructor = func2;
工作正常:)通过将同一对象同时分配给func1.prototype
和func2.prototype
,您有点违背了拥有单独“类”的目的(例如,构造函数为其创建的实例分配不同的特征),因为通过任一函数创建的所有实例都将具有相同的底层原型。此外,您正在设置这样一种情况,即通过new func1
创建的实例将具有instance.constructor==func2
,而不是func1
,这似乎具有误导性。
var func1 = function(parameter1, parameter2) {
// do your stuff here
}
var func2 = function(parameter1, parameter2, parameter3) {
// call the constructor of func1 with actual 'this'
func1.call(this, parameter1, parameter2);
// do your specific task here
}
func2.prototype = func1.prototype;
func2.prototype.constructor = func2;