简体   繁体   English

JavaScript中的原型继承如何真正起作用?

[英]How does prototypal inheritance in JavaScript really works?

I'm still not fully understanding the inheritance dychotomy (prototypal vs. classical) in JavaScript. 我仍然没有完全理解JavaScript中的继承dychotomy(prototypal与经典)。

If the class is just a syntactic sugar over prototypes, how I'm supposed to de-sugar it? 如果这个class只是原型的语法糖,我应该如何去糖呢?

May you show me the different approaches in creating React elements with classes and prototypes (ie without class & React.createClass )? 您可以向我展示使用类和原型创建React元素的不同方法(即没有classReact.createClass )吗?

So, is there a way to get stateful component using native Object.create ? 那么,有没有办法使用本机Object.create获取有状态组件?

Like this: 像这样:

const Hello = Object.create(React.Component.prototype, {
  componentDidMount: {
    value: function() {
      alert('Mounted');
    }
  },
  render: {
    value: function() {
      return <div>I love StackOverflow community! It is so helpful and friendly</div>;
    }
  }
});

ReactDOM.render(<Hello />, document.getElementById('root'));

Seems something like this won't work because of inner lib's restrictions. 由于内部lib的限制,似乎这样的东西不起作用。 But why we can't use it in more natural to prototypal nature of JavaScript? 但是为什么我们不能在JavaScript更自然的原型性质中使用它呢?

There's a note from the official docs: https://facebook.github.io/react/docs/composition-vs-inheritance.html#so-what-about-inheritance 官方文档中有一条说明: https//facebook.github.io/react/docs/composition-vs-inheritance.html#so-what-about-inheritance

[...] we haven't found any use cases where we would recommend creating component inheritance hierarchies [...]我们没有找到任何建议创建组件继承层次结构的用例

But isn't class mostly about inheritance? 但主要是关于继承的class不是吗?

I'm very confused and would like to hear your opinions of what I am doing and thinking wrong? 我很困惑,想听听你对我在做什么和想错的看法?

I've asked that question at Reactiflux and Brendan Hurley proposed this: https://codepen.io/niechea/pen/xdVEvv?editors=0010 我在Reactiflux和Brendan Hurley提出这个问题提出了这个问题: https ://codepen.io/niechea/pen/xdVEvv?edit = 0010

function MyComponent(props) {
  this.props = props;
  this.state = {
    clickCount: 0,
  };
}

MyComponent.prototype = Object.create(React.Component.prototype);

MyComponent.prototype.clickHandler = function() {
  this.setState({
    clickCount: this.state.clickCount + 1,
  });
}

MyComponent.prototype.render = function() {
  return (
    <div>
      <p>Hello, {this.props.name}.</p>
      <p>You have clicked {this.state.clickCount} time(s)!</p>
      <button onClick={this.clickHandler.bind(this)}>Click me</button>
    </div>
  );
}

ReactDOM.render(<MyComponent name="Bogdan" />, document.getElementById('app'));

Is his solution truly prototypal? 他的解决方案真的是原型吗?


Here are some references: 以下是一些参考:


* The question is mostly about inheritance, not about React. *问题主要是关于继承,而不是关于React。 React here is just a reference. 这里的反应只是一个参考。

If the class is just a syntactic sugar over prototypes, how I'm supposed to de-sugar it? 如果这个类只是原型的语法糖,我应该如何去糖呢?

For example this is a good article on the matter. 例如,这是一篇关于此事的好文章 So if you have create an entity Animal using class : 因此,如果您使用class创建一个实体Animal

class AnimalES6 {
    constructor(name) {
        this.name = name;
    }

    doSomething() {
        console.log("I'm a " + this.name);
    }
}

var lionES6 = new AnimalES6("Lion");
lionES6.doSomething();

Prototypal version would look something like this: Prototypal版本看起来像这样:

var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    // Methods
    Animal.prototype.doSomething = function () {
        console.log("I'm a " + this.name);
    };
    return Animal;
})();

var lion = new Animal("Lion");
lion.doSomething();

It's even more complicated with extend functionality (eg TypeScript simulation of inheritance): extend功能(例如继承的TypeScript模拟)更加复杂:

var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

May you show me the different approaches in creating React elements with classes and prototypes (ie without class & React.createClass)? 您可以向我展示使用类和原型创建React元素的不同方法(即没有类和React.createClass)吗?

There are also SO questions already answered for this question, eg this one . 对于这个问题,也已经回答了一些问题,例如这个问题。

But still, in my opinion the real question is: Do you want to? 但在我看来,真正的问题是: 你想要吗?

Since you linked Eric Elliot's article you probably noticed that there is a kind of dispute around EC6's class es in javascript world. 由于你链接了Eric Elliot的文章,你可能已经注意到在javascript世界中围绕EC6的class es存在一种争议。 Apart from the examples you posted, there are some aggregates of opinions from more developers, eg this github repo , here and more. 除了您发布的例子,也有来自更多的开发商的意见有些聚集,如该GitHub库这里多。 There are also articles defending the purpose of class ... 还有一些文章捍卫了class的目的......

Anyway, the creators of React seem to have embraced the "evilness" of class es and as you pointed out you would bump into problems when trying to use prototypal approach with React. 无论如何,React的创造者似乎已经接受了class es的“邪恶”,正如你所指出的那样,在尝试使用React的原型方法时会遇到问题。 So in my opinion: why bother with it? 所以在我看来:为什么要烦恼呢? I too like prototypal nature of javascript more and I also like the ReactJS framework, but in my opinion it would be better to come up with some new framework which combines the best of both, something like "Protypal React" rather than trying to enforce prototyping on React when it wasn't intended for such use. 我也喜欢javascript的原型性质,我也喜欢ReactJS框架,但在我看来,最好能够提出一些新的框架,它结合了两者的优点,比如“Protypal React”,而不是试图强制进行原型设计。当React不打算用于此类用途时。

But isn't class mostly about inheritance? 但主要是关于继承的课程不是吗?

That was probably answered in the comments but no. 这可能在评论中得到了回答,但没有。 Classes have their advantages as has Composition design concept over inheritance. 类具有Composition设计概念优于继承的优点。 It depends on the needs but many frameworks/libraries build on object-oriented languages which use class embrace composition over inheritance, eg Unity. 取决于需求,但许多框架/库建立在面向对象的语言上,这些语言使用类包含组合而不是继承,例如Unity。

Anyway, really nice question and I wanted to share my thoughts on the matter too. 无论如何,非常好的问题,我想分享我对此事的看法。 Hopefully it will help you form an opinion. 希望它能帮助你形成一个观点。

So, like others have said, the best way to think about classes in Javascript is as syntactical sugar over prototypal inheritance. 因此,就像其他人所说的那样,在Javascript中考虑类的最佳方式是作为原型继承的语法糖。 It's best to avoid the association of classical inheritance and classes in other languages, particularly if you have been taught it in university/school. 最好避免将经典继承与其他语言的课程联系起来,特别是如果你在大学/学校教过它。

Prototypal inheritance can be considered to be more expressive than classical inheritance. 原型继承可以被认为比经典继承更具表现力

Using the 'class' keyword in JS is syntactically closer to classical inheritance. 在JS中使用'class'关键字在语法上更接近于经典继承。

For example, you have in your UI these components: Link, MediaLink, ImageLink & VideoLink. 例如,您在UI中有这些组件:Link,MediaLink,ImageLink和VideoLink。 In classical inheritance, you might be tempted to have a Link class, a MediaLink class that extends Link, and VideoLink & ImageLink classes that extend MediaLink class where MediaLink is an abstract class (not to be instantiated). 在经典继承中,您可能想要一个Link类,一个扩展Link的MediaLink类,以及扩展MediaLink类的VideoLink和ImageLink类,其中MediaLink是一个抽象类(不是实例化的)。 If you were to implement this hierarchy with stateful React components using prototypal inheritance, the states could theoretically be easily manipulated by calling super (or calling React.Component.apply(this, props); as in an answer above), however the core of React, ie 'render' would be a little abstract and difficult to extend. 如果你使用原型继承来实现具有有状态React组件的这个层次结构,理论上可以通过调用super(或调用React.Component.apply(this, props);在上面的答案中)来轻松地操作状态,但是React,即'render'会有点抽象,难以扩展。 A link might return <a ...>link</a> , what would a MediaLink return? 链接可能会返回<a ...>link</a> ,MediaLink会返回什么? How would a VideoLink work with the return of its parent? VideoLink如何与其父级的回归一起工作? Or do you begin to negate the parent rendering functions and replace them entirely? 或者你是否开始否定父渲染函数并完全替换它们? It gets awkward at this point and it begins to look a little like spaghetti. 它在这一点上变得尴尬,它看起来有点像意大利面条。

Instead, you can compose components. 相反,您可以组成组件。 In classical inheritance, you can think of your Component class as a final class that has inherited from an abstract class , React.Component. 在经典继承中,您可以将Component类视为从抽象类 React.Component继承的最终类 To implement the above, you add behaviour to your class that may be common to a lot of classes. 为了实现上述功能,您可以向类中添加许多类可能常见的行为。 For example, Components TextLink, VideoLink & ImageLink all have a boolean state 'clicked'. 例如,组件TextLink,VideoLink和ImageLink都具有“单击”的布尔状态。 I think this article summarises these ideas pretty well. 我认为这篇文章很好地总结了这些想法。

Another way of composing components is by wrapping components with other components and passing state via props to their children. 组成组件的另一种方法是将组件与其他组件包装在一起,并通过props将状态传递给子组件。 A really crude example might be: Link, SwitchedLink. 一个非常粗糙的例子可能是:Link,SwitchedLink。 The Link class has a prop 'active' which determines whether or not it is active. Link类具有prop'active',用于确定它是否处于活动状态。 The Switched link will render either <Link /> or <Link active /> as a single child depending on its own state. Switched链接将根据其自身状态将<Link /><Link active />呈现为单个子项。 The child is stateless. 孩子是无国籍的。 The parent has state. 父母有州。 These are common patterns with React and thus why there is no need for a classical multiple level inheritance structure. 这些是React的常见模式,因此不需要经典的多级继承结构。

I hope that addresses your question. 我希望能解决你的问题。

Sorry i can't write any comment. 对不起,我不能写任何评论。 I think you must initialize super class constructor below. 我认为你必须在下面初始化超类构造函数。

function MyComponent(props) {
  React.Component.prototype.apply(this, props);
  this.props = props;
  this.state = {
    clickCount: 0,
  };
}

In React, there are two types of component; 在React中,有两种类型的组件; the functional component and the class-based component. 功能组件和基于类的组件。 Here is a link to the react docs explaining the difference. 以下是解释差异的反应文档的链接

An interesting thing to note is that a Component in React is essentially a function that returns some amount of JSX. 值得注意的一件事是,React中的Component本质上是一个返回一定量JSX的函数。 So you can write a function and have it return JSX and that would be a component but without key internal details like the lifecycle methods such as componentWillMount , this.props and this.state that the Class Component would provide to you out of the box. 因此,您可以编写一个函数并让它返回JSX,这将是一个组件,但没有关键的内部细节,如lifecycle methods ,如componentWillMountthis.propsthis.stateClass Component将提供给您开箱即用。

So you can create a functional component (ie a function that returns JSX) and have prototypes in it like so 因此,您可以创建一个功能组件(即返回JSX的函数)并在其中包含原型

const Card = (props) => {
this.sayHello = function () {
  console.log('hello hello hello');
};

return (  
  <View style={styles.containerStyle}>
    {props.children}
    { this.sayHello() }
  </View>
 );
};

and this would pass for a Component created in React without the normal terminology you are used to. 这将传递在React中创建的Component,而不使用您习惯的常规术语。

Hope this is helpful 希望这是有帮助的

I think its important to break things down in easy to understand ways. 我认为以易于理解的方式解决问题非常重要。 You are asking about prototypal inheritance in JavaScript and then how it applies regarding React. 您在JavaScript中询问原型继承,然后询问它如何应用于React。 First of all, I have to say that prototype is not easy to explain, nor is it well documented, but lets focus on one thing at a time. 首先,我不得不说原型不容易解释,也没有很好的记录,但我们一次只关注一件事。 So yes, JavaScript does not have a concept of object inheritance, but instead prototypal inheritance. 所以,是的,JavaScript没有对象继承的概念,而是原型继承。 Lets create a function on cars: 让我们在汽车上创建一个功能:

function Car() {

}

We can then make a Toyota that will inherit from Car . 然后我们可以制造一辆将继承Car的丰田Car So lets see what a constructor function looks like when not using classes. 因此,让我们看看不使用类时构造函数的样子。

function Car(options) {
  this.title = options.title;
}

So every single Car has to have options passed into it and this options has a title . 因此, Car都必须有options传递给它,这个options有一个title

Now we can create a new car and pass it an options object of title like so: 现在我们可以创建一个新车并将其传递给titleoptions对象,如下所示:

const car = new Car({ title: 'Focus' });
car;

Now lets add a method on to this Car class, so we are adding this method to the prototype object of the constructor like so: 现在让我们为这个Car类添加一个方法,所以我们将这个方法添加到构造函数的原型对象中,如下所示:

Car.prototype.drive = function() {
  return 'vroom!';
}

Now when we call car.drive(); 现在我们打电话给car.drive(); we get a result of vroom! 我们得到了vroom!的结果vroom! .

So this is basic object creation with JavaScript. 所以这是使用JavaScript创建的基本对象。 We create the constructor object, then use the new keyword on it and then we can add methods to the object, but we add it on the prototype property of the constructor: 我们创建构造函数对象,然后在其上使用new关键字,然后我们可以向对象添加方法,但我们将它添加到构造函数的prototype属性中:

constructorFunction.prototype.methodWeAdd = function() {
  return something;
};

Now I will add an object that will inherit from Car to set up the prototypal link. 现在我将添加一个将从Car继承的对象来设置原型链接。

So I will make a Toyota and I want it to inherit from this Car object like so: 所以我将制作一辆丰田,我希望它继承这个Car对象,如下所示:

function Toyota(options) {

}

So I passed it an options object as an argument which contains the color of the car. 所以我传给了一个options对象作为包含汽车color的参数。

function Toyota(options) {
  this.color = options.color;
}

const toyota = new Toyota({ color: 'grey', title: 'Prius' });
console.log(toyota);

So I want the Toyota to inherit all the properties and methods of a Car because toyota is a Car . 所以我想丰田继承所有属性和方法的Car ,因为toyotaCar So how do I create a link between the two? 那么如何在两者之间建立联系呢? How do I delegate toyota to be able to call all the methods that Car has? 如何委托toyota Car能够调用Car所有方法?

So whenever I call Toyota the constructor I want to ensure I run any initialization that occurs in the Car as well like so: 所以每当我打电话给Toyota的构造函数时,我想确保我在Car运行任何初始化,如下所示:

function Toyota(options) {
  Car.call(this.options);
  this.color = options.color;
}

I also want to make sure I can call the Car drive method from a Toyota object like so: 我还想确保我可以从Toyota对象中调用Car drive方法,如下所示:

Toyota.prototype = Object.create(Car.prototype);
Toyota.prototype.constructor = Toyota;

Now I should be able to add a method to Toyota prototype like so: 现在我应该能够为Toyota原型添加一个方法,如下所示:

Toyota.prototype.honk = function() {
  return 'beep beep!';
};

Now I should be able to call the following: 现在我应该可以调用以下内容:

const toyota = new Toyota({ color: 'grey', title: 'Prius' });
console.log(toyota);
console.log(toyota.drive());
console.log(toyota.honk());

So toyota should be inheriting some level of setup that is coming from Car . 所以, toyota应该继承一些来自Car的设置。 So the above painful process I just put myself through, is to say to you, this is how you would de-sugar it in plain vanilla JavaScript. 所以我刚刚完成的上述痛苦过程,就是对你说,这就是你用普通的JavaScript来解冻它的方法。 Not sure why you would want to, but here it is. 不知道为什么你会这么想,但在这里。

So the above is how prototypal inheritance is done in JavaScript. 所以上面是如何在JavaScript中完成原型继承。

The idea of classes is not just for syntactic sugar for the sake of it, it's to remove such a painstaking process from our workload so we can focus on other challenges in application development. 类的想法不仅仅是为了语法糖,它是从我们的工作量中去除这样一个艰苦的过程,因此我们可以专注于应用程序开发中的其他挑战。

When you look at new developments such as with ES6, yes some of it falls under syntactic sugar, but the idea is to remove or resolve a painstaking process from an everyday task, such as creating classes or accessing properties/methods from an object such as with destructuring. 当你看到像ES6这样的新开发时,是的,其中一些属于语法糖,但是想法是从日常任务中删除或解决一个艰苦的过程,例如创建类或从对象访问属性/方法,如与解构。

In practice, the JavaScript community has embraced the use of classes, you will see a ton of libraries utilizing classes, whereas template strings and destructuring was a nice addition, classes has dramatically changed the way we write JavaScript code. 在实践中,JavaScript社区已经接受了类的使用,你会看到大量的库利用类,而模板字符串和解构是一个很好的补充,类已经戏剧性地改变了我们编写JavaScript代码的方式。

A lot of this is due to how difficult it was to write prototypal inheritance whereas with classes its a lot easier using the class and extend keywords. 很多这是因为编写原型继承有多么困难,而使用classextend关键字使class更容易。

When React first came out, the idea of using prototypal inheritance all over the place to create a component was considered laughable just because there is so much setup you have to do (as you saw above) whenever you are creating a subclass that inherits from another object. 当React第一次出现时,在整个地方使用原型继承来创建组件的想法被认为是可笑的,因为只要你创建一个继承自另一个的子类,就必须做很多设置(如上所述)宾语。 Instead, to create a component, we would use the createClass helper. 相反,要创建一个组件,我们将使用createClass帮助器。 This is a part of React, but I want to show you an instance of where some existing library migrated towards classes. 这是React的一部分,但我想向您展示一个现有库迁移到类的位置的实例。

So it used to be the case that with React, we had this helper called createClass like so: 所以过去的情况是,使用React,我们有这个名为createClass的帮助器,如下所示:

React.createClass({

});

And you would pass in an object and it might have a method like so: 你会传入一个对象,它可能有一个像这样的方法:

React.createClass({
   doSomething() {

   },

   doSomethingElse() {

    }
});

This was not the worst thing in the world, but it definitely made it really hard whenever you wanted to subclass a component or create a component with some about of behavior and reuse it somewhere else. 这不是世界上最糟糕的事情,但是无论何时你想要为一个组件创建一个子组件或创建一个具有某些行为的组件并在其他地方重用它,它肯定会变得非常困难。 These days React has migrated towards using the class syntax instead, for example: 这些天React已经转向使用类语法,例如:

class MyComponent extends Component {
    doSomething() {

    }

    doSomethingElse() {

    }
}

The benefit of this approach is if I wanted to make some generic, very reusable component to utilize somewhere else in my codebase, I can do so much more easily with a class-based approach as opposed to using createClass() syntax and definitely much easier than the prototypal inheritance syntax way. 这种方法的好处是,如果我想制作一些通用的,非常可重用的组件来利用我的代码库中的其他地方,我可以使用基于类的方法更容易地做到这一点,而不是使用createClass()语法并且更容易比原型继承语法方式。

You will also notice the inheritance chain is much more obvious. 您还会注意到继承链更加明显。 So this Component keyword is a generic component that is provided by the React library. 因此,此Component关键字是React库提供的通用组件。

Anyone who glances at this class can see that its creating a component and borrows functionality from Component . 浏览此类的任何人都可以看到它创建一个组件并从Component借用功能。

The benefit of this is that if you are a fan of Object Oriented Programming and work with React, the class syntax is generally easier to work with than the older createClass() syntax. 这样做的好处是,如果您是面向对象编程的粉丝并使用React,那么类语法通常比旧的createClass()语法更容易使用。

Prototype inheritance is when you put a method on the original constructor here 原型继承是指在此处将方法放在原始构造函数上

function Cat(name,breed){
   this.name = name;
   this.breed = breed;
}

It will be inherited by the rest of them 它将由其余的继承

const susie = new Cat('Susie', 'Persian');

If I create a new array, const names = ['Gilbert','Mia'] I just crated a brand new array, but now names.join() is a method names.pop)() is a method. 如果我创建一个新数组, const names = ['Gilbert','Mia']我只是创建了一个全新的数组,但现在names.join()是一个方法names.pop)()是一个方法。

在此输入图像描述

We have all these methods on top of names. 我们在名称之上有所有这些方法。 Where did they com from? 他们来自哪里? Well, we have the mother Array capital A 好吧,我们有母Array 资本A

If you look inside of it, you'll know that Array has many prototype methods, 如果你看一下它,你就会知道Array有很多原型方法,

在此输入图像描述

which means that when you create Cat 这意味着当你创建Cat

const susie = new Cat('Susie', 'Persian');

from the mother array or from the mother Cat 来自母阵或来自Cat

function Cat(name,breed){
   this.name = name;
   this.breed = breed;
}

every single instance of that inherits those methods. 每个实例都继承了这些方法。

 function Cat(name,breed){ this.name = name; this.breed = breed; } Cat.prototype.meow = function() { Console.log(`Meow meow! My name is ${this.name}`); } const susie = new Cat('Susie', 'Persian'); const mimi = new Cat('Mimi', 'Persian'); 

Now you can see the prototype Cat.prototype.meow is now inherited by every instance cat. 现在您可以看到原型Cat.prototype.meow现在由每个实例cat继承。

const susie = new Cat('Susie', 'Persian');
const mimi = new Cat('Mimi', 'Persian');

susie.meow(); -> Meow meow! - >喵喵! My name is Susie mimi.meow(); 我的名字是Susie mimi.meow(); -> Meow meow! - >喵喵! My name is Mimi 我的名字是咪咪

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM