Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

130
Visualizações
Making a true copy of a function in Javascript

I would like to define two functions (or classes) in Javascript with the exact same function body, but have them be completely different objects. The use-case for this is that I have some common logic in the body which is polymorphic (the function can accept multiple types), but by only calling the function with a single type the function ends up faster, I assume since the JIT can take a happier fast path in each case.

One way of doing this is simply to repeat the function body entirely:

function func1(x) { /* some body */ }
function func2(x) { /* some body */ }

Another way of accomplishing the same thing with less repetition is eval():

function func(x) { /* some body */ }
function factory() { return eval("(" + func.toString() + ")") }
let func1 = factory(), func2 = factory()

The downside of eval() of course being that any other tools (minifiers, optimisers, etc) are completely taken by surprise and have the potential to mangle my code so this doesn't work.

Are there any sensible ways of doing this within the bounds of a standard toolchain (I use Typescript, esbuild, and Vite), without using eval() trickery or just copy-pasting the code? I also have the analagous question about class definitions.


Edit: to summarise what's been going on in the comments:

  1. Yes, the performance difference is real and measurable (especially on Chrome, less pronounced on Firefox and Safari), as demonstrated by this microbenchmark. The real program motivating this question is much larger and the performance differences are much more pronounced, I suspect because the JIT can do more inlining for monomorphic functions, which has many knock-on effects.
  2. The obvious solution of returning a closure does not work, i.e.
    function factory() { function func() { /* some body */ } return func }
    let func1 = factory(), func2 = factory()
    
    as demonstrated by this second microbenchmark. This is because a JIT will only compile a function body once, even if it is a closure.
  3. It may be the case that this is already the best solution, at least when working within a standard JS/Typescript toolchain (which does not include code-generation or macro facilities).
over 4 years ago · Santiago Trujillo
4 Respostas
Responde à pergunta

0

you can try this way:

function func1(x) { /* some body */ }

var func2 = new Function("x", func1.toString().match(/{.+/g)[0].slice(1,-1));

I are defining new function func2(x) using the function constructor where the first n-1 arguments are parameters and the last parameter is the function body

for function body I used regex to extract all the lines in the scope of func1 i.e. between the function braces { and }

you can read more about the Function constructor here

over 4 years ago · Santiago Trujillo Relatório

0

  1. Use the file system to make a single ESM module file appear as multiple different files.
  2. Then import your function multiple times.

The only toolchain requirement is esbuild, but other bundlers like rollup will also work:

Output:

// From command: esbuild  main.js --bundle
(() => {
  // .func1.js
  function func(x2) {
    return x2 + 1;
  }

  // .func2.js
  function func2(x2) {
    return x2 + 1;
  }

  // main.js
  func(x) + func2(x);
})();



// From command: rollup main.js
function func$1(x) { return x+1 }

function func(x) { return x+1 }

func$1(x) + func(x);

With these files as input:

// func.js
export function func(x) { return x+1 }
// main.js
import {func as func1} from './.func1';
import {func as func2} from './.func2';

func1(x) + func2(x)

The imported files are actually hard links to the same file generated by this script:

#!/bin/sh
# generate-func.sh

ln func.js .func1.js
ln func.js .func2.js

To prevent the hard links from messing up your repository, tell git to ignore the generated hard links. Otherwise, the hard links may diverge as separate files if they are checked in and checked out again:

# .gitignore
.func*

Notes

  • I put everything in the same folder for simplicity, but you can generate the hard links in their own folder for organization.
  • Rollup will "see through" this trick if you use symlinks to the same JS file. However symlinks to directories work fine.
  • Tested on git-bash for Windows; YMMV on other platforms.
over 4 years ago · Santiago Trujillo Relatório

0

Your idea of having a "factory" or a master function that would produce independent, physically separate, functions instead of referencing to the same one is a very good start...

In times before CSS animations, we had to use JavaScript for creating timed effects and so on. The idea of hovering over elements that would light them up, but as the mouse leaves that element hovering over the other, you'd want them to slowly fade out, ( in sort of leaving a smooth trail of light kind of fashion ), not abruptly over hundreds of elements, it would be impossible to do with a single function, whereas rewriting the same function body with slightly different names hundreds of times would be nonsensical, let alone assigning each function to the target element individually.

We faced the same problem...

To cut the story short, we had to go with a 'sensible solution' as you say, or drop the idea completely..., I didn't!

Here is a logic behind the (Factory) solution and a console log content to prove that these identical twin functions are physically separate (as required) not references to the same.

function Factory( x ){ return function( ){ console.log( x ) } };

func1 = new Factory("I'm the first born!");
func2 = new Factory("I'm the second born!");

func1(); func2();

Hope you find this solution sensible enough.

p.s.: You can add as many arguments you need to the Factory and provide their specific values during the creation of the functions which will be available throughout the session at all times, just as the console.log string we see in the demo is.

Regards.

over 4 years ago · Santiago Trujillo Relatório

0

Playing around with the Function constructor function, I guess that this would do the job

function func(x) { /* some body */ }
function factory() {
    return (
        new Function('return ' + func.toString())
    )();
}

let func1 = factory(), func2 = factory()
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda