简单的JavaScript继承示例

3
我正在尝试在JavaScript中实现简单的继承概念,而不使用任何外部库。这是我的代码。
<script>
    BaseClass = function () {
        this.doSomething = function () {
            console.log("base class");
        };
    };

    DerivedClass1 = function () {
        var bc = new BaseClass();
        bc.doSomething = function () {
            console.log("derived class");
        };
        return bc;
    };

    DerivedClass2 = function () {
        var bc = new BaseClass();
        return bc;
    }


    var cls1 = new DerivedClass1();
    cls1.doSomething();

    var cls2 = new DerivedClass2();
    cls2.doSomething();


</script>

它属于任何已知的设计模式吗?


1
http://codereview.stackexchange.com - Hanky Panky
1
共享方法应该在prototype上,实例属性在构造函数上,然后你可以使用Object.create进行继承,并像这样调用超类:BaseClass.apply(this, arguments) - elclanrs
关于原型和构造函数有很多要学习的地方。我在这里尝试给出一个完整的答案:https://dev59.com/J2Qo5IYBdhLWcg3wbe5K#16063711 DerivedClass2 没有做任何事情,你可以使用 new BaseClass(),因为那是它返回的内容。 - HMR
3个回答

3

简而言之

var BaseClass = function (bar) {
    this.foo = bar;
};
BaseClass.prototype.doSomething = function () {
    console.log("from base class");
};

var DerivedClass = function () {
    BaseClass.apply(this, arguments);
};
DerivedClass.prototype = Object.create(BaseClass.prototype);
DerivedClass.prototype.doSomething = function () {

    BaseClass.prototype.doSomething.call(this);
    console.log("from derived class");
};

var cls1 = new DerivedClass('I am cls1');

cls1.doSomething();
// -> from base class
// -> from derived class

console.log(cls1.foo);
// -> "I am cls1"

更新

感谢@HMR的评论(请参见他下面的评论,非常有用),我更新了我的答案:

  1. “在设置DerivedClass的原型时,不应创建BaseClass的新实例,而应使用Object.create(如果需要,请进行polyfill)”
  2. “您还忘记初始化BaseClass并通过BaseClass.apply(this,arguments)拥有它的实例变量所有权”

1/ 使用Object.create

var BaseClass = function () {
    this.foo = 'bar';
};
BaseClass.prototype.doSomething = function () {
    console.log("base class");
};

var DerivedClass = function () {

};
DerivedClass.prototype = Object.create(BaseClass.prototype);

注:

  • Object.create "复制" Base 的原型到 Derived 中。
  • 公共属性 this.foo 没有被复制到 Derived 中(因为它不是原型的一部分)- 请参见下面的第二点。

关于 Object.create 的更多信息 在这里

2/ BaseClass.apply(this, arguments)

如上所述,this.fooDerived 实例中不可用。为了使其可用,我们需要将 Base 构造函数应用到 Derived 构造函数中。

因此,Base 的所有 特权 属性(this.foo,...)都被应用于新的 Derived 实例。

var DerivedClass = function () {

    // Calls `Base` constructor with `Derived` context
    BaseClass.apply(this, arguments);
};
DerivedClass.prototype = Object.create(BaseClass.prototype);

在流行的HMR的回答中了解更多关于JavaScript继承的细节。


我将原始答案保留以供比较和教育目的。
您的方法存在问题(虽然它可能按预期工作),即doSomething方法被复制到每个BaseClass实例中(因为它声明为简单的public属性)。
为了避免这种情况,从而在所有BaseClass实例之间共享doSomething方法,您应该将其添加到BaseClassprototype中。
var BaseClass = function () {

};

BaseClass.prototype.doSomething = function () {
    console.log("base class");
};

你无法在最终结果中看到任何区别,但是这样做可以使doSomething方法被“继承”,而不是被复制。
现在知道了这一点,要在Javascript中实现原型继承
// Derived Class 1
var DerivedClass1 = function () {

};
DerivedClass1.prototype = new BaseClass();

var cls1 = new DerivedClass1();
cls1.doSomething();

// -> "base class"


// Derived Class 2
var DerivedClass2 = function () {

};
DerivedClass2.prototype = new BaseClass();
DerivedClass2.prototype.doSomething = function () {
    console.log("derived class (2)");
};

var cls2 = new DerivedClass1();
cls2.doSomething();

// -> "derived class (2)"

如果你想从DerivedClass中调用parent方法,可以使用以下方法:

// Derived Class 3
var DerivedClass3 = function () {

};
DerivedClass3.prototype = new BaseClass();
DerivedClass3.prototype.doSomething = function () {

    BaseClass.prototype.doSomething.call(this);

    console.log("derived class (3)");
};

var cls3 = new DerivedClass1();
cls3.doSomething();

// -> "base class"
// -> "derived class (3)"

在设置Child的原型时,不应创建Parent的新实例,而应使用Object.create(如果需要,请进行polyfill)。或者使用一个帮助函数。您还忘记了初始化Parent并通过Parent.apply(this,arguments)拥有它的实例变量。由于在使用原型时需要处理很多事情,并且类似这样的问题每天都会被问到两次,因此我添加了这个答案:https://dev59.com/J2Qo5IYBdhLWcg3wbe5K#16063711 - HMR
谢谢您的见解!我尝试了一下console,更深入地理解了发生的事情。我更新了我的答案以反映您的评论。如果我漏掉了什么,请告诉我。 - eightyfive
哇,你理解得真快,并且给出了一个相当不错的答案 +1。为了完整起见,在你的第一个代码块中,我通常会执行 DerivedClass.prototype.constructor = DerivedClass;,因为构造函数有时可以在实例中用于指向创建它们的函数(它始终存在于原型上,但在设置继承时被覆盖)。我明白为什么Douglas Crockford不是构造函数的铁粉,因为它很复杂且容易出错。但由于这是JS中的标准做法,我个人还是坚持使用它,并希望JS解释器能够更好地运行我的代码。 - HMR
一个需要注意的地方是:在JavaScript中,深拷贝对象并不容易,而Object.create只会进行浅拷贝。为什么这可能会成为问题,请参考这里:https://dev59.com/KHjZa4cB1Zd3GeqPkPMK#19879651(第二个代码块中的注释:“继承时会有问题:”)。我没有在原型答案中添加这个内容,因为我想坚持基础知识,并且已经足够长了。当您使用复杂模式时,这可能会成为问题,但是您可以在其他地方解决问题,而不是让代码变得过于复杂。 - HMR
谢谢。非常有教育意义。 - eightyfive

2

0

继承是创建一个类作为一个或多个类的专门版本的一种方式(仅支持单继承)。
这个专门的类通常被称为子类,另一个类通常被称为父类。在JavaScript中,您可以通过将父类的实例分配给子类,然后进行特化来实现这一点。在现代浏览器中,您也可以使用Object.create来实现继承。

注意:JavaScript无法检测到子类原型的constructor属性(请参见Object.prototype),因此我们必须手动声明它。请参阅问题为什么需要设置原型构造函数?

在下面的例子中,我们将类Student定义为Person类的子类。
然后我们重新定义了sayHello()方法并添加了sayGoodBye()方法。
// Define the Person constructor
var Person = function(firstName) {
  this.firstName = firstName;
};

// Add a couple of methods to Person.prototype
Person.prototype.walk = function(){
  console.log("I am walking!");
};

Person.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName);
};

// Define the Student constructor
function Student(firstName, subject) {
  // Call the parent constructor, making sure (using call)
  // that "this" is set correctly during the call
  Person.call(this, firstName);

  // Initialize our Student-specific properties
  this.subject = subject;
}

// Create a Student.prototype object that inherits from Person.prototype.
// Note: A common error here is to use "new Person()" to create the
// Student.prototype. That's incorrect for several reasons, not least 
// that we don't have anything to give Person for the "firstName" 
// argument. The correct place to call Person is above, where we call 
// it from Student.
Student.prototype = Object.create(Person.prototype); // See note below

// Set the "constructor" property to refer to Student
Student.prototype.constructor = Student;

// Replace the "sayHello" method
Student.prototype.sayHello = function(){
  console.log("Hello, I'm " + this.firstName + ". I'm studying "
              + this.subject + ".");
};

// Add a "sayGoodBye" method
Student.prototype.sayGoodBye = function(){
  console.log("Goodbye!");
};

// Example usage:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
console.log(student1 instanceof Person);  // true 
console.log(student1 instanceof Student); // true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接