Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > Странное поведение при работе с переменными протот


Автор: Megas 3.12.2012, 13:05
Работая над одним проектом обнаружил для себя странное поведение при работе с переменными родительского класса.

Допустим у нас есть некий родительский класс:
Код

function ParentObj()
{
    this._some_arr = [];
    this._some_var = 5;

    this.pushArr = function(val)
    {
        this._some_arr.push(val);
    }

    this.getArr = function()
    {
        return this._some_arr;
    }

    this.setVal = function(val)
    {
        this._some_var = val;
    }

    this.getVal = function()
    {
        return this._some_var;
    }
}

Как видно у него две внутренние переменные: одно числовое, а другое массив. И пара функций для работы с каждой из переменных.

Теперь определим класс наследник:
Код

function ChildObj()
{
}

ChildObj.prototype = new ParentObj();


Я не гуру javascript'а, поэтому для меня было большим удивлением, что переменная-массив является общей для всех объектов класса ChildObj, в то время как вторая переменная каждая своя у каждого объекта.

Вот пример кода:
Код

var obj1 = new ChildObj;
var obj2 = new ChildObj;

obj1.pushArr(5);
obj1.setVal(11);

obj2.pushArr(8);
obj2.setVal(12);

console.log('Object 1 Array: '+ obj1.getArr().toString()); //Object 1 Array: 5,8
console.log('Object 1 Variable '+ obj1.getVal());              //Object 1 Variable 11
console.log('Object 2 Array: '+ obj2.getArr().toString()); //Object 2 Array: 5,8
console.log('Object 2 Variable '+ obj2.getVal());              //Object 2 Variable 12


Я прочитал много различных статей про ООП в javascript'е, но нигде не описывалась подобная проблема.
Ктото может объяснить почему такое происходит? Или дать ссылку на какуюто хорошую статью описывающую данную проблему?

И как эту проблему можно решить не прибегая к объявлении такой же переменной в классе наследника?

Автор: Arantir 3.12.2012, 13:30
Цитата
И как эту проблему можно решить не прибегая к объявлении такой же переменной в классе наследника? 


Код

function ParentObj()
{
    this._some_arr = null;
    this._some_var = 5;
    this.pushArr = function(val)
    {
     if (this._some_arr == null)
         this._some_arr = new Array();
     this._some_arr.push(val);
    }
    this.getArr = function()
    {
        return this._some_arr;
    }
    this.setVal = function(val)
    {
        this._some_var = val;
    }
    this.getVal = function()
    {
        return this._some_var;
    }
}
function ChildObj()
{
}
ChildObj.prototype = new ParentObj();

var obj1 = new ChildObj;
var obj2 = new ChildObj;
obj1.pushArr(5);
obj1.setVal(11);
obj2.pushArr(8);
obj2.setVal(12);
console.log('Object 1 Array: '+ obj1.getArr().toString()); //Object 1 Array: 5
console.log('Object 1 Variable '+ obj1.getVal());              //Object 1 Variable 11
console.log('Object 2 Array: '+ obj2.getArr().toString()); //Object 2 Array: 8
console.log('Object 2 Variable '+ obj2.getVal());              //Object 2 Variable 12

О чем-то говорит? =)

Автор: Megas 3.12.2012, 15:29
Arantir, т.е. ты предлагаешь просто заменить массив на null? Это больше похоже на костыль, чем на нормальное решение. 

А почему это происходит, не вкурсе?

Автор: baldina 3.12.2012, 15:40
Цитата(Megas @  3.12.2012,  13:05 Найти цитируемый пост)
что переменная-массив является общей для всех объектов класса ChildObj, в то время как вторая переменная каждая своя у каждого объекта.

"переменная-массив" тоже своя, только это не массив, а ссылка

Автор: CruorVult 3.12.2012, 15:46
Array(он же Object) - ссылочный тип, а String, Number - нет.
 

Автор: Megas 3.12.2012, 15:57
baldina, CruorVult, спасибо. Тоже уже догодался, что именно в этом дело.

Автор: Arantir 3.12.2012, 19:53
Цитата(Megas @  3.12.2012,  14:29 Найти цитируемый пост)
Arantir, т.е. ты предлагаешь просто заменить массив на null? Это больше похоже на костыль, чем на нормальное решение. 
Нет!
Я предлагаю каждому потомку создавать собственный массив автоматически в родителе.
Можете поставить там ноль! Можете хоть поставить там "Hello world!"!
null просто не сбивает с толку, мол что это за число или что за строка.

Представьте себе, даже вот это работает:
Код

function ParentObj()
{
    this._some_arr = function() { };
    this._some_var = 5;
    this.pushArr = function(val)
    {
     if (typeof(this._some_arr) == "function")
         this._some_arr = new Array();
     this._some_arr.push(val);
    }
    this.getArr = function()
    {
        return this._some_arr;
    }
    this.setVal = function(val)
    {
        this._some_var = val;
    }
    this.getVal = function()
    {
        return this._some_var;
    }
}
А почему - уже объяснил CruorVult. Вы создаете свой массив (свою ссылку на отдельный массив) каждому потомку, а не используете ссылку на массив родителя.

При желании вы можете в родителе вписать в объявление даже готовый заполненный массив. Место объявления осталось одним-единственным. Но у каждого потомка будет свой независимый массив.

Где тут "костыль", не пойму никак... По-моему, это называется "отложенная инициализация". Ну ладно, я инициализировал ее в null. Но вы можете свободно оставить ее неинициализированной и проверять ее тип на undefined. Суть метода-то не в этом.

Автор: ksnk 3.12.2012, 20:43
Megas, Дело просто в другой схеме "наследования". Точнее - своей собственной, прототипной  smile 
В нормальной схеме есть класс - "разметка" будущего участка отведенных для объекта рессурсов под методы и атрибуты. а в прототипной - существует ссылка на другой объект - прототип.  Когда запрашивается атрибут от this, смещаемся по списку прототипов "вниз", а не лезем в несуществующую в JS таблицу классов. Некое просветление наступает, когда осознаешь, что прототипы можно менять на лету, тем самым получая себе доступ к свойствам самых странных объектов. 

Автор: CruorVult 4.12.2012, 12:26
Я бы посоветовал почитать книжку Object-Oriented JavaScript By Stoyan Stefanov. Все вопросы сразу пропадут smile 
Там описано 12 способов наследования smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)