簡體   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