JavaScript 扩展类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15192722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 23:49:21  来源:igfitidea点击:

JavaScript Extending Class

javascriptoopobjectinheritanceecmascript-6

提问by Lucas Penney

I have a base class:

我有一个基类:

function Monster() {
  this.health = 100;
}

Monster.prototype.growl = function() {
  console.log("Grr!");
}

That I want to extend and create another class with:

我想扩展并创建另一个类:

function Monkey extends Monster() {
  this.bananaCount = 5;
}

Monkey.prototype.eatBanana {
  this.bananaCount--;
  this.health++; //Accessing variable from parent class monster
  this.growl();  //Accessing function from parent class monster
}

I've done quite a bit of research and there appears to be many convoluted solutions for doing this in JavaScript. What would be the simplest and most reliable way of accomplishing this in JS?

我已经做了很多研究,在 JavaScript 中似乎有很多令人费解的解决方案。在 JS 中实现这一点的最简单和最可靠的方法是什么?

回答by Oliver Spryn

Updated below for ES6

下面针对 ES6 更新

March 2013 and ES5

2013 年 3 月和 ES5

This MDN document describes extending classes well:

这个 MDN 文档很好地描述了扩展类:

https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript

https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript

In particular, here is now they handle it:

特别是,现在他们处理它:

// define the Person Class
function Person() {}

Person.prototype.walk = function(){
  alert ('I am walking!');
};
Person.prototype.sayHello = function(){
  alert ('hello');
};

// define the Student class
function Student() {
  // Call the parent constructor
  Person.call(this);
}

// inherit Person
Student.prototype = Object.create(Person.prototype);

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;

// replace the sayHello method
Student.prototype.sayHello = function(){
  alert('hi, I am a student');
}

// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
  alert('goodBye');
}

var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();

// check inheritance
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true

Note that Object.create()is unsupported in some older browsers, including IE8:

请注意,Object.create()某些旧浏览器不支持,包括 IE8:

Object.create browser support

Object.create 浏览器支持

If you are in the position of needing to support these, the linked MDN document suggests using a polyfill, or the following approximation:

如果您需要支持这些,链接的 MDN 文档建议使用 polyfill,或以下近似值:

function createObject(proto) {
    function ctor() { }
    ctor.prototype = proto;
    return new ctor();
}

Using this like Student.prototype = createObject(Person.prototype)is preferable to using new Person()in that it avoids calling the parent's constructor functionwhen inheriting the prototype, and only calls the parent constructor when the inheritor's constructor is being called.

使用 this likeStudent.prototype = createObject(Person.prototype)比使用更可取new Person(),因为它避免在继承原型时调用父构造函数,并且仅在调用继承者的构造函数调用父构造函数

May 2017 and ES6

2017 年 5 月和 ES6

Thankfully, the JavaScript designers have heard our pleas for help and have adopted a more suitable way of approaching this issue.

幸运的是,JavaScript 设计者听到了我们的求助,并采用了更合适的方式来解决这个问题。

MDNhas another great example on ES6 class inheritance, but I'll show the exact same set of classes as above reproduced in ES6:

MDN有另一个关于 ES6 类继承的很好的例子,但我将展示与上面在 ES6 中复制的完全相同的类集:

class Person {
    sayHello() {
        alert('hello');
    }

    walk() {
        alert('I am walking!');
    }
}

class Student extends Person {
    sayGoodBye() {
        alert('goodBye');
    }

    sayHello() {
        alert('hi, I am a student');
    }
}

var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();

// check inheritance
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true

Clean and understandable, just like we all want. Keep in mind, that while ES6 is pretty common, it's not supported everywhere:

干净易懂,就像我们都想要的那样。请记住,虽然 ES6 很常见,但并非所有地方都支持

ES6 browser support

ES6 浏览器支持

回答by Abdennour TOUMI

ES6 gives you now the opportunity to use class& extendskeywords :

ES6 现在让你有机会使用class& extends关键字:

Then , your code will be :

然后,您的代码将是:

You have a base class:

你有一个基类:

class Monster{
       constructor(){
             this.health = 100;
        }
       growl() {
           console.log("Grr!");
       }

}

That You want to extend and create another class with:

你想扩展和创建另一个类:

class Monkey extends Monster {
        constructor(){
            super(); //don't forget "super"
            this.bananaCount = 5;
        }


        eatBanana() {
           this.bananaCount--;
           this.health++; //Accessing variable from parent class monster
           this.growl(); //Accessing function from parent class monster
        }

}

回答by elclanrs

Try this:

试试这个:

Function.prototype.extends = function(parent) {
  this.prototype = Object.create(parent.prototype);
};

Monkey.extends(Monster);
function Monkey() {
  Monster.apply(this, arguments); // call super
}

Edit:I put a quick demo here http://jsbin.com/anekew/1/edit. Note that extendsis a reserved word in JS and you may get warnings when linting your code, you can simply name it inherits, that's what I usually do.

编辑:我在这里放了一个快速演示http://jsbin.com/anekew/1/edit。请注意,这extends是 JS 中的保留字,您在整理代码时可能会收到警告,您可以简单地将其命名为inherits,这就是我通常所做的。

With this helper in place and using an object propsas only parameter, inheritance in JS becomes a bit simpler:

有了这个助手并使用对象props作为唯一参数,JS 中的继承就变得更简单了:

Function.prototype.inherits = function(parent) {
  this.prototype = Object.create(parent.prototype);
};

function Monster(props) {
  this.health = props.health || 100;
}

Monster.prototype = {
  growl: function() {
    return 'Grrrrr';
  }
};

Monkey.inherits(Monster);
function Monkey() {
  Monster.apply(this, arguments);
}

var monkey = new Monkey({ health: 200 });

console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"

回答by Harold

If you don't like the prototype approach, because it doesn't really behave in a nice OOP-way, you could try this:

如果你不喜欢原型方法,因为它并没有真正以一种很好的 OOP 方式表现,你可以试试这个:

var BaseClass = function() 
{
    this.some_var = "foobar";

    /**
     * @return string
     */
    this.someMethod = function() {
        return this.some_var;
    }
};

var MyClass = new Class({ extends: BaseClass }, function()
{
    /**
     * @param string value
     */
    this.__construct = function(value)
    {
        this.some_var = value;
    }
})

Using lightweight library (2k minified): https://github.com/haroldiedema/joii

使用轻量级库(2k 缩小):https: //github.com/haroldiedema/joii

回答by GMeister

This is an extension (excuse the pun) of elclanrs' solution to include detail on instance methods, as well as taking an extensible approach to that aspect of the question; I fully acknowledge that this is put together thanks to David Flanagan's "JavaScript: The Definitive Guide" (partially adjusted for this context). Note that this is clearly more verbose than other solutions, but would probably benefit in the long-term.

这是 elclanrs 解决方案的扩展(请原谅双关语),包括实例方法的详细信息,以及对问题的该方面采取可扩展的方法;我完全承认这要归功于 David Flanagan 的“JavaScript:权威指南”(针对此上下文进行了部分调整)。请注意,这显然比其他解决方案更冗长,但从长远来看可能会受益。

First we use David's simple "extend" function, which copies properties to a specified object:

首先我们使用 David 的简单“扩展”函数,它将属性复制到指定的对象:

function extend(o,p) {
    for (var prop in p) {
        o[prop] = p[prop];
    }
    return o;
}

Then we implement his Subclass definition utility:

然后我们实现他的子类定义实用程序:

function defineSubclass(superclass,     // Constructor of our superclass
                          constructor,  // Constructor of our new subclass
                          methods,      // Instance methods
                          statics) {    // Class properties
        // Set up the prototype object of the subclass
    constructor.prototype = Object.create(superclass.prototype);
    constructor.prototype.constructor = constructor;
    if (methods) extend(constructor.prototype, methods);
    if (statics) extend(constructor, statics);
    return constructor;
}

For the last bit of preparation we enhance our Function prototype with David's new jiggery-pokery:

在最后一点准备工作中,我们使用 David 的新 jiggery-pokery 增强了我们的 Function 原型:

Function.prototype.extend = function(constructor, methods, statics) {
    return defineSubclass(this, constructor, methods, statics);
};

After defining our Monster class, we do the following (which is re-usable for any new Classes we want to extend/inherit):

定义我们的 Monster 类后,我们执行以下操作(可重用于我们想要扩展/继承的任何新类):

var Monkey = Monster.extend(
        // constructor
    function Monkey() {
        this.bananaCount = 5;
        Monster.apply(this, arguments);    // Superclass()
    },
        // methods added to prototype
    {
        eatBanana: function () {
            this.bananaCount--;
            this.health++;
            this.growl();
        }
    }
);

回答by Yarik

I can propose one variant, just have read in book, it seems the simplest:

我可以提出一种变体,只是在书中读过,似乎最简单:

function Parent() { 
  this.name = 'default name';
};

function Child() {
  this.address = '11 street';
};

Child.prototype = new Parent();      // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment

var a = new Child(); 

console.log(a.name);                // "default name" trying to reach property of inherited class

回答by Willem van der Veen

Summary:

概括:

There are multiple ways which can solve the problem of extending a constructor function with a prototype in Javascript. Which of these methods is the 'best' solution is opinion based. However, here are two frequently used methods in order to extend a constructor's function prototype.

有多种方法可以解决在 Javascript 中使用原型扩展构造函数的问题。这些方法中的哪一个是“最佳”解决方案是基于意见的。然而,这里有两个常用的方法来扩展构造函数的函数原型。

ES 2015 Classes:

ES 2015 课程:

class Monster {
  constructor(health) {
    this.health = health
  }
  
  growl () {
  console.log("Grr!");
  }
  
}


class Monkey extends Monster {
  constructor (health) {
    super(health) // call super to execute the constructor function of Monster 
    this.bananaCount = 5;
  }
}

const monkey = new Monkey(50);

console.log(typeof Monster);
console.log(monkey);

The above approach of using ES 2015classes is nothing more than syntactic sugar over the prototypal inheritance pattern in javascript. Here the first log where we evaluate typeof Monsterwe can observe that this is function. This is because classes are just constructor functions under the hood. Nonetheless you may like this way of implementing prototypal inheritance and definitively should learn it. It is used in major frameworks such as ReactJSand Angular2+.

上述使用ES 2015类的方法只不过是对 javascript 中原型继承模式的语法糖。在我们评估的第一个日志中,我们typeof Monster可以观察到这是函数。这是因为类只是引擎盖下的构造函数。尽管如此,您可能喜欢这种实现原型继承的方式,并且绝对应该学习它。它用于主要框架,例如ReactJSAngular2+

Factory function using Object.create():

工厂功能使用Object.create()

function makeMonkey (bananaCount) {
  
  // here we define the prototype
  const Monster = {
  health: 100,
  growl: function() {
  console.log("Grr!");}
  }
  
  const monkey = Object.create(Monster);
  monkey.bananaCount = bananaCount;

  return monkey;
}


const chimp = makeMonkey(30);

chimp.growl();
console.log(chimp.bananaCount);

This method uses the Object.create()method which takes an object which will be the prototype of the newly created object it returns. Therefore we first create the prototype object in this function and then call Object.create()which returns an empty object with the __proto__property set to the Monster object. After this we can initialize all the properties of the object, in this example we assign the bananacount to the newly created object.

此方法使用的Object.create()方法接受一个对象,该对象将是它返回的新创建对象的原型。因此,我们首先在此函数中创建原型对象,然后调用Object.create()它返回一个空对象,其__proto__属性设置为 Monster 对象。之后我们可以初始化对象的所有属性,在这个例子中我们将香蕉计数分配给新创建的对象。

回答by gdanov

the absolutely minimal (and correct, unlike many of the answers above) version is:

绝对最小(并且正确,与上面的许多答案不同)版本是:

function Monkey(param){
  this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }

That's all. You can read here the longer explanation

就这样。你可以在这里阅读更长的解释

回答by Steffomio

For Autodidacts:

对于自学者:

function BaseClass(toBePrivate){
    var morePrivates;
    this.isNotPrivate = 'I know';
    // add your stuff
}
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';


// MiddleClass extends BaseClass
function MiddleClass(toBePrivate){
    BaseClass.call(this);
    // add your stuff
    var morePrivates;
    this.isNotPrivate = 'I know';
}
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';



// TopClass extends MiddleClass
function TopClass(toBePrivate){
    MiddleClass.call(this);
    // add your stuff
    var morePrivates;
    this.isNotPrivate = 'I know';
}
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';


// to be continued...

Create "instance" with getter and setter:

使用 getter 和 setter 创建“实例”:

function doNotExtendMe(toBePrivate){
    var morePrivates;
    return {
        // add getters, setters and any stuff you want
    }
}

回答by Daniel Garmoshka

For traditional extending you can simply write superclass as constructor function, and then apply this constructor for your inherited class.

对于传统的扩展,您可以简单地将超类编写为构造函数,然后将此构造函数应用于您继承的类。

     function AbstractClass() {
      this.superclass_method = function(message) {
          // do something
        };
     }

     function Child() {
         AbstractClass.apply(this);
         // Now Child will have superclass_method()
     }

Example on angularjs:

angularjs 示例:

http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview

http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview

app.service('noisyThing', 
  ['notify',function(notify){
    this._constructor = function() {
      this.scream = function(message) {
          message = message + " by " + this.get_mouth();
          notify(message); 
          console.log(message);
        };

      this.get_mouth = function(){
        return 'abstract mouth';
      }
    }
  }])
  .service('cat',
  ['noisyThing', function(noisyThing){
    noisyThing._constructor.apply(this)
    this.meow = function() {
      this.scream('meooooow');
    }
    this.get_mouth = function(){
      return 'fluffy mouth';
    }
  }])
  .service('bird',
  ['noisyThing', function(noisyThing){
    noisyThing._constructor.apply(this)
    this.twit = function() {
      this.scream('fuuuuuuck');
    }
  }])