Javascript-форум (https://javascript.ru/forum/)
-   ExtJS (https://javascript.ru/forum/extjs/)
-   -   Проблема с созданием класса - Значения свойств передаются другому экземпляру (https://javascript.ru/forum/extjs/57023-problema-s-sozdaniem-klassa-znacheniya-svojjstv-peredayutsya-drugomu-ehkzemplyaru.html)

khusamov 15.07.2015 17:50

Проблема с созданием класса - Значения свойств передаются другому экземпляру
 
Что я сделал не так?

В консоли должно быть пусто, а вместо этого [1, 2, 3, 4]
То есть в экземпляре s2 оказались члены массива из экземпляра s1...
Как такое могло получиться???

Код в песочнице https://fiddle.sencha.com/#fiddle/qgo

Ext.define("Subpath", {
	
	segments: [],
	
	add: function(segment) {
		this.segments.push(segment);
		return segment;
	}
    
});

Ext.application({
    name : 'Fiddle',
    launch : function() {
        
        var s1 = Ext.create("Subpath");
        s1.add(1);
        s1.add(2);
        s1.add(3);
        s1.add(4);
        
        var s2 = Ext.create("Subpath");
        
        console.log(s2.segments);
        
    }
});


Вот тут
http://docs.sencha.com/extjs/5.1/cor...s/classes.html
явно видно, что можно задавать значения по умолчанию для свойств класса.

novikov 15.07.2015 18:28

В документации видно, что такие свойства задаются через конструктор:

Ext.define('My.sample.Person', {
    name: 'Unknown',

    constructor: function(name) {
        if (name) {
            this.name = name;
        }
    },

    eat: function(foodType) {
        alert(this.name + " is eating: " + foodType);
    }
});

var bob = Ext.create('My.sample.Person', 'Bob');

bob.eat("Bob"); // alert("Bob is eating: Salad");


А не через самодельный сеттер. Интуитивно непонятно, конечно.

khusamov 15.07.2015 18:37

Строка 05 содержит условие if (name) {...},. То есть предполагается, что класс можно использовать так:

var noname = Ext.create('My.sample.Person');

В этом случае name будет содержать 'Unknown'.

Отсюда я делаю вывод, что строка 02 содержит значение по умолчанию.

Иначе бы они написали так:

name: undefined,

constructor: function(name) {
        if (name) {
            this.name = name;
        } else {
            this.name = 'Unknown';
        }
},

В этом случае видно, что значение по умолчанию задается только в конструкторе.

nohuhu 15.07.2015 21:14

Вы путаете конфигурационные свойства, которые задаются в блоке config: {} и могут наследоваться/объединяться, с обычными свойствами, которые никаким специальным образом не обрабатываются.

Класс Ext JS - это функция-конструктор; всё, что вы задаёте в блоке Ext.define(, {…}) попадает на прототип этого конструктора. Экземпляр этого класса, соответственно, это просто объект JavaScript, который через __proto__ показывает на соответствующий класс.

Ну и как отсюда очевидно, если у вас в *экземпляре* класса (т.е. в объекте) нет своего свойства segments, то будет использоваться свойство segments с *прототипа*. Т.е. каждый экземпляр класса Subpath будет обращаться к одному и тому же массиву, наступая друг другу на ноги. Обычный JavaScript, ничего сложного. ;)

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

Ext.define('Subpath', {
    /**
     * @property {Number[]} segments Тут живут сегменты
     */

    constructor: function() {
        this.segments = [];
    }
});


Что касается примера г-на novikov, то это как раз и есть случай конфигурационного свойства. В таких случаях велосипед изобретать не надо, а лучше использовать блок config:

Ext.define('My.sample.Person', {
    config: {
        name: 'Unknown' // значение по умолчанию
    }
});

// Значение не передано, будет использовано по умолчанию
var unknown = new My.sample.Person();
console.log(unknown.getName()); // Unknown

// А тут мы передаём значение явно
var foo = new My.sample.Person({ name: 'foo' });
console.log(foo.getName()); // foo


Особо отмечу, что первый и второй случаи друг другу совершенно не противоречат; segments это *внутреннее* свойство объекта, а name это конфигурационное свойство. Использовать и те и другие в одном объекте это совершенно обычное дело, надо просто понимать разницу.

khusamov 15.07.2015 21:26

Цитата:

Ну и как отсюда очевидно, если у вас в *экземпляре* класса (т.е. в объекте) нет своего свойства segments, то будет использоваться свойство segments с *прототипа*. Т.е. каждый экземпляр класса Subpath будет обращаться к одному и тому же массиву, наступая друг другу на ноги. Обычный JavaScript, ничего сложного. ;)
Не понятно как система классов Ext JS допустила эту странную ситуацию. Зачем такое может вообще понадобиться? Чтобы иметь доступ к общему массиву (или что там будет вложено в свойство).

На мой взгляд не совсем логично, что свойства надо создавать в конструкторе (точнее с точки зрения JS нормально, но с точки зрения обычного ООП не привычно, а ведь Ext JS пытается имитировать обычный ООП). И если забудешь создать, то будет ссылка на общее значение.

Кстати, я до сих пор не сталкивался с этой проблемой, а ведь уже сколько подобных свойств я насоздавал и пока не напарывался... Придется всю толпу классов перерыть и всюду сделать инициализацию в конструкторе... неслабо однако...

Кстати, а где об этом написано в документации?

khusamov 15.07.2015 21:32

Кстати, в свете новых сведений о создании свойств не ясно как создавать приватные свойства.

Ext.define(..., {

privates: {...}

});


Ведь в блоке privates их объявлять нельзя получается. Только разве комментарий можно. А в конструкторе похоже можно создавать публичные свойства. Бардачок намечается чтоли?

nohuhu 15.07.2015 23:57

Цитата:

Сообщение от khusamov (Сообщение 379852)
Не понятно как система классов Ext JS допустила эту странную ситуацию. Зачем такое может вообще понадобиться? Чтобы иметь доступ к общему массиву (или что там будет вложено в свойство).

Подождите, а как вы предполагаете обойти базовые свойства языка в библиотеке? :))

Цитата:

На мой взгляд не совсем логично, что свойства надо создавать в конструкторе (точнее с точки зрения JS нормально, но с точки зрения обычного ООП не привычно, а ведь Ext JS пытается имитировать обычный ООП). И если забудешь создать, то будет ссылка на общее значение.
Ну, вот такой вот язык этот JavaScript. Выбора какбэ нет, скорбь и пичалька.

Цитата:

Кстати, я до сих пор не сталкивался с этой проблемой, а ведь уже сколько подобных свойств я насоздавал и пока не напарывался... Придется всю толпу классов перерыть и всюду сделать инициализацию в конструкторе... неслабо однако…
Большая часть таких конструкций может работать просто по случайности, как оно обычно и бывает. Если не создаёте более одного экземпляра класса одновременно, то и проблем как будто бы нет. У вас ведь юнит-тесты последовательно исполняются, правильно? Обычно все так и делают, поэтому проблема и не вылазит. Точнее, не вылазит в разработке, а вот внедрение уже бывает бодрым и весёлым. :))

Цитата:

Кстати, а где об этом написано в документации?
В документации не написано просто потому, что не имеет смысла повторять учебник JavaScript. А вот в каких-нибудь вводных статьях может и упомянуто, но я их никогда не читал. :)

Цитата:

Ведь в блоке privates их объявлять нельзя получается. Только разве комментарий можно. А в конструкторе похоже можно создавать публичные свойства. Бардачок намечается чтоли?
Я вам страшную тайну открою: у объектов JavaScript вообще не бывает приватных свойств. То есть совсем. Ну, вот язык такой. Есть closures, но это не совсем то (точнее, совсем не то). А то, что в Ext JS классовая система поддерживает приватные методы, так это просто хак для облегчения нашей с вами жизни. И то больше нашей, чем вашей. :)

Практически единственный смысл блока privates - это дать вам, как пользователю, возможность узнать о том, что в вашем наследованном классе есть метод с таким же названием, как и приватный метод в родительском классе. В отличие от публичного API, приватные методы не гарантированы по прямой и обратной совместимости между версиями; т.е. мы оставляем себе право их менять как нам заблагорассудится. Мы слишком часто натыкались на ситуации, когда пользователи в своём коде называли методы так же, как и мы в своём. Результаты бывают феерически прекрасны, но очень трудоёмки к поимке.

Из этой проблемы и растут ноги у блока privates. Это наш способ дать вам знать: вот этот метод - он приватный, не трогайте его. А если потрогаете и ему не понравится, то мы не виноваты. :)

Makarov 16.07.2015 12:09

nohuhu,
я каждый раз когда ваши сообщения в разделе читаю, жалею что движок форума не разрешает одному человеку много плюсов ставить =)

khusamov 16.07.2015 13:06

Цитата:

Я вам страшную тайну открою: у объектов JavaScript вообще не бывает приватных свойств. То есть совсем.
Я наверное плохо донес... это свойство JS я в общем-то знаю. Просто я считал, что классовая система Ext исправила ситуацию.

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

Цитата:

Подождите, а как вы предполагаете обойти базовые свойства языка в библиотеке? :))
Мне сейчас придется начальные значения этих свойств описывать в конструкторе. По идее, классовая система Ext может это делать за меня, если в конфиге класса найдет такие свойства. Хотя бы так.

Но это не принципиально, просто заранее надо знать, что этот момент Ext не решает и надо самому не попасться.

khusamov 16.07.2015 13:09

Цитата:

Сообщение от Makarov (Сообщение 379965)
nohuhu,
я каждый раз когда ваши сообщения в разделе читаю, жалею что движок форума не разрешает одному человеку много плюсов ставить =)

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


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