Извлечение ссылки на объект из замыкания

    var  singleton = (function () {
    	var data, method_args;
    
    	data = [];
    	method_args = [];
    
    	function add (items) {
    		var i;
    
    		data.push(items);
    		method_args.push(arguments);
    	}
    
    	function remove () {
    		data.pop();
    		method_args.push(arguments);
    	}
    
    	return {
    		add : add,
    		remove : remove
    	}
    }());
    

    Есть доступ к объекту singleton.

    Публичные методы синглтона (он приведен как пример, и ценность составляет создаваемое при его инициализации замыкание) вызывают методы массива, который хотелось бы достать. В этих методах this ссылается на вожделенный массив. Значит до него можно добраться через this.

    Если бы не было method_args.push, то можно было бы обойтись переопределением Array.prototype.push (с обязательным возвратом все на свои места после «кражи» объекта).

    var original_push,
    	data;
    
    // запоминаем как было
    original_push = Array.prototype.push;
    
    // подставляем фальшивый метод
    Array.prototype.push = function () {
    	// извлекаем ссылку
    	data = this;
    };
    // запускаем фальшивый метод
    singleton.add();
    // возвращаем все на свои места
    Array.prototype.push = original_push;
    
    // массив в наших руках
    console.log(data); 
    

    Однако push используется не единожды. Разовое невыполнение метода может повлечь за собой поломку. А этого не хочется. Хочется ссылку на массив. Значит переписать код выше, сохраняя стандартную логику работы. Создаем новый метод push, который умен достаточно, чтобы взять ссылку this только при первом вызове, и может имитировать поведение по умолчанию.

    var original_push,
    	fake_method_calls,
    	data;
    
    // запоминаем как было
    original_push = Array.prototype.push;
    
    // считаем
    fake_method_calls = 0;
    
    // подставляем фальшивый метод
    Array.prototype.push = function () {
    
    	// запоминаем this только для первого вызова
    	if (fake_method_calls === 0) {
    		data = this;
    	}
    	fake_method_calls += 1;
    	// стандартная логика
    	return original_push.apply(this, arguments);
    }
    // запускаем фальшивый метод
    singleton.add();
    // возвращаем все на свои места
    Array.prototype.push = original_push;
    
    // массив в наших руках
    console.log(data); 
    

    Вуаля: овцы целы (стандартная логика не нарушена) и волки сыты (ссылка в руках).

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 19

      0
      Очень хороший метод, если надо изменить порядок выполнения, например, ф-ций, переданых в jQuery(document).ready
        0
        Если только нет явных методов для этого.
        0
        А с какой целью подобное может пригодиться?
          +1
          Как в первом комменте написано, изменения порядка выполнения обработчиков DOMReady в jQuery 1.4.2.

          Использовать такое решение в проектах, где у вас полный контроль над исходными данными очень не рекоммендую.
            +1
            Мне кажется, его в любом случае не стоит использовать, а как-то по-другому решать проблемы, где подходит этот метод (как с обработчиком ready). Такое решение--это дурной запах (точнее, отвратительное зловоние) того, что что-то не так с архитектурой.
              0
              В том случае не было доступа к всему проекту. Был только внешний скрипт, вставленный на страницу. Иного решения проблемы не было
                +1
                Такие решения называются «хаками» и, к сожалению, иногда без них не обойтись.
                  0
                  Подобные решения называются человек способен мыслить и понимает свой инструмент. Иногда в команде полезно иметь того кто способен отклонится от нормы.
                    0
                    Главное: не переусердствовать ;)
                    А умение мыслить и понимать свой инструмент нужно всегда, не только в таких случаях :)
                  0
                  Требовалось внести изменения «налету» в уже работающей системе без доступа к ее исходникам.
              0
              Мне кажется так немного прикольные

              var dataContainer = {};
              
              Array.prototype.push = (function(fnOriginal, dataContainer) {
                  var fakeCallsCount = 0;
              
                  return function() {
                      if (fakeCallsCount === 0) {
                          dataContainer.data = this;
                          Array.prototype.push = fnOriginal;
                      }
              
                      fakeCallsCount++;
              
                      return fnOriginal.apply(this, arguments);
                  };
              })(Array.prototype.push, dataContainer);
              
              [].push('test data1');
              [].push('test data2');
              
              console.dir(dataContainer);
              
                0
                Вообще-то приватные свойства для того и приватные, чтоб на них не завязываться. В следующей версии библиотеки уберут этот массив — и пользовательский код, который рассчитывал на него, перестанет работать. О том, что этот массив убран, никто не узнает до момента возникновения ошибки (потому как разработчик справедливо полагает, что его никто не использует, потому такие изменения не анонсирует).

                В этой ситуации (если уж точно никак без доступа к этому массиву не обойтись) я бы форкнул jQuery, и дописал бы нужный мне функционал там, тогда при обновлении версии в результате мерджа были бы видны изменения, которые это затрагивают.
                0
                Зачем счетчики? Можно же проще.

                var arr = [1,2,3], 
                    data, 
                    i = 1,
                    original_push = Array.prototype.push;
                
                Array.prototype.push = function () {
                    
                    console.log(i++);
                    
                    data = this;
                    
                    Array.prototype.push = original_push;
                
                    Array.prototype.push.apply(this, arguments);
                
                }
                
                arr.push(4);
                console.log(data);
                
                arr.push(5);
                console.log(data);
                
                  0
                  Помоему влезать в стандартный метод и править его это ради такого это слишком. Тем более в первом посте чуш написана, я не представляю ситуации когда надо влезать в обрабочик jquery. Решить задачу можно всегда проще.
                    0
                    Как пример — хаки для брузерных игр. Вполне годно использовать.
                      0
                      Я не рассматриваю такие вещи. Речь о разработке.
                        0
                        для игр у вас есть прекрасный веб инспектр кстати. делайте что хотите
                        0
                        Разработка бывает разная. В моем случае у меня есть только один js-файл, который включается на другой сайт. И исходя из этого мне надо плясать

                      Only users with full accounts can post comments. Log in, please.