Backbone View расширяет странное поведение

В последнее время я немного поигрался с базовым представлением и только что столкнулся с ОЧЕНЬ странным поведением при создании собственных подклассов представления:

Мне кажется, что вызов extend () в подклассах Backbone.View пытается объединить атрибуты объекта в определении суперкласса (извините за варварскую формулировку).

Обратите внимание на следующий код:

(function() {
var SomeCustomView = Backbone.View.extend({
    children : {},

    //internal use only
    _children : {},

    counter : 0,

    initialize : function(options){
    },

    render : function(){
        var t = this;
        _.each(this.children, function(child, childId){
            if(_.isFunction(child))child=child.call(this);
            this._children[childId] = child;
            this.counter++;
        },t );

        return this;
    },

});

//register on window object
this.SomeCustomView = SomeCustomView;


}).call(this);

затем создание подклассов SomeCustomView:

(function() {

console.error("START STRANGE BUG TEST");

var BetterCustomView = SomeCustomView.extend({
    children : {
        firstChild : { name : 'hi'}
    }
});

var instance1 = new BetterCustomView({ id : 'one' });

console.error("   _____________________________   ");

console.error("instance1.counter before render" + instance1.counter);
instance1.render();
console.error("instance1.counter after render" + instance1.counter);

console.error("instance1 _children");
console.error(instance1._children);

console.error("instance1._children.firstChild");
console.error(instance1._children.firstChild);
console.error("instance1._children.secondChild");
console.error(instance1._children.secondChild);

console.error("   _____________________________   ");



var EvenBetterCustomView = SomeCustomView.extend({
    children : {
        firstChild : { name : 'wuazza', foo : 'bar' },
        secondChild : { name : 'NotSupposedToBeInUltimate'}
    }
});

var instance2 = new EvenBetterCustomView({ id : 'two' });

console.error("   _____________________________   ");

console.error("instance2.counter before render" + instance2.counter);
instance2.render();
console.error("instance2.counter after render" + instance2.counter);

console.error("instance2 _children");
console.error(instance2._children);

console.error("instance2._children.firstChild");
console.error(instance2._children.firstChild);
console.error("instance2._children.secondChild");
console.error(instance2._children.secondChild);

console.error("   _____________________________   ");




var TheUltimateCustomView = SomeCustomView.extend({
    children : {
        firstChild : { name : 'whizzz' }
    }
});

var instance3 = new TheUltimateCustomView({ id : 'three' });

console.error("   _____________________________   ");

console.error("instance3.counter before render" + instance3.counter);
instance3.render();
console.error("instance3.counter after render" + instance3.counter);

console.error("instance3 _children");
console.error(instance3._children);

console.error("instance3._children.firstChild");
console.error(instance3._children.firstChild);
console.error("instance3._children.secondChild");
console.error(instance3._children.secondChild);


console.error("   _____________________________   ");

 }).call(this);

теперь консоль выводит следующее:

        START STRANGE BUG TEST test.html:46
       _____________________________    test.html:56
    instance1.counter before render0 test.html:58
    instance1.counter after render1 test.html:60
    instance1 _children test.html:62
    Object {firstChild: Object}
     test.html:63
    instance1._children.firstChild test.html:65
    Object {name: "hi"} test.html:66
    instance1._children.secondChild test.html:67
    undefined test.html:68
       _____________________________    test.html:70
       _____________________________    test.html:83
    instance2.counter before render0 test.html:85
    instance2.counter after render2 test.html:87
    instance2 _children test.html:89
    Object {firstChild: Object, secondChild: Object}
     test.html:90
    instance2._children.firstChild test.html:92
    Object {name: "wuazza", foo: "bar"} test.html:93
    instance2._children.secondChild test.html:94
    Object {name: "NotSupposedToBeInUltimate"} test.html:95
       _____________________________    test.html:97
       _____________________________    test.html:110
    instance3.counter before render0 test.html:112
    instance3.counter after render1 test.html:114
    instance3 _children test.html:116
    Object {firstChild: Object, secondChild: Object}
     test.html:117
    instance3._children.firstChild test.html:119
    Object {name: "whizzz"} test.html:120
    instance3._children.secondChild test.html:121
    Object {name: "NotSupposedToBeInUltimate"} test.html:122
       _____________________________    

Обратите внимание, что каждый раз в реальных тестах я расширяю SomeCustomView. Обратите внимание, как в третьем подклассе TheUltimateCustomView вы можете найти «secondChild», который был объявлен внутри EvenBetterCustomView и помещен внутри объекта _children во время вызова функции render ();

Для меня это, мягко говоря, очень странное поведение.

TheUltimateCustomView не расширяет EvenBetterCustomView, в котором был объявлен secondChild. Более того, мы тестируем объект _children, который заполняется во время вызова render () в INSTANCE SUBCLASS нашего CustomViewClass. Как он попадает в другие подклассы CustomViewClass ...?

Может кто-то объяснить это мне ? Является ли это ошибкой в ​​том, как BackBone.View выполняет свой метод .extend?

Я что-то ужасно не так делаю?

            <html>

        <head>



            <script type="text/javascript" src="jquery.js"></script>
            <script type="text/javascript" src="underscore.js"></script>
            <script type="text/javascript" src="backbone.js"></script>

            <script type="text/javascript">
        (function() {
            var SomeCustomView = Backbone.View.extend({
                children : {},

                //internal use only
                _children : {},

                counter : 0,

                initialize : function(options){
                },

                render : function(){
                    var t = this;
                    _.each(this.children, function(child, childId){
                        if(_.isFunction(child))child=child.call(this);
                        this._children[childId] = child;
                        this.counter++;
                    },t );

                    return this;
                },

            });

            //register on window object
            this.SomeCustomView = SomeCustomView;


        }).call(this);


        (function() {

            console.error("START STRANGE BUG TEST");

            var BetterCustomView = SomeCustomView.extend({
                children : {
                    firstChild : { name : 'hi'}
                }
            });

            var instance1 = new BetterCustomView({ id : 'one' });

            console.error("   _____________________________   ");

            console.error("instance1.counter before render" + instance1.counter);
            instance1.render();
            console.error("instance1.counter after render" + instance1.counter);

            console.error("instance1 _children");
            console.error(instance1._children);

            console.error("instance1._children.firstChild");
            console.error(instance1._children.firstChild);
            console.error("instance1._children.secondChild");
            console.error(instance1._children.secondChild);

            console.error("   _____________________________   ");



            var EvenBetterCustomView = SomeCustomView.extend({
                children : {
                    firstChild : { name : 'wuazza', foo : 'bar' },
                    secondChild : { name : 'NotSupposedToBeInUltimate'}
                }
            });

            var instance2 = new EvenBetterCustomView({ id : 'two' });

            console.error("   _____________________________   ");

            console.error("instance2.counter before render" + instance2.counter);
            instance2.render();
            console.error("instance2.counter after render" + instance2.counter);

            console.error("instance2 _children");
            console.error(instance2._children);

            console.error("instance2._children.firstChild");
            console.error(instance2._children.firstChild);
            console.error("instance2._children.secondChild");
            console.error(instance2._children.secondChild);

            console.error("   _____________________________   ");




            var TheUltimateCustomView = SomeCustomView.extend({
                children : {
                    firstChild : { name : 'whizzz' }
                }
            });

            var instance3 = new TheUltimateCustomView({ id : 'three' });

            console.error("   _____________________________   ");

            console.error("instance3.counter before render" + instance3.counter);
            instance3.render();
            console.error("instance3.counter after render" + instance3.counter);

            console.error("instance3 _children");
            console.error(instance3._children);

            console.error("instance3._children.firstChild");
            console.error(instance3._children.firstChild);
            console.error("instance3._children.secondChild");
            console.error(instance3._children.secondChild);


            console.error("   _____________________________   ");

        }).call(this);

            </script>



        </head>


        <body>
        coucou

        </body>



        </html>

Ниже представлена ​​полная html-страница, на которой вы можете проверить это поведение:

Большое спасибо за Вашу помощь.


person azpublic    schedule 22.02.2014    source источник


Ответы (1)


Если быть более точным, когда вы это сделаете:

(function() {
var SomeCustomView = Backbone.View.extend({
    ...
    //internal use only
    _children : {},

    ...

});

у каждого экземпляра есть собственное поле _children, но все они имеют одно и то же инициализированное значение {}, поэтому все представления, расширяющие SomeCustomView, будут иметь одно и то же значение, если вы не измените _children на другое значение.

Попробуйте что-то вроде этого:

(function() {
var SomeCustomView = Backbone.View.extend({
    children : {},

    //internal use only
    _children : null, // or whatever you want

    ...

    render : function(){
        this._children = {}; // here your instance will have it's own value
        ...
    },

});

Вот пример

person Rida BENHAMMANE    schedule 22.02.2014
comment
Хорошо, но почему значение счетчика равно 0 в каждом экземпляре перед рендерингом? Также есть ли другой способ определить _views в суперклассе без необходимости переопределять его каждый раз в подклассах? В противном случае это кажется мне очень нелогичным, поскольку я ожидал, что расширение фактически `` расширит '' класс View с помощью нестатического объекта, то есть он не используется всеми другими экземплярами. - person azpublic; 22.02.2014
comment
Я обновил свой ответ. Разница между _children и counter в том, что первый - это объект, а второй - примитив. Объекты назначаются по ссылке, а примитивы - по значению. - person Rida BENHAMMANE; 23.02.2014
comment
Хорошо, спасибо, Рида. Я прочитал эту статью, в которой подробно объясняется проблема и предлагается хорошее решение: erichynds.com/blog/backbone-and-inheritance - person azpublic; 23.02.2014