3

I am looking at a JavaScript code, and I've noticed the following pattern across multiple functions

function getValues() {
    var values = ['a', 'b', 'c'];
    return (getValues = function () {
        return values;
    })();
}

from what I know the following function has the same effect of

function getValues() {
    return ['a', 'b', 'c'];
}

Does wrapping it in an IIFE here have a special effect? especially the following section return (fnName = function() {...})() Noticing that a variable always get assigned with the same name of the function.

Note: I am very familiar with IIFE concept and when it's useful, and not to confuse with the following pattern

var fn = (function() {
    function fn() {
    }
    fn.prototype.function1 = function () { /* ... */ }
    return fn;
})();

In my question the variable get assigned inside the function and not the opposite.

amd
  • 20,637
  • 6
  • 49
  • 67
  • 5
    In this case, it has no effect. In other cases, it's worth noting that the values exist in closure. But even closure can be achieved easier. This looks just like convoluted garbage code to me. – The Fool Dec 27 '21 at 09:20
  • 1
    Typescript converts classes to constructor functions this way: [Understanding Javascript generated by Typescript compiler](https://stackoverflow.com/questions/25072525) – adiga Dec 27 '21 at 09:25
  • Even if the `getValues` function assigned by an expression to the returned function internally, the item in closure is an array and it's a reference type. It's meaningless to have it under closure since you can modify it through the returned `values` value. – Redu Dec 27 '21 at 09:56
  • Where is the example function used? Can you provide a use-case of the pattern? – Teemu Dec 27 '21 at 10:20
  • @Teemu I am working on writing a JavaScript deobfuscator, and was collecting codes from different open source repos/public websites, I found this pattern in one of the code base – amd Dec 27 '21 at 10:35
  • OK, but any real use-case? As I wrote into a comment to FZs' answer, the pattern behaves very differently when one or more references to `getValues` function are stored before the function will be called for the first time. – Teemu Dec 27 '21 at 10:40

1 Answers1

5

Well, in this case it's part of a weird memoization technique.

The key point to notice is that the function reassigns itself:

return (getValues = function () {
        ^^^^^^^^^^^^

So, what happens when getValues is called for the first time is that getValues creates a new function that has values as a closure, then it replaces itself with that new function.

Because of that, further calls to getValues will call the same inner function, that returns the value from the closure without creating a different object every time.

You can check this behaviour by checking the return values:

console.log(getValues() === getValues()); //true

While, with your implementation, that wouldn't work:

function getValues() {
    return ['a', 'b', 'c'];
}

console.log(getValues() === getValues()); //false

Reassigning the function means that the value of getValues itself will change between the first and second calls. So:

var gv0 = getValues;
getValues();
console.log(gv0 === getValues); // false

That behaviour has ist own risks if multiple references are created to the function:

var gv0 = getValues;
console.log(getValues() === gv0()); // false!

By the way, the usual (and more readable) way to write memoization is to make the outer function an IIFE, that returns the inner function. It also eliminates the need for reassignment.

var getValues = (function () {
    var values = ['a', 'b', 'c'];
    return function () {
        return values;
    };
})();

That will, however, create the object immediately, while the code in question will only create it on the first call.

This difference is important if the creation of the object is expensive.

FZs
  • 16,581
  • 13
  • 41
  • 50
  • 2
    The difference to the "usual" way to write this IIFE is, as you stated, that the `values` are only created "*when `getValues` is called for the first time*", not up-front. – Bergi Dec 27 '21 at 09:56
  • @Bergi You're right, I'll mention that. – FZs Dec 27 '21 at 10:04
  • 1
    OP's example behaves very differently depending on the calling order, when `getValues` is used as a borrowed method of multiple objects. If you'll borrow all the methods first, and then call the methods, the array is unique for every method, but when borrowing a single method, and then call it, all the later borrowed methods are sharing the same array. – Teemu Dec 27 '21 at 10:16