[英]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元素的不同方法(即没有
class
和React.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
,如componentWillMount
, this.props
和this.state
, Class 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: 现在我们可以创建一个新车并将其传递给
title
的options
对象,如下所示:
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
,因为toyota
是Car
。 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. 很多这是因为编写原型继承有多么困难,而使用
class
和extend
关键字使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.