5 ms·
Maybe it's just me, but [Symbol.dispose]() seems like a really hacky way to add that functionality to an Object. Here's their example: using readerResource
by russellbeattie 1y ago
Maybe it's just me, but [Symbol.dispose]() seems like a really hacky way to add that functionality to an Object. Here's their example:
using readerResource = {
reader: response.body.getReader(),
[Symbol.dispose]() {
this.reader.releaseLock();
},
};
First, I had to refresh my memory on the new object definition shorthand: In short, you can use a variable or expression to define a key name by using brackets, like: let key = "foo"; { [key]: "bar"}, and secondly you don't have to write { "baz" : function(p) { ... } }, you can instead write { baz(p) {...} }. OK, got it.
So, if I'm looking at the above example correctly, they're implementing what is essentially an Interface-based definition of a new "resource" object. (If it walks like a duck, and quacks...)
To make a "resource", you'll tack on a new magical method to your POJO, identified not with a standard name (like Object.constructor() or Object.__proto__), but with a name that is a result of whatever "Symbol.dispose" evaluates to. Thus the above definition of { [Symbol.dispose]() {...} }, which apparently the "using" keyword will call when the object goes out of scope.
Do I understand that all correctly?
I'd think the proper JavaScript way to do this would be to either make a new object specific modifier keyword like the way getters and setters work, or to create a new global object named "Resource" which has the needed method prototypes that can be overwritten.
Using Symbol is just weird. Disposing a resource has nothing to do with Symbol's core purpose of creating unique identifiers. Plus it looks fugly and is definitely confusing.
Is there another example of an arbitrary method name being called by a keyword? It's not a function parameter like async/await uses to return a Promise, it's just a random method tacked on to an Object using a Symbol to define the name of it. Weird!
Maybe I'm missing something.
- paavohtl 1y agoJS has used "well-known symbols"[1] to allow extending / overriding the functionality of objects for about 10 years. For example, an object is an iterable if it has a `[Symbol.iterator]` property. Symbols are valid object keys; they are not just string aliases. [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#well-known_symbols https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- demarq 1y agoYes you are missing something. You are not supposed to call these methods, they are for the runtime. more specifically, javascript will call the [Symbol.dispose] when it detects you are exiting the scope of a "using" declaration.
- uasi 1y ago> create a new global object named "Resource" which has the needed method prototypes that can be overwritten. those methods could conflict with existing methods already used in other ways if you’d want to make an existing class a subclass of Resource.
- deleted 1y ago[deleted]
- Spivak 1y agoI hadn't considered how blessed I was to have __enter__ / __exit__ in Python for context managers and the more general Protocol concept that can be used for anything because lordy that definition looks ugly as sin. Even Perl looks on in horror of the sins JS has committed.
- russellbeattie 1y ago> Even Perl looks on in horror of the sins JS has committed. LOL. What a great line. It's getting pretty ridiculous, I agree. I don't understand the need for so many shorthands for one. All they do is make code illegible for the sake of saving a few keystrokes. Using the Symbol object that way is just ugly.
- masklinn 1y ago> To make a "resource", you'll tack on a new magical method to your POJO, identified not with a standard name [...] nothing to do with Symbol's core purpose of creating unique identifiers. The core purpose and original reason why Symbol was introduced in JS is the ability to create non-conflicting but well known / standard names, because the language had originally reserved no namespace for such and thus there was no way to know any name would be available (and not already monkey patched onto existing types, including native types). > Is there another example of an arbitrary method name being called by a keyword? It's not a function parameter like async/await uses to return a Promise, it's just a random method tacked on to an Object using a Symbol to define the name of it. Weird! `Symbol.iterator` called by `for...of` is literally the original use case for symbols. > I'd think the proper JavaScript way to do this would be to either make a new object specific modifier keyword like the way getters and setters work, or to create a new global object named "Resource" which has the needed method prototypes that can be overwritten. Genuinely: what are you talking about.
- russellbeattie 1y ago> Genuinely: what are you talking about. They added the get and set keywords to plain JS objects to identify getters and setters. So just add a dispose keyword. Like this: const obj = { log: ["a", "b", "c"], get first() { return this.log[0]; }, dispose cleanup() { // close resource } }; Much cleaner.
- masklinn 1y agoRight, so instead of just grabbing a property by its well know unique name aka the entire point of symbols you need to first resolve the property descriptor, check a new completely unnecessary flag, and only then can you call the method. And of course that breaks if anyone either forgets the attribute or fails to check it when calling the parent method. And you’re now wasting an entire keyword on a property with a fixed name, and code bases which already use that name with different semantics are unable to add `using` compatibility.
- rafram 1y ago> identified not with a standard name (like Object.constructor() or Object.__proto__) __proto__ was a terrible mistake. Google “prototype pollution”; there are too many examples to link. In a duck-typed language where the main mechanism for data deserialization is JSON.parse(), you can’t trust the value of any plain string key.
- jitl 1y agoSymbols are a very safe way to introduce new "protocols" in either the language standard or for application code. This is because Symbol can never conflict with existing class definitions. If we use a string name for the method, then existing code semantics change. Here are the well-known symbols that my NodeJS 22 offers when I `Symbol.<tab>`: Symbol.asyncDispose Symbol.asyncIterator Symbol.dispose Symbol.hasInstance Symbol.isConcatSpreadable Symbol.iterator Symbol.keyFor Symbol.length Symbol.match Symbol.matchAll Symbol.replace Symbol.search Symbol.species Symbol.split Symbol.toPrimitive Symbol.toStringTag Symbol.unscopables