繁体   English   中英

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

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

我仍然没有完全理解JavaScript中的继承dychotomy(prototypal与经典)。

如果这个class只是原型的语法糖,我应该如何去糖呢?

您可以向我展示使用类和原型创建React元素的不同方法(即没有classReact.createClass )吗?

那么,有没有办法使用本机Object.create获取有状态组件?

像这样:

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'));

由于内部lib的限制,似乎这样的东西不起作用。 但是为什么我们不能在JavaScript更自然的原型性质中使用它呢?

官方文档中有一条说明: https//facebook.github.io/react/docs/composition-vs-inheritance.html#so-what-about-inheritance

[...]我们没有找到任何建议创建组件继承层次结构的用例

但主要是关于继承的class不是吗?

我很困惑,想听听你对我在做什么和想错的看法?

我在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'));

他的解决方案真的是原型吗?


以下是一些参考:


*问题主要是关于继承,而不是关于React。 这里的反应只是一个参考。

如果这个类只是原型的语法糖,我应该如何去糖呢?

例如,这是一篇关于此事的好文章 因此,如果您使用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版本看起来像这样:

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();

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 __();
};

您可以向我展示使用类和原型创建React元素的不同方法(即没有类和React.createClass)吗?

对于这个问题,也已经回答了一些问题,例如这个问题。

但在我看来,真正的问题是: 你想要吗?

由于你链接了Eric Elliot的文章,你可能已经注意到在javascript世界中围绕EC6的class es存在一种争议。 除了您发布的例子,也有来自更多的开发商的意见有些聚集,如该GitHub库这里多。 还有一些文章捍卫了class的目的......

无论如何,React的创造者似乎已经接受了class es的“邪恶”,正如你所指出的那样,在尝试使用React的原型方法时会遇到问题。 所以在我看来:为什么要烦恼呢? 我也喜欢javascript的原型性质,我也喜欢ReactJS框架,但在我看来,最好能够提出一些新的框架,它结合了两者的优点,比如“Protypal React”,而不是试图强制进行原型设计。当React不打算用于此类用途时。

但主要是关于继承的课程不是吗?

这可能在评论中得到了回答,但没有。 类具有Composition设计概念优于继承的优点。 取决于需求,但许多框架/库建立在面向对象的语言上,这些语言使用类包含组合而不是继承,例如Unity。

无论如何,非常好的问题,我想分享我对此事的看法。 希望它能帮助你形成一个观点。

因此,就像其他人所说的那样,在Javascript中考虑类的最佳方式是作为原型继承的语法糖。 最好避免将经典继承与其他语言的课程联系起来,特别是如果你在大学/学校教过它。

原型继承可以被认为比经典继承更具表现力

在JS中使用'class'关键字在语法上更接近于经典继承。

例如,您在UI中有这些组件:Link,MediaLink,ImageLink和VideoLink。 在经典继承中,您可能想要一个Link类,一个扩展Link的MediaLink类,以及扩展MediaLink类的VideoLink和ImageLink类,其中MediaLink是一个抽象类(不是实例化的)。 如果你使用原型继承来实现具有有状态React组件的这个层次结构,理论上可以通过调用super(或调用React.Component.apply(this, props);在上面的答案中)来轻松地操作状态,但是React,即'render'会有点抽象,难以扩展。 链接可能会返回<a ...>link</a> ,MediaLink会返回什么? VideoLink如何与其父级的回归一起工作? 或者你是否开始否定父渲染函数并完全替换它们? 它在这一点上变得尴尬,它看起来有点像意大利面条。

相反,您可以组成组件。 在经典继承中,您可以将Component类视为从抽象类 React.Component继承的最终类 为了实现上述功能,您可以向类中添加许多类可能常见的行为。 例如,组件TextLink,VideoLink和ImageLink都具有“单击”的布尔状态。 我认为这篇文章很好地总结了这些想法。

组成组件的另一种方法是将组件与其他组件包装在一起,并通过props将状态传递给子组件。 一个非常粗糙的例子可能是:Link,SwitchedLink。 Link类具有prop'active',用于确定它是否处于活动状态。 Switched链接将根据其自身状态将<Link /><Link active />呈现为单个子项。 孩子是无国籍的。 父母有州。 这些是React的常见模式,因此不需要经典的多级继承结构。

我希望能解决你的问题。

对不起,我不能写任何评论。 我认为你必须在下面初始化超类构造函数。

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

在React中,有两种类型的组件; 功能组件和基于类的组件。 以下是解释差异的反应文档的链接

值得注意的一件事是,React中的Component本质上是一个返回一定量JSX的函数。 因此,您可以编写一个函数并让它返回JSX,这将是一个组件,但没有关键的内部细节,如lifecycle methods ,如componentWillMountthis.propsthis.stateClass Component将提供给您开箱即用。

因此,您可以创建一个功能组件(即返回JSX的函数)并在其中包含原型

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

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

这将传递在React中创建的Component,而不使用您习惯的常规术语。

希望这是有帮助的

我认为以易于理解的方式解决问题非常重要。 您在JavaScript中询问原型继承,然后询问它如何应用于React。 首先,我不得不说原型不容易解释,也没有很好的记录,但我们一次只关注一件事。 所以,是的,JavaScript没有对象继承的概念,而是原型继承。 让我们在汽车上创建一个功能:

function Car() {

}

然后我们可以制造一辆将继承Car的丰田Car 因此,让我们看看不使用类时构造函数的样子。

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

因此, Car都必须有options传递给它,这个options有一个title

现在我们可以创建一个新车并将其传递给titleoptions对象,如下所示:

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

现在让我们为这个Car类添加一个方法,所以我们将这个方法添加到构造函数的原型对象中,如下所示:

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

现在我们打电话给car.drive(); 我们得到了vroom!的结果vroom!

所以这是使用JavaScript创建的基本对象。 我们创建构造函数对象,然后在其上使用new关键字,然后我们可以向对象添加方法,但我们将它添加到构造函数的prototype属性中:

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

现在我将添加一个将从Car继承的对象来设置原型链接。

所以我将制作一辆丰田,我希望它继承这个Car对象,如下所示:

function Toyota(options) {

}

所以我传给了一个options对象作为包含汽车color的参数。

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

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

所以我想丰田继承所有属性和方法的Car ,因为toyotaCar 那么如何在两者之间建立联系呢? 如何委托toyota Car能够调用Car所有方法?

所以每当我打电话给Toyota的构造函数时,我想确保我在Car运行任何初始化,如下所示:

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

我还想确保我可以从Toyota对象中调用Car drive方法,如下所示:

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

现在我应该能够为Toyota原型添加一个方法,如下所示:

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

现在我应该可以调用以下内容:

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

所以, toyota应该继承一些来自Car的设置。 所以我刚刚完成的上述痛苦过程,就是对你说,这就是你用普通的JavaScript来解冻它的方法。 不知道为什么你会这么想,但在这里。

所以上面是如何在JavaScript中完成原型继承。

类的想法不仅仅是为了语法糖,它是从我们的工作量中去除这样一个艰苦的过程,因此我们可以专注于应用程序开发中的其他挑战。

当你看到像ES6这样的新开发时,是的,其中一些属于语法糖,但是想法是从日常任务中删除或解决一个艰苦的过程,例如创建类或从对象访问属性/方法,如与解构。

在实践中,JavaScript社区已经接受了类的使用,你会看到大量的库利用类,而模板字符串和解构是一个很好的补充,类已经戏剧性地改变了我们编写JavaScript代码的方式。

很多这是因为编写原型继承有多么困难,而使用classextend关键字使class更容易。

当React第一次出现时,在整个地方使用原型继承来创建组件的想法被认为是可笑的,因为只要你创建一个继承自另一个的子类,就必须做很多设置(如上所述)宾语。 相反,要创建一个组件,我们将使用createClass帮助器。 这是React的一部分,但我想向您展示一个现有库迁移到类的位置的实例。

所以过去的情况是,使用React,我们有这个名为createClass的帮助器,如下所示:

React.createClass({

});

你会传入一个对象,它可能有一个像这样的方法:

React.createClass({
   doSomething() {

   },

   doSomethingElse() {

    }
});

这不是世界上最糟糕的事情,但是无论何时你想要为一个组件创建一个子组件或创建一个具有某些行为的组件并在其他地方重用它,它肯定会变得非常困难。 这些天React已经转向使用类语法,例如:

class MyComponent extends Component {
    doSomething() {

    }

    doSomethingElse() {

    }
}

这种方法的好处是,如果我想制作一些通用的,非常可重用的组件来利用我的代码库中的其他地方,我可以使用基于类的方法更容易地做到这一点,而不是使用createClass()语法并且更容易比原型继承语法方式。

您还会注意到继承链更加明显。 因此,此Component关键字是React库提供的通用组件。

浏览此类的任何人都可以看到它创建一个组件并从Component借用功能。

这样做的好处是,如果您是面向对象编程的粉丝并使用React,那么类语法通常比旧的createClass()语法更容易使用。

原型继承是指在此处将方法放在原始构造函数上

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

它将由其余的继承

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

如果我创建一个新数组, const names = ['Gilbert','Mia']我只是创建了一个全新的数组,但现在names.join()是一个方法names.pop)()是一个方法。

在此输入图像描述

我们在名称之上有所有这些方法。 他们来自哪里? 好吧,我们有母Array 资本A

如果你看一下它,你就会知道Array有很多原型方法,

在此输入图像描述

这意味着当你创建Cat

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

来自母阵或来自Cat

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

每个实例都继承了这些方法。

 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'); 

现在您可以看到原型Cat.prototype.meow现在由每个实例cat继承。

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

susie.meow(); - >喵喵! 我的名字是Susie mimi.meow(); - >喵喵! 我的名字是咪咪

暂无
暂无

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

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