Помогите процедурщику переписать маленький скриптик на javascript в ООП стиле
Есть код
function popup(){}
popup.hello = function(){
$("#container").html('<div onclick="popup.bye()">Hello</div>')
}
popup.bye = function(){
alert("Bye");
}
popup.hello()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
Как ни пробовал его сделать через классы, модули или вообще в ООП стиле, ничего не получается.
Так, чтобы не было лишних вопросов с лишним кодом и минусов, решил сделать пример более наглядным
var module =(function (){
var instanceCount = 0;
var CONSTANT_GlOBAL = Math.random();
function render(localRandom){
$("#container").append('\
<div onclick="module.create('+ instanceCount++ +')">\
Module №: '+ instanceCount +'\
<br>Instance random variable: '+localRandom+'\
<br>Module random constant: '+ CONSTANT_GlOBAL +'\
</div><br>');
}
return {
run: function(number){
var localRandom = Math.random();
render(localRandom,number);
},
create: function(number){
this.run(number);
}
}
}());
module.run(1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
3 answers
Вообще реализация чистого ООП подхода на js, до стандарта es6, это одна из самых нетривиальных задач и поэтому у нее есть множество вариантов решения:
Вариант 1 - найти подходящую js библиотеку
Таких библиотек в интернете множество, вот пара примеров:
Вариант 2 - использовать js compiler
Тоже множество вариантов, например:
- Babel - компилирует(транслирует) ванильный es6 код.
- TypeScript - отдельное надмножество js, позиционируемый как js c типами.
Вариант 3 - написать свою реализацию
В ответах уже есть простейшая реализация класса с использованием module-pattern, но ООП на этом не заканчивается, а какже наследование? В js уже реализован механизм наследования через prototype вот так:
MyClass.prototype = Object.create(Base.prototype);
Но это скопирует только методы прототипа, а у нас есть еще свойства самого объекта - в ООП это статические методы и свойства, поэтому я использую следующий паттерн:
function extend(current, base) {
// копируем статические проперти
for (var key in base) {
if (base.hasOwnProperty(key)) {
current[key] = base[key];
}
}
// копируем прототип
current.prototype = Object.create(base.prototype);
};
Теперь можно это использовать:
var Dialogs;
(function (Dialogs) {
// класс Popup
Dialogs.Popup = (function () {
// статическая функция
Popup.show = function () {
alert("show");
};
// Конструктор класса
function Popup(message) {
this._message = message;
}
Popup.prototype.hello = function () {
var self = this;
var message = $(this._message).click(function () {
self.sayBye();
});
$("#container").append(message);
};
Popup.prototype.sayBye = function () {
alert("Bye");
};
Popup.prototype.say = function () {
alert(this._message);
};
return Popup;
}());
// класс Popup2 extend Popup
Dialogs.Popup2 = (function (superClass) {
// наследуем прототип
extend(Popup2, superClass);
function Popup2() {
// вызываем конструктор базового класса
superClass.apply(this, arguments);
}
// перегружаем базовую функцию sayBye
Popup2.prototype.sayBye = function () {
alert("Bye2");
};
return Popup2;
}(Dialogs.Popup)); // передаем базовый класс
})(Dialogs || (Dialogs = {}));
В коде ваше создается область видимости(namespace) Dialogs внутри которой происходит вся магия, так же в ней можно объявлять глобальные переменные:
var Dialogs;
(function (Dialogs) {
var a = 100; // переменная видна внутри Dialogs
Dialogs.b = 100; // можно обратиться как к Dialogs.b снаружи
...
Можно конечно и без нее тогда паттерн класса будет выглядеть так:
var Popup2 = (function (superClass) {
extend(Popup2, superClass);
function Popup2() {
superClass.apply(this, arguments);
}
return Popup2;
}(Popup));
Посмотреть как это работает можно тут: jsfiddle
А React использовать можно?
class Popup extends React.Component {
constructor(props) {
super(props);
this.bye = this.bye.bind(this);
}
render() {
return <div onClick={this.bye}>{this.props.msg}</div>;
}
bye() {
alert("Bye");
}
}
class App extends React.Component {
render() {
return <Popup msg="Hello"/>;
}
}
ReactDOM.render(<App />, document.getElementById('container'));
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.0/react-dom.js"></script>
<div id="container"></div>
var Popup = function () {
this.hello = function() {
$("#container").html('<div onclick="popup.bye()">Hello</div>')
},
this.bye = function(){
alert("Bye");
}
}
var popup = new Popup;
popup.bye()