Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Prototype. Одноблочное определение псевдокласса. Литеральная форма не робит. Почему? (https://javascript.ru/forum/misc/22017-prototype-odnoblochnoe-opredelenie-psevdoklassa-literalnaya-forma-ne-robit-pochemu.html)

GuardCat 03.10.2011 10:49

Prototype. Одноблочное определение псевдокласса. Литеральная форма не робит. Почему?
 
Мне не очень нравятся многоблочные определения псевдоклассов. Т.е. когда вначале определяют класс, блок закрывают, а затем определяют его прототип. Я решил попробовать определить прототип прямо в блоке класса. И столкнулся с полтергейстом:
Создан простой класс, в котором есть прототипная переменная, общая для всех экземпляров класса. Вызывается, через один экземпляр, функция, которая увеличивает общую переменную на 1. Затем выводится значение общей переменной для другого экземпляра.
<html><body>
<script>
function Test()
{
	Test.prototype.data=1;
	Test.prototype.f=function()
	{
		Test.prototype.data++;
	}
}


test1=new Test;
test2=new Test;
test1.f();
document.write("test2.data=",test2.data,"<br>");
</script>
Всё верно, общая переменная поменялась — выводит: test2.data=2.

Отлично! Пробуем оптимизировать:
<html><body>
<script>
function Test()
{
	Test.prototype=
	{
		data:1,
		f:function()
		{
			Test.prototype.data++;
		}
	}
}


test1=new Test;
test2=new Test;
test1.f();
document.write("test2.data=",test2.data,"<br>");
</script>

Получаем: Uncaught TypeError: Object #<Test> has no method 'f'. WTF?!!

А если так:
<html><body>
<script>
function Test(){}
Test.prototype=
{
	data:1,
	f:function()
	{
		Test.prototype.data++;
	}
}

test1=new Test; 
test2=new Test;
test1.f();
document.write("test2.data=",test2.data,"<br>");
</script>
То работает! Но опять разбиваем на два блока верхнего уровня =(.

Вопросы:
  1. Почему литеральная запись объекта внутри блока определения класса не работает, но работает присвоение там же но по полям? (Кстати с with внутри класса тоже не робит). Чем отличается присвоение по полям от литеральной записи?
  2. Почему, в таком случае, литеральная запись работает вне блока определения класса?
Поясните, кто знает механику работы объектов, я что-то не догоняю.

Riim 03.10.2011 11:41

Когда во втором примере ты пишешь:
Цитата:

Сообщение от GuardCat
test1=new Test;

this.__proto__ внутри конструктора начинает ссылаться на Test.prototype. А дальше ты бац и заменяешь Test.prototype на новый объект, но this.__proto__ по-прежнему ссылается на старый объект, в котором метода f нет. Вот в ошибке и написано, что его нет. В первом примере ты изменяешь уже существующий объект, и т. к. на него ссылаются и this.__proto__ и Test.prototype, изменения видны в обоих.


Цитата:

Сообщение от GuardCat
Мне не очень нравятся многоблочные определения псевдоклассов. Т.е. когда вначале определяют класс, блок закрывают, а затем определяют его прототип

прототип будет .... , короче, не надо так, бяка.

GuardCat 03.10.2011 11:52

Спасибо, Riim. Стало понятнее. Про бяку, раскройте тему, пожалуйста.
Вы имеете ввиду, что не следует определять прототип литерально, заменяя объект на свой (это теперь понятно)?
Тогда вопрос, внутри определения класса можно (не бяка ли) трогать прототип за поля, не заменяя объект или нужно делать это за пределами определения класса?
Или вообще размещать переменные в прототипе моветон и надо использовать поля класса?

Пристрелите во мне быдлокодера =)

Riim 03.10.2011 12:40

Цитата:

Сообщение от GuardCat
внутри определения класса можно (не бяка ли) трогать прототип за поля

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


Цитата:

Сообщение от GuardCat
Мне не очень нравятся многоблочные определения псевдоклассов. Т.е. когда вначале определяют класс, блок закрывают, а затем определяют его прототип

а так вообще все много-много раз создается.

Kolyaj 03.10.2011 12:51

Цитата:

Сообщение от GuardCat
Мне не очень нравятся многоблочные определения псевдоклассов. Т.е. когда вначале определяют класс, блок закрывают, а затем определяют его прототип.

https://github.com/Kolyaj/CrossJS/bl...unction.js#L97
var MyClass = AnotherClass.inherit({
    constructor: function() {
        MyClass.superclass.constructor.apply(this, arguments);
        this.a = 5;
    },

    method1: function() {
        alert('method1');
    },

    method2: function() {
        alert('method2');
    }
});

GuardCat 03.10.2011 13:18

Цитата:

Сообщение от Riim (Сообщение 129330)
...

а так вообще все много-много раз создается.

Вкурил. И правда, на то он и конструктор.

Kolyaj, спасибо. Только я не настолько крут пока, чтобы понять, что происходит в библиотеке, даже несмотря на комментарии. Сохранил. Буду разбираться.

Riim 03.10.2011 13:46

Цитата:

Сообщение от Riim
теряется одно из преимуществ прототипного ООП над обычным

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


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