Глубокое копирование в 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 таже логика но всё более прозрачно. Но каким образом, при этом, копировать значения самой функции - что-то мне совсем не понятно :-? |
Функция в js тоже объект.
alert((function(){}) instanceof Object)Так что по ссылке. Всё кроме примитивов - по ссылке. А клонировать фунцию ни в каком разумном случае не требуется. Если требуется - значит что-то вы делаете не так. |
Да, в общем случае функцию склонировать невозможно, да и не нужно. Просто передают ссылку на неё, как примитивное значение. Конечно, это объект, но только с методами из прототипа, а что-то своё в этот объект добавлять не принято.
Передать по ссылке можно и регулярку (объект RegExp), если она без флага g. А если с этим флагом, то создать новый регекс с тем же паттерном и скопировать поле lastIndex. Любые иммутабельные объекты можно передавать по ссылке - промисы, блобы и т.д. Клонировать надо {}, [], специальные объекты вроде Date, Map, Set, бинарные массивы. В общем, всё, что мутабельное. |
var sum = function(a, b) { return a + b; } sum.a = 'Masha'; sum.b = 'Phedya'; Ну, то есть, даже вот такую простейшую функцию со своими свойствами(да, по-видимому и без них) - не склонировать? Она будет существовать в единственном экземпляре, до тех пор, пока на неё ссылается хотя бы одна ссылка? Вообще, как-то странно, прочитать её можно, выполнить - тоже, а сделать точную копию, то есть прочитать и записать в другую "область памяти" - нельзя... PS: навскидку, сделать какие-нибудь финты ушами - проверить, на функцию ли ссылается наша переменная, если да, вызвать какой-нибудь хитрый .toString и сохранить результат строке, а потом из неё соорудить новую функцию через new Function, возможен такой ход мысли или может ещё какой?... |
Можно всё скопировать кроме внутренних полей!
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!) А зачем вам именно копия функции? |
Цитата:
Можно сделать обертку, которая будет вызывать исходную функцию: 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 надо свою функцию использовать, я так уж для примера написал |
Цитата:
|
Спасибо большое ответившим, идея более-менее ясна, буду учить синтаксис ES6, чтоб понять Ваши примеры!
|
Кстати, можно сделать копию, вот таким незамысловатым образом(ходил вокруг да около, а в голову, чего-то не пришло).
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), по-видимому, потребуется копирование отдельно методов, и отдельно тела функции, и их дальнейшего соединения в что-то общее и работающее - вопрос всё-таки требующий отдельного рассмотрения.:) |
Часовой пояс GMT +3, время: 04:37. |