Jump to content

Javascript в ооп стиле


li4e
 Share

Recommended Posts

Добрый день.

Видел во многих вакансиях, требование уметь писать на Javascript в ООП стиле.

Вот написал простой калькулятор. 

Посмотрите пожалуйста, это ли имелось ввиду?

http://codepen.io/anon/pen/vLjJmM?editors=0010

Link to comment
Share on other sites

Посмотрите пожалуйста, это ли имелось ввиду?
 

 

Я думаю что не совсем. Хотя твоя функция технически и работает как конструктор, на практике return насколько я знаю никогда не используется. Свойства возвращаемого объекта задаются через this. Сами свойства не обязательно должны быть объектам, а вот в качестве аргумента конструктора как правило используют объекты. То есть в твоей реализации выйдет вот что.

function Calculator(obj){        this.el = obj.el;                          // Элемент с калькулятором        this.field = obj.el.querySelector('.field');  // Элемент с экраном калькулятора        this.do = [''];                          // Массив с действиями калькулятор в формате [1,'+',2]        this.math = {                             // Обьект с операторами            '+': function(x,y) {return x + y},            '-': function(x,y) {return x - y},            '×': function(x,y) {return x * y},            '÷': function(x,y) {return x / y}        };        this.clear = function() {                 // Метод для очистки экрана калькулятора            this.do = [''];            this.updateField();        };        this.updateField = function () {          // Метод для обновления значений на экране калькуляторе            var text = '';            for(var i = 0; i < this.do.length;i++){                text += ' ' + (this.do[i] === '' ? (this.do.length-1 !== i ? '0' : '') : this.do[i]);            }            this.field.innerHTML = text;        };        this.pressButton = function (text) {                  // Метод обрабатывающий нажатие кнопок калькулятора            var num = text !== '.' ? parseFloat(text) : '.';            if(text === 'Clear') {                      // Если нажата кнопка Clear                this.clear();            } else if (!isNaN(num) || text === '.') {   // Если нажата кнопка с числом или точка                var last = this.do[this.do.length-1];                last += last.length === 0 && num === '.' ? '0.' : String(num);                var txt = last.split('.');                txt = txt.length > 1 ? String(parseInt(txt[0]))+'.'+txt[1]: parseInt(txt[0]);                this.do[this.do.length-1] = txt;                this.updateField();            } else {                                    // Если нажата кнопка с оператором                if(text === '=' && this.do.length >= 3 && this.do[this.do.length-1] !== '') {                    var result = parseFloat(this.do[0]);                    result = isNaN(result) ? 0 : result;                    this.do.splice(0,1);                    while(this.do.length > 0) {                        result = this.math[this.do[0]](result, parseFloat(this.do[1] === '' ? 0 : this.do[1]));                        this.do.splice(0,2);                    }                    this.do = [result];                    this.field.innerHTML = result;                } else if(text !== '=') {                    this.do.push(text, '');                    this.updateField();                }            }        };     // Метод инициализации в котором вешается обработчик на клик на калькулятор            var $this = this;            this.el.onclick = function (event) {                var target = event.target;                if(target.tagName === 'BUTTON') {                    $this.pressButton(target.innerHTML);                }            }}

Создание объекта калькулятор.

var calc = new Calculator({el: document.querySelector('.calculator')}); 

Запуск

calc.init(); 

Но один фиг это не то что надо.  Так как объекту калькулятор все эти методы, которые ты в него напихал не нужны. Никто не будет запускать  calc.updateField() , по этому это свойство должно быть в прототипе.

 

ЗЫ Если где не прав, исправляйте. Сам учусь.

ЗЫЫ Калькулятор кстати не правильно работает, попробуй 2+2*2 ;)


Нашел темку про js литературу, ушел читать

 

По ооп и прототипам намного лучше Кантора будет Николас Закас .  

  • Like 1
Link to comment
Share on other sites

1. @li4e, см. коммент http://htmlforum.ru/index.php?showtopic=55226#entry369653

2. забудь про DOM. Напиши калькулятор который работает БЕЗ DOM

3. если ты сумеешь правильно написать таковой, то визуализировать его работу труда не составит

 

я тебе предлагаю упростить твой калькулятор до такого

let obj = new Calculator();obj.add('7');obj.add('+');obj.add('2');obj.add('fffff'); // должно бросаться исключениеlet result = obj.calculate(); // результат или ошибкаobj.clear();class Calculator {    constructor() {        // your code here    }    add(some) {        // your code here    }    clear() {        // your code here    }    calculate() {        // your code here        }}
  • Like 1
Link to comment
Share on other sites

Спасибо вам за ответы сейчас приступлю по новой делать.

 

 

ЗЫЫ Калькулятор кстати не правильно работает, попробуй 2+2*2 ;)

 

Сначала хотел написать что вы не правы, так как задумывалось как пошаговые расчеты слева на права, но подумал, что будет логичнее если вычисления будут производиться не линейно, а в зависимости от приоритетности знаков.

Edited by li4e
Link to comment
Share on other sites

Сделал
http://codepen.io/anon/pen/obypdP?editors=0012
Проверьте пожалуйста все ли верно.

Действия можно добавлять как с помощью метода add, так и при создании объекта передав в конструктор строку  с пробелами между действиями, как в примере.

Есть вопросы только про приватные свойства и методы, их получается пока нет в es6 в классах?

Edited by li4e
Link to comment
Share on other sites

new Calculator({actions: '2 + 2 * 2'})
не понятно, зачем указывать объект

{actions: '2 + 2 * 2'}
который содержит строку, которая должна быть разбита пробелами, чтобы потом разбирать эту строку по пробелам, вместо того, чтобы просто указать

{new Calculator([2, '+', 2, '*', 2])
 

throw new Error("Должно быть число или операция, а не: "+val);
на английском

 

    static getOperations() {        return {            '+': {priority: 1,func: (a,B) => a + b},            '-': {priority: 2,func: (a,B) => a - b},            '*': {priority: 3,func: (a,B) => a * b},            '/': {priority: 4,func: (a,B) => a / b}        };    }
каждый раз создаешь и возвращаешь объект. Зачем?

 

    static checkOperation(val) {        return Calculator.getOperations()[val] !== undefined;    }
не так проверяется наличие/отсутствие в объекте ключа

должно проверяться либо hasOwnProperty либо key in object (в зависимости от ситуации)

из-за того, что используешь parseFloat, твой калькулятор позволяет писать

'2_ololo + 1' // 3
мне кажется это не то поведение, которое ожидается от калькулятора

похоже на хард код

        if(isOperation && Calculator.checkOperation(actions[actions.length-1])) { // Если последнее и новое действие это операция то заменяем старое новым            actions[actions.length-1] = val;        } else if (!isOperation || (isOperation && actions.length > 0)) {            if(!isOperation && Calculator.checkNumber(actions[actions.length-1])) {                actions.push('+'); // (!) ТУТ            }            actions.push(val);        }    }
Сделай еще проще:

1. на входе проверяешь регулярками

2. чтобы получить результат, делаешь так

var fn = new Function('return 2 + 2 * 2;');console.log(fn());
  • Like 3
Link to comment
Share on other sites

var fn = new Function('return 2 + 2 * 2;');console.log(fn());

Спасибо. Это нечто.

Вот что получилось.

http://codepen.io/anon/pen/eJKPVL?editors=1011

Валидацию тоже можно сделать регулярками, но пока не хватает знаний регулярок.

Такая реализация уже имеет право на жизнь?

Link to comment
Share on other sites

Такая реализация уже имеет право на жизнь?

любая реализация имеет право на жизнь, даже нерабочая =)

== мелочи ==

в нескольких местах

if(Object.prototype.toString.call( arr ) !== '[object Array]') {
вместо Array.isArray

в чем смысл?

this.actions = this.actions.concat(val);
== главное ==

могу ошибаться, но при данных

let obj = new Calculator([2, '+', '*', 2.15]);
твой калькулятор тихо умирает

валидация не отрабатывает, ошибка не перехватывается

мне кажется тебе надо еще больше упростить провеку: не пытайся валидировать весь массив, валидурий входящие данные поэлементно

думаю, проверку надо делать на этапе добавления данных, а не запуске (просчете) калькулятора

---

примерно так (обработка ошибок по вкусу)

'use strict';const PRIVATE = Symbol();class Calculator {    constructor(...items) {        this[PRIVATE] = [];        this.add(...items);    }    clear() {        this[PRIVATE].length = 0;    }    add(...items) {      let valideted = items.filter(item => Calculator.isValid(item));      this[PRIVATE].push(...valideted);    }    calculate() {        let string = this[PRIVATE].join('');        let fn;              try {          fn = new Function(`return ${string} + 0;`);        } catch (e) {          console.log(1);          throw e;        }              return Number(fn());    }    static isValid(item) {        return /^(?:[-+*/%]|\d+|\d+.\d+)$/.test(item);    }}let obj = new Calculator(2, '/', 4);console.log(obj);console.log(obj.calculate());
http://codepen.io/anon/pen/PZBowj?editors=1011
  • Like 1
Link to comment
Share on other sites

Спасибо буду дальше править.

А можешь пожалуйста объяснить этот участок. Примерно понятно но все же не до конца.

А так же не понятно для чего константа PRIVATE и функция в ней.

add(...items) {      let valideted = items.filter(item => Calculator.isValid(item));      this[PRIVATE].push(...valideted);    }
Link to comment
Share on other sites

Лайк тебе за старания =)

Твой код выдает тебя. Он просто кричит, что у тебя мало практики)

План таков:

1. Сейчас дожимаешь свой "объектный" калькулятор

2. После чего я покажу, как достаточно просто его визуализировать без лишних плясок с бубном

---

Далее все по п.1:

1.1 Чем очевидней твой код, тем его проще читать. Вместо

(''+val).match
рекомендую

String(val).match
Не надо самому себе создавать доп. трудности

1.2

    static isOperation(val) {        return (''+val).match(/^[\+\-\*\/]$/) !== null;    }
вместо см. match мой пример выше с REGEXP.test()

1.3

вместо

(+val)
лучше писать

Number(val)
1.4 вместо

let fn = new Function('return '+str+';'),
см. мой пример выше со string templates

1.5

рекурсия на ровном месте непонятно зачем

 add(val) {        if(Array.isArray(val)) {            val.forEach((value) => this.add(value));
сам себе сложности создаешь

если хочешь валидировать массив actions, лучше отдельную функцию создать

я последовательность не валидировал. на мой взгляд это лишнее)

1.6

// Предотвращает ошибку в случае если последний элемент это знак, удаляя его
все равно try catсh нужен. Понятно почему?

А так же не понятно для чего константа PRIVATE

чтобы сделать свойство приватным. Когда ты описываешь класс, подразумевается, что он инкапсулирован в модуль

константа PRIVATE и функция в ней

что еще за "функция в ней"? =)

Далее по п.2

Набросал на скорую руку очень упрощенно и приближенно https://jsfiddle.net/gLgo2s2d/2/

  • Like 3
Link to comment
Share on other sites

Спасибо. Но очень сложный вопрос, где находится граница, которая разделяет то что нужно проверять, валидировать, а что нет.
С одной стороны почти все можно переложить на человека который будет использовать класс и отправлять данные сразу в правильном формате, с другой стороны хочется исключить малейшую возможность ошибки.

Получается если не валидировать последовательность, то в случае если человек отправит подряд 2 знака или числа, то будет ошибка при попытки подсчёта. Поэтому не могу понять где эта грань.

 

Хотя с другой стороны будет ошибка, что человек введет данные с валидацией, что без. Вопрос лишь во времени возникновения ошибки. В случае если есть валидации, то ошибка выйдет сразу при добавлении, а если нет валидации, то во время расчёта.

Или же форматировать строку которая идёт в калькулятор нужно в модуле отображения?

Edited by li4e
Link to comment
Share on other sites

Но очень сложный вопрос, где находится граница, которая разделяет то что нужно проверять, валидировать, а что нет.

думаю, надо рассматривать каждый случай отдельно. Например, данный подпадает, как мне кажется, под "это твой калькулятор, ты вправе сам решать, как он будет работать" =)

лично я воспринимаю данный калькулятор исключительно как игрушечный, т.к. там отсутствуют хотя бы "скобочные группы". А по скольку он игрушечный и так нормально)

Или же форматировать строку которая идёт в калькулятор нужно в модуле отображения?

зависит от ситуации. А данном случае я склоняюсь к тому, что в "модуле отображения"
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy