20 March 2012

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

Website developmentJavaScript
Я знаю про 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.
Tags:JavaScriptmvvm
Hubs: Website development JavaScript
+3
1k 63
Comments 24