Open hax opened 4 years ago
f.call()
has an undefined
receiver. I'm confused why f()
would be "banned" in any case?
@ljharb Yes f.call()
has an undefined
receiver but I think f.call
, f.apply
or f.bind
are some degree reflection APIs like Reflect.apply(f, undefined)
, programmers only use them for specific reasons, most time you use f.call
because you want to send a specific this
argument, for example, if we supported function f(this = value)
you may want to use f.call(undefined)
to use the default value, but it's the consequence not the reason. So I don't treat them as important use cases.
why
f()
would be "banned" in any case
This is proposed by @gilbert : "If a function has explicitly named its this parameter, it could be useful to throw an error when that function gets called without one" . I believe the intention is to avoid common mistakes of invoking methods without receiver.
Personally, I find call/apply/bind use cases much more critical to support than any of the edge cases listed in the readme.
If the intention is to avoid that mistake, then it seems that providing a default value would be defeating that goal entirely.
I find call/apply/bind use cases much more critical to support than any of the edge cases listed in the readme.
@ljharb What edge cases? There is no section named "edge cases" in the readme of this proposal :-P
If the intention is to avoid that mistake, then it seems that providing a default value would be defeating that goal entirely.
Not sure what you mean. Do you mean programmers can provide default value so that f()
could just work?
It's possible, but I believe in most cases there is no proper default value programmers can provide. Actually most builtin methods would throw if do not provide receiver, for example (0, Promise.resolve)(1)
would throw. On the other side, the history of non-strict functions (which use globalThis as default value) tell us default value for this
is very likely a bad idea ^_^
You’re right; i confused it with your other proposal, my apologies.
I mean that by providing a default value for this
, the programmer has explicitly said that they don't want an undefined receiver to throw.
the programmer has explicitly said that they don't want an undefined receiver to throw.
So
function f1(this) {}
function f2(this = foo) {}
f1() // throw
f2() // not throw
Seems possible! Though I hope we could find some good use cases for providing default value for this
parameter.
And we also need to figure out what should happen on non-strict functions.
Sloppy functions can’t ever see undefined/null as the receiver, so i wouldn’t expect that to change - a default value for the receiver in a sloppy function would never trigger.
If never trigger, we'd better forbid it in syntax layer?
Since you can't write
function f(x = foo) {
'use strict' // <-- syntax error
}
Which means when parser see function f(this =
, it already know whether f
is strict or non-strict, so it could report syntax error for non-strict.
Sloppy functions can’t ever see undefined/null as the receiver, so i wouldn’t expect that to change
@ljharb I ask the question because there are two possible ways for function nonStrict(this = foo()) {}
:
let o = Object(this ?? globalThis)
o = o === undefined ? foo() : o // never trigger
this = o
or
let v = this === undefined ? foo() : this
this = Object(v ?? globalThis)
I think you expect the first one, which never trigger default initializer.
I'm not sure whether people would expect the second.
Yes, I’d expect the first one. People who want the second’s behavior can make their function strict, just like now.
FYI, C# ban the default value for this
parameter
Currently, TS also do not support default value for this
parameter.
(Java don't support default value for all parameters at all.)
I don't see much use cases of
function f(this = value) {}
in the wild, because it's impossible to invokeundefined.foo()
anyway, and we already banf()
if f is using explicit this.The only possible use case is
foo::bar()
whenfoo
could beundefined
, but I feel it bring confusion more than usefulness, and as previous discussion in bind/pipeline operators, it's likely we may wantfoo?::bar()
forundefined
/null
cases (like?.
) in the future.There are also some other issues:
this = value
is always invalid in all places, allow it in parameter may cause confusion.function f(this = this == null ? globalThis : this)
. So what's the accurate semantic offunction f(this = value) {}
iff
is non-strict? It's a historical debt, theoretically we could revise the semantic for explicit this functions (will create separate issue for that), but I found all options have some weirdness in some way.My current preference is do not support default value initializers for
this
parameter.