Open jmdyck opened 2 years ago
I’d say a method is any function-valued property that pays attention to its receiver, whether static or instance.
Could you rephrase that in terms that the spec defines?
Which term there does the spec not define? receiver is “this value”, static means “on a constructor”, instance means “on a prototype object”.
Which term there does the spec not define?
It doesn't define:
receiver is “this value”, static means “on a constructor”, instance means “on a prototype object”.
Okay, so why not use those replacements/expansions instead?
Sure:
A method is a function stored in any object property whose behavior changes based on its this
value.
Much better.
The "stored in" phrase is unusual for the spec. The reader would probably understand that the function in question can be the [[Value]]
of a data property, but it's unclear if it can be the [[Get]]
or [[Set]]
of an accessor property.
In practice, it looks like the spec never refers to accessor functions as "methods". (And that's not due to a #2592 normalization, it goes way back.) I'm guessing that's intentional rather than an oversight, so I'd suggest changing:
a function stored in any object property
to something like:
a function that is the value of a data property
Also, I'd suggest replacing:
behavior changes based on its
this
value
to:
behavior depends on its
this
value
(My thinking is that "behavior" refers to the totality of a function's input/output effect, which doesn't change.)
So that would be:
[A method is a] function that is the value of a data property whose behavior depends on its this
value
(Either way, it's a bit odd that the "whose" seems to attach to "property", whereas it actually attaches to "function". But I don't think that requires fixing.)
Sounds good; your corrections still read clearly to me!
I think you're overcomplicating things. If a child asks their parent what 'rain' is, the parent may say that it's 'fluid water that falls from the clouds'. While rain is water, an important aspect is the relationship between that water and the clouds. Whether a function depends on its receiver is irrelevant when speaking about methods. I remember that in the past you wouldn't be able to call console.log
without the console
receiver. This changed in the mean time, but the effect of the log
function printing something to the console is the same. We would still say that it's a method of the console
object because of the way we access it, meaning because of its relationship with object console
. The relationship with objects is not intrinsic to the log
function so it's not always relevant, we may put the log
function it in a variable and refer to it as the 'log function'. But we speak of 'methods' when we consider them in certain relations to objects, for instance when we access the log
function through a [[Get]] access on the console
object.
Regarding accessor properties, I think that's also irrelevant, because from a property relationship perspective the distinction is only relevant in terms of the property attributes. In the context of methods (when we consider the relationship between objects and functions), data and accessor properties are the same with respect to the result of the [[Get]]
, [[HasProperty]]
and [[OwnPropertyKeys]]
operations. Going into more details and distinguishing between properties based on [[GetOwnProperty]]
, for instance, only overcomplicates things. I like how simply the spec currently defines methods and I think the best would be to leave it as is.
In the console example, console.log isn’t a method anymore - but it used to be. Now it’s just a function in a namespace.
Wondering if it'd complicate definitions to refer to things like console.log
as a bound method? When you consider Node.js where you can create multiple Console
instances, log
is clearly a method related to that specific console instance, but it's usable as a function (without a receiver) because it's bound.
In the console example, console.log isn’t a method anymore - but it used to be. Now it’s just a function in a namespace.
You're using your own discretionary definition of the word 'method' in contradiction with the spec.
@raulsebastianmihaila the spec's definition isn't what's actually important tho - the colloquial one is, and it largely matches the definition I've provided.
One traditional and very familiar to experienced JS programmers is this style of creating objects:
function Car() {
let speed = 0;
let accelerationRate = 5;
return {
get speed() {
return speed;
},
accelerate() {
speed += accelerationRate;
},
decelerate() {
speed -= accelerationRate;
}
};
}
const car = new Car();
car.accelerate();
car.accelerate();
car.accelerate();
car.decelerate();
console.log(car.speed);
Colloquially, and also officially, accelerate
and decelerate
are methods and they don't use any receiver.
To be fair tho, that module pattern was popular so long ago that there is an entire cohort of very experienced JS developers that have never used it.
In this case I'd actually say they aren't methods - specifically, they can be destructured off and called bare, and they'll still work. They're just pretending to be methods, but they're really just namespaced functions.
That's very relative, for instance I wouldn't be surprised if those very experienced JS developers you speak of have never opened the spec. Anyway, would you agree that when that pattern was popular, accelerate
and decelerate
were considered methods and since then you decided on your own to change the meaning of the word 'method'? If you take a look at some JS books from that 'era' (because, you see, it was so long ago...) you would also find in that pattern the notion of 'private methods', meaning functions defined in the context of that constructor which are not exposed publicly. So, be sure you're contradicting existing books on JS patterns.
As to destructuring, there's an obvious reason not to do that:
const car1 = new Car();
const car2 = new Car();
car1.accelerate();
car2.accelerate();
is much better than
const {accelerate: accelerate1} = new Car();
const {accelerate: accelerate2} = new Car();
accelerate1();
accelerate2();
Sure. That doesn't mean the spec needs to adhere to a definition that includes an obsolete practice, though.
I would consider accelerate
and decelerate
to be bound methods (by way of the object as closure pattern). They apply to the car
instance but can be used as free functions.
And I wouldn't consider that pattern as obsolete (however we prefer to name these functions "makers", and avoid calling them with new
)
That doesn't mean the spec needs to adhere to a definition that includes an obsolete practice
I don't agree that it's obsolete. It has advantages over post-ES5 classes for instance:
we prefer to name these functions "makers", and avoid calling them with
new
Is this the position of TC39?
However, your "methods" aren't shared between instances - you have N copies of each function when you have N instances. That pattern was a hack around not having a good declarative way to create a constructor and prototype - class
exists now, which makes it (7+ years now) obsolete even if it still has advantages.
Is this the position of TC39?
Sorry for the confusion, no. This is our coding style at Agoric.
you have N copies of each function when you have N instances
You can say it's not a perfect pattern, however nowadays people go even further and they create N copies of functions with React hooks on each rerender. I think the sense in which you use the word 'obsolete' is more like 'not trendy', which I think is a strange way of looking at things, given that the pattern has important semantic advantages.
is a property containing a function
4.4.37 defines "method" as "function that is the value of a property", which includes just about every intrinsic function. So that definition should probably change.
Originally posted by @jmdyck in https://github.com/tc39/ecma262/issues/2576#issuecomment-968193862
We could say "function that is the value of a property of a prototype object". That wouldn't handle
[Typed]Array.{from,of}
, which don't satisfy that definition, but are described as methods. Still, it's pretty close.