Functions are objects, callable objects. Regular object initializer benefits don't apply to callable objects like spread operator and __proto__ initializer
To be more inclusive shouldn't we make a way to define a callable object as a regular one?
I don't know what property name to use to be backward compatibile, but I start proposing the keyword function:
f = {
function(parameters) {
// β¦
},
// or (exclusive)
function: (parameters)=>{
// β¦
},
// or (exclusive)
function: {
function(parameters) {
// β¦
},
// other properties
},
// and the rest of goodies:
a: "foo",
b: 42,
c: {},
1: "number literal property",
"foo:bar": "string literal property",
shorthandProperty,
method(parameters) {
// β¦
},
get property() {},
set property(value) {},
[expression]: "computed property",
__proto__: prototype,
...spreadProperty,
};
Like the use of __proto__, any second function property to be an error or any non callable object as value of function to be an error as well.
In this example f will be f.function with all the properties assigned to f as spread operator, not as Object.assign(). Today we can only use Object.assign() to load a function object with properties. The __proto__ value to be applied to f, replacing the prototype chain of f.function. After initialization, the function accessor is not needed anymore, it shall disapear.
Edit 1:
If function accessor remains we can get another benefit which current stage of ES is missing: callability inheritance: If another object o takes f in its prototype chain, o becomes callable by the presence of function property.
Reminds me of TypeScript's syntax for defining a call signature as part of an object type, that doesn't use a keyword and instead is like an anonymous method
{
prop: string;
(arg: number): number; // type is callable
}
I do worry about the effect of having callable objects appear in JavaScript.
Today, if you want to check if something is callable, you can just do typeof value === 'function'. With something like this, that wouldn't be the case anymore. (In fact, we would probably need to create a new function for end-users to use to decide if something is callable - e.g. globalThis.isCallable()).
This means these new callable objects may not work well in a number of existing libraries - e.g. I took a quick look at Lodash's _.find() as just an example, and it looks like it'll use a typeof value === 'function' style of check to decide how your callable would behave when passed in. From the end-user's perspective, this isn't the worst thing, you just wrap it in an arrow function and it works (after you've debugged for a long time to figure out why it's not working). From a library maintainer's perspective, now you need to update all of your libraries to use isCallable() instead of typeof value === 'function' if you want your library to provide good support for the latest JavaScript.
I think it would be best if we stuck with only functions being callable. Maybe a shorthand syntax could be provided so we can make functions with additional properties, on the fly, but then again we already have the following:
"x is a function" doesn't mean it can't throw an error unconditionally. Any function can be implemented that way. However, you can proxy its apply method and make it not throw.
Classes are callable (they still have the [[Call]] internal method), they just throw as soon as you call them. But as a callable you are able to trap apply in a Proxy and prevent calls from throwing. You can't do this with non-callables.
let MyClass = class MyClass {}
MyClass = new Proxy(MyClass, {
apply(target, thisArg, argumentsList){
return new target(...argumentsList)
}
})
console.log(new MyClass()) // MyClass {}
console.log(MyClass()) // MyClass {}
Generator functions are also callable, though the generator (non-function) objects they return when called are not.