Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Как граматно создать конструктор (https://javascript.ru/forum/misc/52088-kak-gramatno-sozdat-konstruktor.html)

Siend 03.12.2014 15:37

Как граматно создать конструктор
 
НЕ ПУГАЙТЕСЬ ЧТО МНОГО ТЕКСТА, ТУТ ВСЕ ПРОСТО!!!

Решил попытаться переписать свой код, и столкнулся с огромным провалом в понимании ООП в javascript, связи с чем прошу вашей помощи в создании эдакого мануала.

Я работаю с элементам canvas, но думаю что логика подхода применительно к обычному диву будет такой же. Но не суть, ближе к делу.

Пусть у нас будут даны несколько элементов canvas:
...
<canvas id="canvasA"></canvas>
<canvas id="canvasB"></canvas>
<canvas id="canvasC"></canvas>
...

и располагаем мы таким набором говнокода (написанным применительно для одного canvas):

function init() { //функция инициализации canvas
        canvas = document.getElementById("canvas");
        ctx = canvas.getContext("2d");
    }
...
function animate() { //функция запускающая отрисовку
        requestAnimationFrame(animate);
        draw();
    }

//координаты мыши относительно элемента канвас
var xMouse=0
var yMouse=0
var HOLD=false
var WIDTH = canvas.offsetWidth
var HEIGHT = canvas.offsetHeight

function draw(){ //отрисовка
        ctx.beginPath();
        ctx.rect(0, 0, WIDTH , HEIGHT ); // очистка экрана
        ctx.fill();
        ctx.stroke();
        if (HOLD==false)
                ctx.strokeText(xMouse + " " + yMouse, 10, 10) // 10 10 -точка вывода координат мыши. 
        if (HOLD==true)
                ctx.strokeText("Click is true", 10, 10)
}

//обработчики:
canvas.onmousedown = myDown;
canvas.onmouseup = myUp;
canvas.onmousemove = myMove;

function myUp(e) { HOLD = false }
function myDown(e) { HOLD = true}
function myMove(e) {
        xMouse = e.pageX - canvas.offsetLeft
        yMouse = e.pageY - canvas.offsetTop
}


Итак, это пожалуй все что нам понадобиться.

Задача:
отображать на каждом canvas координаты xMouse, yMouse (координаты мышки относительно этого канваса, а не всей страницы), а в случае клика выводить надпись (как видите в случае клика меняется значение переменной HOLD, а в функции отрисовки есть проверка этой переменной)

Сложность:
Все что я написал действительно является ничем иным как говнокодом, который можно применить к одному единственному элементу canvas, я же хочу сделать универсальный конструктор, но не знаю как, и в этом прошу вашей помощи. Думаю этот урок может быть полезен и другим новичкам js в будущем!

tsigel 03.12.2014 17:30

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

var SomeConstructor = function(name) {
  this.name = name;
};
SomeConstructor.prototype.getName = function () {
  return this.name;
};

var vasia = new SomeConstructor("Вася");
var petia = new SomeConstructor("Петя");
alert([vasia.getName(), petia.getName()]);


Пишите что и где конкретно не получается - вам помогут. За вас нет желания переписывать.

nerv_ 03.12.2014 17:35

tsigel,
function SomeConstructor() {}

потому, что может пригодиться function.name (имя класса)

tsigel 03.12.2014 17:40

nerv_,
Ну не знаю. Однажды я брал так имя класса, когда делал одному из базовых классов метод который выдает всю цепочку наследования. Других использований имени класса не было нужно ни разу. К тому же я пишу на TypeScript (в больших проектах сильно помогают интерфейсы), там в генерируемых им классах имен в функции-конструкторе нет.

tsigel 03.12.2014 17:44

Кстати про TypeScript, я заметил что если писать обычный js код, то idea и webshtorm понимают интерфейсы написанные на TypeScript. Так что теоретически можно оттуда тока интерфейсы брать :)

Siend 03.12.2014 17:53

На примере Васи и Пети - все предельно понятно, даже про прототипы уже прочитал и тоже вообщем-то понял. В данном случае мы переопределили объект SomeConctructor, добавив в него метод возвращающий имя. К конкретному коду никаких вопросов нет.

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

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

var vasia = {
name: "Vasia O_O",
age: 30,
friends: vasiaFriends
}

var vasiaFriends = ["Egor","Igor","SexyBitch"]

function addAge(){
vasia.age++
}

function addFriends(name){
vasiaFriends.push(name)
}


как теперь мне создать 2 васи и добавить одному из них нового друга, а другому увеличить возраст?

kostyanet 03.12.2014 18:04

Цитата:

Сообщение от tsigel (Сообщение 344195)
Все самое важное я написал вам в вашем прошлом посте.
Пример создания конструктора там тоже есть, могу кинуть второй:

var SomeConstructor = function(name) {
  this.name = name;
};
SomeConstructor.prototype.getName = function () {
  return this.name;
};

В чем разница

var SomeConstructor = function(name) {
  this.name = name;
  this.getName=function(){
    return this.name;
  };
};


?

jsnb 03.12.2014 18:07

Цитата:

Сообщение от Siend
как теперь мне создать 2 васи и добавить одному из них нового друга, а другому увеличить возраст?

function Person(options) {
  this.name = options.name;
  this.age = options.age;
  this.friends = options.friends;
}

Person.prototype.addAge = function() { this.age++ }
Person.prototype.addFriends = function(name) { this.friends.push(name) }

var vasia1 = new Person({
  name: "Vasia 1",
  age: 30,
  friends: ["Egor","Igor","SexyBitch"]
});

var vasia2 = new Person({
  name: "Vasia 2",
  age: 30,
  friends: ["Egor","Igor","SexyBitch"]
});

vasia1.addFriends('New friend');
vasia2.addAge();

alert( JSON.stringify(vasia1,'',2) );
alert( JSON.stringify(vasia2,'',2) );

tsigel 03.12.2014 18:08

var SomeConstructor = function(name, age, friends) {
  this.name = name;
  this.age = age;
  this.friends = friends;
};
SomeConstructor.prototype.getName = function () {
  return this.name;
};
SomeConstructor.prototype.addAge = function () {
  return this.age++;
};
SomeConstructor.prototype.addFriends = function (friend) {
  return this.friends.push(friend);
};

var some1 = new SomeConstructor("a", 30, []);
var some2 = new SomeConstructor("b", 30, []);

some1.addAge();
some2.addFriends("gosha");

alert(JSON.stringify(some1,'',2));
alert(JSON.stringify(some2,'',2))


Эх, долго писал :(

jsnb 03.12.2014 18:10

Цитата:

Сообщение от kostyanet
В чем разница

При использовании прототипа метод getName будет у всех экземпляров общий, а без прототипов - у каждого экземпляра свой, что повлечет лишний расход памяти и проблемы с наследованием.

tsigel 03.12.2014 18:11

kostyanet,
Разница в экономии памяти. У вас функция будет создана для каждого экземпляра, уу меня у каждого экземпляра будет ссылка на функцию из прототипа.

Эх, долго писал :(

kostyanet 03.12.2014 18:14

Цитата:

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

var vasya = new SomeConstructor('vasya');
vasya.getName();
// vasya

но лично мне эта хрень с this'ами не нравится. Это голимые публичные проперти. Смысла в getName() ровно 0, ибо vasya.name дает то же самое. Ну, понятно что в методе может быть что-то посложнее, но все равно нормальный класс должен иметь внутренности и интерфейс.

kostyanet 03.12.2014 18:16

tsigel,

Ясно, спасибо.

jsnb 03.12.2014 18:16

Цитата:

Сообщение от kostyanet
Это голимые публичные проперти.

Никто не мешает прятать приватные в замыкания.

krutoy 03.12.2014 18:18

Цитата:

Сообщение от kostyanet
Смысла в getName() ровно 0, ибо vasya.name дает то же самое.

a=1

Смысла в пременной a нет, ибо 1 дает то же самое

tsigel 03.12.2014 18:18

Цитата:

Сообщение от kostyanet
Это голимые публичные проперти.

Никто не мешает писать приватные методы начиная с "_" и писать JSDoc, тогда IDE сама будет показывать что можно трогать а что нет. В данном случае я писал это просто как пример.

kostyanet 03.12.2014 18:19

Теперь такой вопрос, я тоже нуждаюсь. Цитата:

SomeConstructor.prototype.getName = function () {
  return this.name;
};
SomeConstructor.prototype.addAge = function () {
  return this.age++;
};
SomeConstructor.prototype.addFriends = function (friend) {
  return this.friends.push(friend);
};


конец цитаты. Это все можно собрать в один прототип который типа класс, и, затем от него все эти методы наследовать через какой-то шланг в конкретном объекте?

krutoy 03.12.2014 18:22

Цитата:

Сообщение от kostyanet
Это все можно собрать в один прототип который типа класс, и, затем от него все эти методы наследовать через какой-то шланг в конкретном объекте?

они и так наследуются. Твоего кода достаточно, больше них*я н надо для этого
Или ты хочешь суперкласс создать?

danik.js 03.12.2014 18:25

Цитата:

Сообщение от jsnb
Никто не мешает прятать приватные в замыкания.

Как? Чтобы у методов был к ним доступ.

tsigel 03.12.2014 18:25

kostyanet,
Да

var SomeConstructor = function(name, age) {
  this.name = name;
  this.age = age;
};
SomeConstructor.prototype.getName = function () {
  return this.name;
};
SomeConstructor.prototype.addAge = function () {
  return this.age++;
};

var SomeChild = function () {
  SomeConstructor.apply(this,arguments)
};
SomeChild.prototype = Object.create(SomeConstructor.prototype);
SomeChild.constructor = SomeChild;

SomeChild.prototype.childMethod = function () {
  return true;
}

var child = new SomeChild("child", 20);

alert(JSON.stringify(child, "", 4));
alert(child.childMethod());

kostyanet 03.12.2014 18:28

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

Там я задавал именно такой вопрос, про эту самую память. Не скажу что она меня сильно волнует, ибо и без скриптов FF хавает изрядно, подумаешь еще пару мегов, а Chrome вообще может полтора гига засосать своим leakage. Но тем не менее, вот усеченный код, структура вся сохранена

var FormField=function(elem){
	var	type=		elem.type,
		def=		elem.value,
		rules={};

	return {
		validate:function() {
			value=elem.value.trim();
			if(type in rules)
				rules[type]();
			else
				rules.text();
		}
	};
};

// load

var some[]=FormField[elem];


Я понимаю у каждого будет своя копия (инстансь) объекта который возвращает функция, а все ее кишки тоже скопируются?

tsigel 03.12.2014 18:28

Цитата:

Сообщение от danik.js
Как? Чтобы у методов был к ним доступ.

danik.js,
Например так:

var Class = (function () {

   var private
   var Class = function () {}

   Class.prototype.someMethod = function() {
      private ...
   };

   ...

   return Class;
})();

krutoy 03.12.2014 18:30

Цитата:

Сообщение от danik.js
Как? Чтобы у методов был к ним доступ.

Методы могут быть определны в том ж замыкании, вариантов тьма.

tsigel 03.12.2014 18:32

Цитата:

Сообщение от kostyanet
Я понимаю у каждого будет своя копия (инстансь) объекта который возвращает функция, а все ее кишки тоже скопируются?

Да

krutoy 03.12.2014 18:32

kostyanet,
Да ты долбоеб, по ходу, совсем мягкий, блять. Нахуй ты мне сдался.

kostyanet 03.12.2014 18:33

tsigel,

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

krutoy 03.12.2014 18:36

Цитата:

Сообщение от nerv_
потому, что может пригодиться function.name (имя класса)

а можно пример, когда это может пригодится?

Siend 03.12.2014 18:38

Развели тут срач в моей теме... ><

Вообщем, кажется, я наконец-то понял, поправьте меня, если я туплю.

1. Я создаю конструктор, который должен проинициализировать все мои переменные используемые в данном canvas. Т.е. допустим я использую:
...
var x0Multi
    var y0Multi
    var x1Multi
    var y1Multi
    var xMouse
    var yMouse
    var TEMP = ""
    var ActionState = "single"
    var Creating = "none"
    var InFocus = { H: [], S: [], CvC: [] }
    var DATA = []
    var conn = []
    var vers = []
    var edges = []
    var cnEdg = 0
    var cnCon = 0
    var cnVer = 0
    var idV = 0
    var idE = 0

...
тогда я получу что-то вроде:
var SomeConstructor = function() {
    this.x0Multi
    this.y0Multi
    this.x1Multi
    this.y1Multi
    this.xMouse
    this.yMouse
    this.TEMP = ""
    this.ActionState = "single"
    this.Creating = "none"
    this.InFocus = { H: [], S: [], CvC: [] }
    this.DATA = []
    this.conn = []
    this.vers = []
    this.edges = []
    this.cnEdg = 0
    this.cnCon = 0
    this.cnVer = 0
    this.idV = 0
    this.idE = 0
};


2. Все функции я должен переписать в ввиде расширения прототипа. Т.е.:

было
...
    function drawMultiSelect() {
        ctx.beginPath();
        ctx.rect(x0Multi, y0Multi, x1Multi - x0Multi, y1Multi - y0Multi);
        console.log(x0Multi + " " + y0Multi + " " + xMouse + " " + yMouse)
        ctx.strokeStyle = "rgba(0,0,255,0.5)";
        ctx.fillStyle = "rgba(0,0,255,0.3)";
        ctx.fill();
        ctx.stroke();
    }
...


стало:
SomeConstructor.prototype.drawMultiSelect() {
        this.ctx.beginPath();
        this.ctx.rect(x0Multi, y0Multi, x1Multi - x0Multi, y1Multi - y0Multi);
        this.ctx.strokeStyle = "rgba(0,0,255,0.5)";
        this.ctx.fillStyle = "rgba(0,0,255,0.3)";
        this.ctx.fill();
        this.ctx.stroke();
    }


остается мне только одно не понятным.
допустим я в конструкторе я определил переменную this.ctx, как потом мне обратиться к ней из функции определенной в прототипе? как к ctx или как к this.ctx?

tsigel 03.12.2014 18:39

Цитата:

Сообщение от krutoy
а можно пример, когда это может пригодится?

krutoy,
Я делал систему логирования, которая в случае ошибки отправляла на сервер ошибку, имя метода и класса (со всей цепочкой наследования) в котором произошла ошибка.

krutoy 03.12.2014 18:49

Цитата:

Сообщение от Siend
как к ctx или как к this.ctx?

Как this, естественно. Но Вы по-прежнему не понимаете, как это работает, иначе этого вопроса бы не возникло просто.

Siend 03.12.2014 18:53

Цитата:

Как this, естественно. Но Вы по-прежнему не понимаете, как это работает, иначе этого вопроса бы не возникло просто.
не совсем, я понимать начинаю по мере объяснений и сейчас стал еще на шаг ближе к этому) Все равно всем спасибо, теперь я думаю смогу справится сам. Только один мелкий вопросик, а как мне обратиться к каждому элементу canvas? есть какая-нибудь функция для перебора их?

krutoy 03.12.2014 18:56

Цитата:

Сообщение от Siend
а как мне обратиться к каждому элементу canvas? есть какая-нибудь функция для перебора их?

Цикл?

kostyanet 03.12.2014 18:56

Не получается, набрался наглости попросить показать как из того что было

var FormField=function(elem){
	var	type=		elem.type,
		def=		elem.value,
		rules={};

	return {
		validate:function() {
			value=elem.value.trim();
			if(type in rules)
				rules[type]();
			else
				rules.text();
		}
	};
};

// load

var some[]=FormField[elem];


сделать правильный объект, который бы содержал только данные, а методы держал бы в прототипе.

На php такая задача решается через скажем protected static function(), поскольку статика не создает экземпляров. Ну и там еще легко делаются геттеры с сеттерами всякие. В итоге в объекте может быть ровно 1 array в качестве тех самых данных.

danik.js 03.12.2014 18:57

tsigel, уверен что это правильно будет работать?

var Class = (function () {
 
   var private
   var Class = function () {}
 
   Class.prototype.setPrivate = function(value) {
      private = value;
   };
   Class.prototype.getPrivate = function() {
      return private;
   };
 
 
   return Class;
})();

var o1 = new Class();
var o2 = new Class();
o1.setPrivate('x');
o2.setPrivate('y');

alert( o1.getPrivate() );

danik.js 03.12.2014 18:58

Цитата:

Сообщение от krutoy
Методы могут быть определны в том ж замыкании, вариантов тьма.

Мне пожалуйста всего один, на случай, когда методы определены в прототипе (ну то есть самый верный вариант).

krutoy 03.12.2014 19:00

Цитата:

Сообщение от Siend
SomeConstructor.prototype.drawMultiSelect() {

И, кстати, откуда Вы взяли такой синтаксис?
SomeConstructor.prototype.drawMultiSelect=function(){}

надо по-идее. У Вас что, это работает?

tsigel 03.12.2014 19:04

danik.js,
А для синглтона подойдет XD

Я предпочитаю на "_" и доках выезжать. без замут с замыканиями.

Если поизвращаться, то наверно можно создать в замыкании массив объектов scope, каждый из которых соответствовал бы конкретному экземпляру.

danik.js 03.12.2014 19:06

Цитата:

Сообщение от tsigel
Я предпочитаю на "_" и доках выезжать. без замут с замыканиями.

Потому что насколько я знаю, с замыканиями не сделаешь нормально.

krutoy 03.12.2014 19:10

Цитата:

Сообщение от danik.js
Мне пожалуйста всего один, на случай, когда методы определены в прототипе (ну то есть самый верный вариант).

clos=function(arg){
var proto={get: function(){alert(this.a)}}
var o=Object.create(proto)
o.a=1
return o[arg]()
}

clos("get")

krutoy 03.12.2014 19:13

Цитата:

Сообщение от danik.js
Потому что насколько я знаю, с замыканиями не сделаешь нормально.

Замыкания -- это вообще зло, потому как это полпути к класс-ООП. Но сделать все можно.


Часовой пояс GMT +3, время: 22:26.