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

в 16:18, , рубрики: javascript, замыкания, хак, метки: , ,
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); 

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

Автор: zimorodok

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js