Две маленьких функции, способных упростить жизнь

    Я знаю про Backbone.js и про Knockout.js
    Просто иногда хочется чего-то значительно меньшего.

    1. Введение. О чем речь, какова предметная область. Какая существует проблема.

    О чем речь: есть JavaScript, «сферический, в вакууме».
    Предметная область: объекты и их свойства, то, что задается приведенными ниже способами.

    var obj = new Object();
    var obj = {};
    


    где свойства, соответственно:

    obj.prop = 111;
    obj = {
       "prop1" : "value1"
       "prop2" : 2
    }
    


    Какая существует проблема:

    1. Есть вот такие вот объекты, у них есть эти самые свойства.
    2. Хочется узнать, когда свойство было изменено ( часть MVVM pattern )
    3. И, соответственно, подписаться на это изменение.

    UPD 1Пример в работе, спасибо alist, за очень, по моему мнению, важный комментарий ниже.



    2. Подробно, что за задача которую нужно решить.

    Собственно, это и есть задача, подписаться на изменения свойств, и вызвать функцию, которая может что-нибудь сделать с этим новым значением свойства. Пусть на самом «элементарном» уровне. Достаточно отслеживать изменение простых свойств: чисел, строк, boolean. Т.е., свойства с вложенными массивами, объектами и функциями нам может быть даже и не нужны.

    Даже такое простое «подписывание с callback» на такие простые свойства уже может существенно сократить время разработки. Например, можно навесить триггер на получение новых данных из socket, или подписаться на изменение какого-нибудь свойства в модели, которое визуально выведено на страничку.

    3. Аналоги. Существующие решения проблемы, какие у них плюсы-минусы

    Сильно много не искал, для Тяжелых вещей ведь достаточно Backbone.js и Knockout.js.

    Так что это ни в коем случае им не аналог, так, заглушка для элементарных операций, или просто если эти библиотеки «не участвуют» в коде.

    4. Описание решения проблемы.

    На коленке за ночь пересобрал две функции, способные, соответственно, разными способами с тем или иным успехом со своими плюсами и минусами решить подобную задачу:

    warp ( obj, prop, setcallback, interval )

    observe( value, setcallback )

    Плюс observe ():
    — короткий синтаксис
    — никаких таймаутов, геттеров и прочего
    — элементарен как топор, будет работать везде где есть JS
    Минус observe (): вместо просто свойства будет функция, и об этом нужно помнить.

    Плюс warp(): реально подписывается на реальное свойство, т.е. можно просто присваивать новые значения свойству внутри объекта.
    Минусы warp():
    — объемный синтаксис.
    — если браузер «старый», без геттеров и сеттеров, то интервалы отслеживания изменений будут сильно его грузить при отсутствующем «interval».
    — я не проверял, обнуляется ли интервал при удалении свойства, по идее — должен.

    5. Вывод. Почему это стоит использовать и когда, а когда не стоит.

    Это можно использовать, если нужно сделать что-то простое, не требующее «концепции».

    Всё очень непритязательно:

    warp — подписывается на свойства с callback'ом на set.

    observe — подменяет свойство функцией с callback'ом на set.

    Небольшое объяснение под этой плашкой кода.

    Пример достаточно вставить в пустой HTML файл.

    <textarea id = "context" cols = "70" rows = "15"></textarea>
    


    
    try{
    
      var info = function(str){
    
        var x = document.getElementById('context');
    
        if(str !== undefined){ x.value += str + '\n'; }
    
        else{ x.value = ' '; }
    
        try{ x.scrollTop = x.scrollHeight;; }catch(e){ }
    
      }
    
    
    
      var warp = function( obj, prop, setcallback, interval ){
    
        try{
    
          var val = obj[prop];
    
          if( !obj ){ obj = window; }
    
          if(Object.defineProperty){
    
            Object.defineProperty( obj, prop, {
    
               get : function () { try{ return obj[prop]; }catch(e){ return; } }
    
              , set : function ( value ) {
    
                try{
    
                  val = value;
    
                  if(setcallback){
    
                    if(interval) { window.setTimeout( function(){
    
                      setcallback( value, prop, obj );
    
                    }, interval); }else{ setcallback( value, prop, obj ); }
    
                  }
    
                  return val;
    
                }catch(e){ return; }
    
              }
    
            });
    
          }else{
    
            if(!interval){ interval = 1000; }
    
            var intvl = window.setInterval( function(){
    
              if(obj[prop] !== undefined){
    
                if( val != obj[prop] ){
    
                  val = obj[ prop ];
    
                  if( setcallback ){ setcallback( val, prop, obj ); } 
    
                }
    
              }else{ window.clearInterval( intvl ); }
    
            }, interval);
    
          }
    
        }catch(e){ return; }
    
    
    
      }
    
      var observe = function( value , setcallback ){
    
        return function( val ){
    
          try{
    
            if( val !== undefined ){
    
              if( val != value ){
    
                value = val;
    
                if( setcallback ){ setcallback( value ); }
    
              }
    
            }else{ return value; }
    
          }catch(er){ alert(er); }
    
        };
    
      }
    
    
    
      var obj = { prop : 111, observe : observe ( 222, 
    
          function( val ){ info( 'observe : ' + val ); } ) };
    
    
    
      warp( obj, 'prop', function( val ){ info( 'callback for prop : ' + val ) }, 100 );
    
    
    
      var count1 = 0;
    
      window.setInterval( function(){ count1++; obj.prop = count1; }, 5000 );
    
      var count2 = 0;
    
      window.setInterval( function(){ count2++; obj.observe( count2 ); }, 1000 );
    
      window.setInterval( function(){ info( 'observer lookup : ' + obj.observe() ); } , 2000 );
    
    
    
    }catch(e){ alert(e); }
    
    


    UPD 0 — краткая справка.

    Попытаюсь рассказать как я это использую, по шагам.
    1. Пусть есть какой-то объект.
    2. У него есть какое-то свойство, строка, число или boolean.
    3. Я хочу знать когда оно изменится, и подписаться на это изменение с callback'ом, который примет новое value.

    Для этого я использую warp ( obj, prop, setcallback, interval )

    где:
    obj — тот самый объект
    prop — то самое свойство
    setcallback — тот самый callback, который получит измененное значение.
    interval — если хочу, чтобы callback отработал с задержкой.

    Это то, что касалось warp.

    То что касается observe: ситуация аналогичная, но мне достаточно не действительно слушать свойство а подменить его функцией.
    Тогда:
    1. Есть объект
    2. Есть свойство
    3. Я подменяю его функцией посредством
    observe( value, setcallback )

    после чего установка нового значения будет выглядеть как
    obj.my_property ( 'new value' );
    а чтение этого же значения как obj.my_property ( );

    Читабельные сорцы про геттеры и сеттеры:
    http://robertnyman.com/javascript/javascript-getters-setters.html#regular-getters-and-setters

    https://developer.mozilla.org/en/JavaScript/Reference/Operators/get

    https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects#Defining_Getters_and_Setters

    Но все должно работать и без этих feature.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 24

      +8
      первый вопрос — что это и для чего?, возможно стоить немного объяснить в статье.
        0
        Сейчас попробую.
          0
          объяснил как мог
            +3
            Вы меня простите, но я бы мог вам порекомендовать такой план:

            1. Введение. О чем речь, какова предметная область. Какая существует проблема.
            2. Подробно, что за задача которую нужно решить.
            3. Аналоги. Существующие решения проблемы, какие у них плюсы-минусы
            4. Описание решения проблемы.
            5. Вывод. Почему это стоит использовать и когда, а когда не стоит.

            А то вводная какая-то:
            >1. Пусть есть какой-то объект.
            Какой объект? Груша или яблоко? Ничего же не понятно.
              0
              Хорошо, я попробую.
              просто блог про JS, а я в такой прострации, что думал, что это итак всем ясно.
              Сейчас, минут 10.
                0
                В начале привел описание по Вашему плану.
                Спасибо! :)
                  +1
                  Ниже вам уже пишут :)
                    0
                    Да, спасибо!
            –1
            Супер, все ясно и понятно. Полезная штуковина!
              0
              Если бы не попросили объяснить, было бы пожалуй до сих пор туго.
              Господа, спасибо за то, что объяснили, что оно непонятно было написано.
              А то так и сидел бы и хватал минусы!

              С искренним уважением и наилучшими пожеланиями!
                0
                Очень интересно.
                Почему бы вам это всё не превратить в jQuery плагин? Который, по возможности, будет использовать встроенные возможности браузеров.
                  0
                  0. Это уже сделано более серьёзными людьми. Смотрите комментарий alist ниже

                  1. Это всё очень сильно усложнит. Данный подход можно же трансформировать как угодно.

                  2. Мне просто — лень :)
                  0
                  Ад, вообще, конечно.

                  А чем, сообственно, не нравится метод с Object#get и Object#set? Там кода на три строчки — все нормальные люди им пользуются и как-то, вцелом, довольны.
                    0
                    Это как?
                    Я просто не знаю, объясните пожалуйста…
                      0
                      Ну, как-как.

                      obj.set('prop', 'value');
                      obj.get('another_prop');
                        +1
                        А callback?
                          +1
                          Блин, а фантазию включать кто будет?

                          obj.bind('change:prop_1', function(oldValue, newValue){ alert('prop_1 was changed from ' + oldValue + ' to ' + newValue); });
                          obj.bind('get:prop_2', function(){ alert('prop_2 was accessed'); });


                          Тут можно понапридумывать все что угодно. Любые коллбеки, любые эвенты.
                            0
                            За комментарий — спасибо!
                            Но при чем тут «блин»?
                            Это какая версия JavaScript?
                            Это что-то из нового, я про это ещё ничего не знаю.
                            Можете ссылку «на почитать» (?), думаю не одному мне это будет важно…
                              0
                              Это любая версия джаваскрипта + враппер, который на коленке пишется в 5-10 строчек.
                                0
                                Вы про это?:

                                Object.prototype.set = function( prop , value ){
                                	return this[prop] = value;
                                }
                                
                                Object.prototype.get = function( prop ){
                                	return this[prop];
                                }
                                
                                

                                  0
                                  Ну, не обязательно так, но вцелом да.

                                  documentcloud.github.com/backbone/#Events — вот тут это реализовано. Можно пойти и почитать код, он простой как три рубля.
                    +1
                    Аналоги вашего решения (не говорим ни про Backbone, ни про Knockout):

                    msdn.microsoft.com/en-us/scriptjunkie/hh297451
                    bruth.github.com/synapse/docs/

                    Также BB и KO — не единственные JS-MV*-библиотеки. Есть еще Ember, Batman, Spine, Knockback (BB+KO), SproutCore, JavaScript MVC и еще наверное с десяток.

                    Тем, кому хочется посмотреть на пример автора в деле: jsfiddle.net/Qvs3t/

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

                  Самое читаемое