Is there an idea to add function definition in an object literal or array literal?

For instance, the following external method can be accessed in the same scope of the obj.

console.log(external); // => ƒ external() {}

const obj = {
    a : 1,
    function external() {
       
    },
    b : 2,
};

console.log(external); // => ƒ external() {}
console.log(obj.external); // => undefined

What's the use case for this? Is it, perhaps, because you want to define a function next to a location where you're using it, like this?

const obj = {
  // ...lots of stuff
  function someHelper() { ... },
  a: someHelper(1),
  b: someHelper(2),
  // ...lots more stuff
}

Or, do you have some other use case in mind?

It should be useful when refactoring a large object or array, from which you want to remove some unnecessary properties or items, keeping the code history at the same time.

Take the following snippet as an example:

const obj = {
    // ...
    _private: () => {
        // ...
    }, // no need to be exposed in the object
    a : () => obj._private(),
};

obj._private();

Without such a sugar, we may do refactor like this:

const obj = {
    // ...
};

function _private() {
    // ...
}

Object.assign(obj, {
    a : () => _private(),
});

_private();

But with such a sugar, we can directly transform it as a function definition:

var obj = {
    // ...
    function _private() {
        // ...
    },
    a : () => _private();
};

_private();

What do you mean by "keeping the code history"?

If you want the function to have access to the object, define it inside a closed-over scope that also has access to the object. If you want it not to be exposed, don't expose it.

If you want it associated with an object but not visible by holders of the object, store it in a WeakMap.

1 Like

"Keeping the code history" means that the shit code has been coded by former contributors and if you want Git to keep tracing the coding history after your refactoring, you should not move the code.

That's never been a constraint for me when refactoring; when one blames backwards, one often has to stop at refactor commits and restart the blame process from that point.

Either way, the language shouldn't be designed to optimize for the constraints of a version control system, even one as common as git.

3 Likes

Putting function declarations in the middle of object literals (and likely, class definitions?) without the expectation that they actually become part of that object/class would make code very confusing.
I need to read and understand code way more often than looking at its history.

1 Like

+1 for not putting too much weight on 'git blame'. There is a lot more to a function than who was the last author of a particular line. Code gets moved around, files get run through re-formatting tools. If someone is not taking these changes into account when tracking the history then they are likely to arrive at false conclusions.

1 Like

Not only for 'git blame', but also reducing the complication for reviewers to review code in one "shot" when we can refactor without moving a large snippet of code. Any new thing should bring some overhead for developers to learn, and I don't think a function declaration inside an object or an array will bring us a large barrier to reading code.

Authoring and reviewing best practices should take care of reducing the impact of reviewing refactors. I see no reason to introduce syntax to the language for such motivation. Any new syntax requires people to learn and understand what that syntax does. When reading your example, it adds more scoping hazards, which are hard to understand, and have been mostly relegated to "legacy" usages of var and function declarations.

2 Likes

I don't think so. Reviewers should also be JavaScript developers, and why can't we motivate a plan for them?

JavaScript developers have strong expectations informed by 25 years of the language, and this suggestion would violate that.

1 Like

I think, part of the problem here, is that while you find this particular piece of syntax to be an intuitive addition, others might not. I, for one, without any prior knowledge would expect the proposed syntax to behave the same as inserting the function into an object. e.g.

// I would naturally expect this:
const obj = {
  function fn() {}
}
// to be an alternative syntax for this:
const obj = {
  fn() {}
}

The fact that the function definition is being hoisted out of the object declaration would be something extra I would need to learn about, and wouldn't be something that I would easily guess, so for me, this syntax addition would have just as high of a barrier of entry as any other syntax addition, and the desire to not want to move code around doesn't feel like a strong enough motivation for new syntax.

2 Likes

But in a refactoring such as your example, the body of _private is quite important. A reviewer would certainly like to see whether _private references this. Whether the original _private was an arrow or regular method is irrelevant; in either case the new regular function would behave differently.

1 Like