Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 28.07.2019, 20:28
Интересующийся
Отправить личное сообщение для Launder Посмотреть профиль Найти все сообщения от Launder
 
Регистрация: 25.04.2019
Сообщений: 19

Глубокое копирование в JavaScript. Функция + объект.
Насколько я понимаю, функция копируется как примитив - по значению. А объект - по ссылке. Допустим у нас есть определённая функция и мы сохраняем её в переменную - мы получим ещё одну копию. Но если у этой функции есть свойства или методы, как быть тогда?
Как совместить эти способы?...
Ниже способ копирования среднестатистического объекта с возможными перекрёстными ссылками. Идея в том, что мы делаем полные копии всех объектов, на которые ссылается данный объект, но только один раз. И там где ссылка ссылается на оригинал, в копии мы ссылаемся на копию этого оригинала.
Удобнее всего это сделать с помощью Map:
function deepCopy(obj)
 	     {
			var newObj = new Object();
								 
			var map = new Map();
			map.set(obj, newObj);

			function engine(obj1, newObj1)
						 {
							for(var key in obj1)
								{
								         if( typeof( obj1[key] ) !== 'object' || obj1[key] == null)
									       {	
										      newObj1[key] = obj1[key];
									        }
								          else if(  !map.get( obj1[key] )   )
                                              	                                {
                                            			                     newObj1[key] = new Object();
                                            			                     map.set(obj1, newObj1);
                                            			                     engine( obj1[key], newObj1[key] );
                                                                                 }
									  else
                                               		                         {             
                                            			                     newObj1[key] = map.get(obj1[key]);                                                   
                                                                                 }                                        
                                                               }
                                                      }
                        engine(obj, newObj);
			return newObj;
                }


Можно это делать и без Map, делаешь массив из ссылок на объекты, а когда сталкивается с новым объектом при копировании, то, сначала просматриват свою коллекцию ссылок и если находит, то, там массив из двух элементов, первый элемент - старая ссылка, второй - новая. нашли старую - выдали новую. если кому интересно скину код, но он слегка объёмный, с Map таже логика но всё более прозрачно.
Но каким образом, при этом, копировать значения самой функции - что-то мне совсем не понятно
Ответить с цитированием
  #2 (permalink)  
Старый 28.07.2019, 21:56
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,577

Функция в js тоже объект.
alert((function(){}) instanceof Object)
Так что по ссылке. Всё кроме примитивов - по ссылке.
А клонировать фунцию ни в каком разумном случае не требуется. Если требуется - значит что-то вы делаете не так.
__________________
29375, 35

Последний раз редактировалось Aetae, 28.07.2019 в 22:02.
Ответить с цитированием
  #3 (permalink)  
Старый 29.07.2019, 01:35
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,012

Да, в общем случае функцию склонировать невозможно, да и не нужно. Просто передают ссылку на неё, как примитивное значение. Конечно, это объект, но только с методами из прототипа, а что-то своё в этот объект добавлять не принято.

Передать по ссылке можно и регулярку (объект RegExp), если она без флага g. А если с этим флагом, то создать новый регекс с тем же паттерном и скопировать поле lastIndex.

Любые иммутабельные объекты можно передавать по ссылке - промисы, блобы и т.д.

Клонировать надо {}, [], специальные объекты вроде Date, Map, Set, бинарные массивы. В общем, всё, что мутабельное.
Ответить с цитированием
  #4 (permalink)  
Старый 29.07.2019, 17:43
Интересующийся
Отправить личное сообщение для Launder Посмотреть профиль Найти все сообщения от Launder
 
Регистрация: 25.04.2019
Сообщений: 19

var sum = function(a, b)
			   {
            return a + b;       
         }
sum.a = 'Masha';
sum.b = 'Phedya';


Ну, то есть, даже вот такую простейшую функцию со своими свойствами(да, по-видимому и без них) - не склонировать? Она будет существовать в единственном экземпляре, до тех пор, пока на неё ссылается хотя бы одна ссылка? Вообще, как-то странно, прочитать её можно, выполнить - тоже, а сделать точную копию, то есть прочитать и записать в другую "область памяти" - нельзя...
PS: навскидку, сделать какие-нибудь финты ушами - проверить, на функцию ли ссылается наша переменная, если да, вызвать какой-нибудь хитрый .toString и сохранить результат строке, а потом из неё соорудить новую функцию через new Function, возможен такой ход мысли или может ещё какой?...

Последний раз редактировалось Launder, 29.07.2019 в 18:03.
Ответить с цитированием
  #5 (permalink)  
Старый 29.07.2019, 18:38
Аватар для Malleys
Профессор
Отправить личное сообщение для Malleys Посмотреть профиль Найти все сообщения от Malleys
 
Регистрация: 20.12.2009
Сообщений: 1,714

Можно всё скопировать кроме внутренних полей!

var sum = function(a, b) {
	return a + b;
}
sum.a = 'Masha';
sum.b = 'Phedya';

function cloneFunction(fn) {
	return Object.assign({ __proto__: fn.constructor.prototype }, fn);
}

var sum2 = cloneFunction(sum);
console.log(sum2);


Но такую функцию вы не можете вызвать как функцию, поскольку у неё нет внутреннего поля [[Call]]. В JS нет публичного механизма для чтения и копирования внутренних полей (так называемые слоты), поэтому невозможно произвести ручное копирование некоторых объектов, которые были созданы при помощи встроенных конструкторов (например, функции, промисы и т. д.), если только класс не предоставляет метод для копирования объекта (как например метод cloneNode у класса Node!)

А зачем вам именно копия функции?

Последний раз редактировалось Malleys, 29.07.2019 в 18:45.
Ответить с цитированием
  #6 (permalink)  
Старый 29.07.2019, 18:47
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,012

Сообщение от Launder
Ну, то есть, даже вот такую простейшую функцию со своими свойствами(да, по-видимому и без них) - не склонировать?
такую - можно) Но в общем случае функция может хранить внешние переменные в замыкании, либо быть стрелочной функцией и держать кого-то за this. И вот это мы не склонируем.

Можно сделать обертку, которая будет вызывать исходную функцию:
function cloneFunc(f) {
  if (!f) { return null; }
  var args = Array.from({length: f.length}).map((v, i) => 'a' + i).join(',');
  var newFunc = new Function('f', 'return function (' + args + ') { return f.call(this, ' + args + '); }')(f);
  return Object.assign(newFunc, f);
}

здесь костыль с "new Function" позволяет сохранить length исходной функции, так то можно и проще сделать.

----
если у нас глубокое копирование, то вместо Object.assign надо свою функцию использовать, я так уж для примера написал

Последний раз редактировалось Alexandroppolus, 29.07.2019 в 18:52.
Ответить с цитированием
  #7 (permalink)  
Старый 30.07.2019, 00:11
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,577

Сообщение от Launder Посмотреть сообщение
Вообще, как-то странно, прочитать её можно, выполнить - тоже, а сделать точную копию, то есть прочитать и записать в другую "область памяти" - нельзя...
Проблема, как уже заметили выше, в том, что функция тянет за собой контекст: замыкания и пр., так что получить абсолютно независимый клон не получится, для этого надо весь поток склонировать. Иначе же сайдэффекты всё равно будут, хоть тресни. Потому и не имеет это смысла.
__________________
29375, 35
Ответить с цитированием
  #8 (permalink)  
Старый 05.08.2019, 18:53
Интересующийся
Отправить личное сообщение для Launder Посмотреть профиль Найти все сообщения от Launder
 
Регистрация: 25.04.2019
Сообщений: 19

Спасибо большое ответившим, идея более-менее ясна, буду учить синтаксис ES6, чтоб понять Ваши примеры!
Ответить с цитированием
  #9 (permalink)  
Старый 03.10.2019, 18:50
Интересующийся
Отправить личное сообщение для Launder Посмотреть профиль Найти все сообщения от Launder
 
Регистрация: 25.04.2019
Сообщений: 19

Кстати, можно сделать копию, вот таким незамысловатым образом(ходил вокруг да около, а в голову, чего-то не пришло).

function simple(a, b)
		{
    		    var A = a*a;
                    var B = b*b;
                    var C = A*B;
                    return C;
                }
    
var simple1 = String(simple); //превращаем в строку

simple2 = simple1.slice(0, 83) + '/' + simple1.slice(84); //меняем знак

changeSimple = new Function(simple2[16], simple2[19], simple2.slice(32, 104)); //копируем отдельно переменные, отдельно тело, и приводим её в боевую готовность

alert( simple(5, 2) );  //100
alert( changeSimple(5, 2) ); //6.25


То есть, мы скопировали функцию в строку, немного поменяли(чтоб было немного посложней и поинтересней) и вновь собрали работающий экземпляр.
Понятно, что это всё в первом приближении, что тут много допущений, о которых говорилось выше(например, замыкания, и не понятные мне "слоты", впрочем понятно, что есть некие внутренние поля, которыми движок, при компиляции, помечает какие-то характерные участки кода), что в одном случае, Function Declaration, в другом Function Expression, что добавим мы пару пробелов перед названием, и тело функции изменит свои "координаты" в строке(впрочем, не думаю, что найти тело и аргументы будет такой уж проблемой, у произвольной, и не слишком замысловатой функции), что если функция используется как объект (то есть в ней хранятся свойства и методы), то, хоть и не сложно идентифицировать функцию (с помощью Object.prototype.toString), по-видимому, потребуется копирование отдельно методов, и отдельно тела функции, и их дальнейшего соединения в что-то общее и работающее - вопрос всё-таки требующий отдельного рассмотрения.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Помогите с javascript andruhin Общие вопросы Javascript 12 04.05.2012 10:05
Последние книги по JavaScript! monolithed Учебные материалы 7 26.10.2010 19:40
Выдвет ошибку JavaScript Ромио Opera, Safari и др. 4 21.10.2010 20:34
Функция JavaScript со вставкой РНР Tariel Internet Explorer 9 18.12.2009 19:19
как сделать гиперсылку на объект javascript??? kos_walker Общие вопросы Javascript 3 30.09.2008 06:58