Small Proposal "!in"
On Thu, Jun 28, 2018 at 5:14 PM, Tobias Buschor <tobias.buschor at shwups.ch>
wrote:
I dont like to write: if ( !('x' in obj) && !('y' in obj) ) { doit() }
I was even tempted to write it that way: if ('x' in obj || 'y' in obj) { } else { doit() }
There's
if (!('x' in obj || 'y' in obj)) {
doit()
}
That said, I've wanted !in many a time, in a minor sort of way...
-- T.J. Crowder
I agree, it's very annoying to have to write it !(x in y). I've been wanting this operator for a very, very long time.
If there is interest for !in, I think !instanceof deserves to be included too.
Le jeu. 28 juin 2018 à 18:19, T.J. Crowder <tj.crowder at farsightsoftware.com>
a écrit :
if (!obj.x && !obj.y) {
doit()
}
The cases where they are equal to 0, '', null, undefined shouldn't matter
imo, if for example those x and y are numbers, they would be defined,
defaulted to 0, and you would test for !== 0
rather if needed
Le jeu. 28 juin 2018 à 18:31, Guylian Cox <guyliancox at gmail.com> a écrit :
+1 from me for !in. It's a surprisingly common nuisance.
And I don't care for the !obj.x workaround at all -- even if you can survive the difference in semantics, from a code reading point of view this is saying something entirely different.
And it is very different semantically. 'x' in obj does [[HasProperty]]; obj.x does [[GetProperty]]. With
obj = { get x() { print("getter"); return 3; } };
then |"x" in obj| does not print "getter" while |obj.x| does.
I don't use in
.
What do I get with 'x' !in obj
or !(x in obj)
that I don't get with
!obj['x'] ?
Genuinely asking - I don't know what I am missing.
I use obj[x] because I believe it's a more familiar syntax and I believe I get the same outcome..(?)..
!obj.x
will return true for any falsy property - null, undefined,
positive or negative zero, NaN, and the empty string. !(x in obj)
will
return true only if x
is not an own property on obj
nor anything in its
prototype chain. They are decidedly different tests and they check for
decidedly different things.
Good point. I've never needed falsey valid values in my obj/maps yet, so
it'll continue to be very rare that I will need to use in
(and I'll
continue to prefer !obj.x when I don't).
However, for this proposal I would prefer a new keyword like notin
instead of !in
, because the !
followed by a word normally indicates the
inverse boolean of a value in other cases, whereas a clean break like
notin
I think seems clearer. Reserved keyword would probably be needed
though?
-1
this feature mostly cross-cuts "!obj.hasOwnProperty('foo')". the only semantic-difference is "in" will check prototype-chain as jordan pointed out (and widely regarded as a bad design-pattern), while "hasOwnProperty" [correctly] will not. in every real-world use-case i've encountered, hasOwnProperty is what you actually want instead of in. and for looping, "Object.keys(obj).forEach(...)" is usually faster than "for (key in obj) {...}" (partly because of again, the bad-design prototype-chain lookup inherent with "in").
also, imagine the cognitive-load placed on yourself when debugging integration-javascript with a mix of these cross-cutting design-patterns:
// can you easily spot where the subtle integration-bug is
// when dealing with code has a mashup of
// in and hasOwnProperty?
if ('foo' !in obj) {
...
}
...
if (!obj.hasOwnProperty('bar') {
....
}
in summary for those who like to adhere to the python/jslint philosopy of "there should be one and preferably only one way to do any given task":
- for property-checks, "hasOwnProperty" is generally the one-and-only correct-way to do it.
- for looping over properties, "Object.keys(obj).forEach(...)" is generally the one-and-only correct-way to do it.
following the above 2 guidelines will save you alot of needless cognitive-load when doing integration-debugging of web-projects.
Mixing the semantics of a unary operator with a binary operator seems... not ideal.
Good point.
and, as previously mentioned, !obj.x
might have side effects through the
accessor, as example in every lazily defined property that would be early
defined (or anything else behind a getter that could do more than just
telling the property is there and it's not truthy).
I have mixed feelings about !in
and I also wonder if something like
const not = $ => !$;
would make checks look nicer: if (not('x' in obj)) { ... }
but that's not really a syntax shortcut, isn't it? Probably a ni
would be
better, as new not in relational operator? I wouldn't like notIn
or
notin
much, but I might use it anyway.
---------- Forwarded message ---------- From: Andrea Giammarchi <andrea.giammarchi at gmail.com> To: Jordan Harband <ljharb at gmail.com> Cc: "es-discuss at mozilla.org" <es-discuss at mozilla.org> Bcc: Date: Wed, 11 Jul 2018 16:23:03 +0200 Subject: Re: Small Proposal "!in" and, as previously mentioned,
!obj.x
might have side effects through the accessor, as example in every lazily defined property that would be early defined (or anything else behind a getter that could do more than just telling the property is there and it's not truthy).
Peanut gallery observation: I personally think !in is a Really Bad Idea, or at least I'm not convinced that it's particularly useful. If it is useful, then let one of the transpiling languages like CoffeeScript or TypeScript demonstrate it first.
Also, there's the little matter of pronunciation. I admit to a bit of snarkiness when I first saw this proposal, but I didn't expect it to have any traction. So I'll just say it: do we really want JavaScript to be a "bangin' " language?
Alex
In Linux programming, #!/bin/bash is very commonly the first line of a shell script. In Python, we have #!/bin/python, or #!/bin/perl. The #! characters are pronounced "she-bang", because # is pronounced "hash" and ! is pronounced "bang". en.wikipedia.org/wiki/Shebang_(Unix)
Let me compound on that: Kotlin has a !in
for "not in collection" and
!is
for "not an instance of". I've been using that language quite a bit
lately out of necessity (better than Java, and all the other alternatives
suck in some way), and those two operators just feel wrong to type. It's
to the point I'm sometimes coding my way out of writing them. (No other
feature uses this symbol except for literal logical negation.)
Oh, and as I mentioned before, key in object
checks the prototype chain,
which in my experience is not the thing you usually want to do.
So I agree this is a Really Bad Idea™, for this above if not the Kotlin reference.
On Wed, Jul 11, 2018 at 4:51 PM, Alex Vincent <ajvincent at gmail.com> wrote:
In Linux programming, #!/bin/bash is very commonly the first line of a
shell
script. In Python, we have #!/bin/python, or #!/bin/perl. The #!
characters
are pronounced "she-bang", because # is pronounced "hash" and ! is pronounced "bang".
Indeed. But in JavaScript (and Java and C and C# and...), !
is "not", and
I for one read it that way when reading anything other than bash script. I
don't read !==
as "bang equal," either. (Which in British English would
be really misleading...)
And that's why I like the idea of !in
(or notin
), because I'd much
rather read and write "if property not in object" than "if not property in
object" (and don't get me started on the parens required to make it work
:-) ).
To me, !in
or notin
or whatever you want makes perfect sense, just like
we have !=
and !==
. It's like not having !==
and then arguing we
don't need to have it because after all, you can write n != 1
as !(n == 1)
.
Heck, all we really need is NAND gates, right? ;-)
On Wed, Jul 11, 2018 at 6:33 PM, Isiah Meadows <isiahmeadows at gmail.com>
wrote:
So I agree this is a Really Bad Idea™, for this above if not the Kotlin reference.
I strongly disagree it's a Really Bad Idea™. I think it's a Moderately Good
Idea On The Whole™. And !in
is fine by me but I'm also happy with notin
or whatever.
I just don't expect it to happen. Too much inertia to add it after-the-fact, and too many bigger fish to fry. :-)
-- T.J. Crowder
On Wed, Jul 11, 2018 at 1:51 PM T.J. Crowder < tj.crowder at farsightsoftware.com> wrote:
On Wed, Jul 11, 2018 at 4:51 PM, Alex Vincent <ajvincent at gmail.com> wrote:
In Linux programming, #!/bin/bash is very commonly the first line of a shell script. In Python, we have #!/bin/python, or #!/bin/perl. The #! characters are pronounced "she-bang", because # is pronounced "hash" and ! is pronounced "bang".
Indeed. But in JavaScript (and Java and C and C# and...),
!
is "not", and I for one read it that way when reading anything other than bash script. I don't read!==
as "bang equal," either. (Which in British English would be really misleading...)And that's why I like the idea of
!in
(ornotin
), because I'd much rather read and write "if property not in object" than "if not property in object" (and don't get me started on the parens required to make it work :-) ).To me,
!in
ornotin
or whatever you want makes perfect sense, just like we have!=
and!==
. It's like not having!==
and then arguing we don't need to have it because after all, you can writen != 1
as!(n == 1)
.
Heck, all we really need is NAND gates, right? ;-)
On Wed, Jul 11, 2018 at 6:33 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
So I agree this is a Really Bad Idea™, for this above if not the Kotlin reference.
I strongly disagree it's a Really Bad Idea™. I think it's a Moderately Good Idea On The Whole™. And
!in
is fine by me but I'm also happy withnotin
or whatever.
I agree with all your arguments about why it's more ergonomic, but I keep
coming back to what Naveen said: "I don't use in
."
I think in
is the wrong operator to make more convenient, except as part
of a more general cleanup to provide inverted forms of boolean infix
operators.
Usually, where I see in
I would prefer that the author had used
hasOwnProperty.
And usually where I see (foo.hasOwnProperty(p)), I would prefer the author
wrote Reflect.apply(hasOwnProperty, foo, [p]).
What are the use cases for in
that would not be better served by an
ergonomic, infix hasOwnProperty?
I just don't expect it to happen. Too much inertia to add it
This may be a silly idea that doesn't come naturally to others, but the first thing I thought of was "!n", where the "!" represents a flipped "i", so the inverse of in.
Although, on the discussion of "notin" or "!in", the latter comes far more naturally to me as others have mentioned.
I think in
and instanceof
could both benefit from having negated
versions.
Assuming the developer is using in
correctly, hasOwnProperty concerns are
irrelevant. Either way they would attempt to use !(a in b), not
!hasOwnProperty.
Same reason we don't use... !(a == b) // a != b !(a === b) // a !== b !(a > b) // a <= b (!(a > b) && !(a == b)) // a < b
On Wed, Jul 18, 2018 at 11:05 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote:
I think
in
andinstanceof
could both benefit from having negated versions.Assuming the developer is using
in
correctly, hasOwnProperty concerns are irrelevant. Either way they would attempt to use !(a in b), not !hasOwnProperty.
Why should we assume the developer is using in
correctly?
Apologies if I buried my question at the end. It was, what are the use
cases for in
that would not be better served by an ergonomic, infix
hasOwnProperty?
Same reason we don't use...
!(a == b) // a != b !(a === b) // a !== b
!(a > b) // a <= b (!(a > b) && !(a == b)) // a < b
I'm not sure this is relevant to your larger point, and I've already conceded ergonomics, but these last two are not equivalent because NaN is weird.
a = NaN, b = 0 [!(a > b), a <= b] // [true, false] [!(a > b) && !(a == b), a < b] // [true, false]
[sorry dropped group]
On Fri, Jul 13, 2018 at 11:36 AM <alexander at debenclipper.com> wrote:
This may be a silly idea that doesn't come naturally to others, but the first thing I thought of was "!n", where the "!" represents a flipped "i", so the inverse of in.
You'd need yet another restricted production which would make infix in
unrestricted
but the negation restricted.
x // no semicolon inserted in (obj)
x // semicolon inserted !n (obj)
Not a blocker, but worth keeping in mind. I also have to look hard at them to distinguish.
I think it is irrelevant; the operator already exists and I would assume if you want the negation of it you are using it correctly in the first place. Otherwise are you not just arguing for its removal altogether? But to answer your question one case that comes to mind is trapping get/has in a proxy handler.
On Wed, Jul 18, 2018 at 12:21 PM Michael Theriot < michael.lee.theriot at gmail.com> wrote:
I think it is irrelevant; the operator already exists and I would assume if you want the negation of it you are using it correctly in the first place. Otherwise are you not just arguing for its removal altogether? But to answer your question one case that comes to mind is trapping get/has in a proxy handler.
Why should we assume that only people who consistently use in
correctly
would want the negation? It seems that people who use it incorrectly
because they are confused about the precise semantics or don't care might
want the negation too. If there are more of the latter then we should not
assume what you assume.
Proxy handler code is important, but very few developers will ever write a proxy handler over their careers, so this seems like a marginal use case. Besides, Reflect.has is probably a better choice in a proxy handler.
I am not arguing for removing in
. That would break the web. I am just
arguing for prioritizing changes that provide features that more closely
match the semantics developers typically want over making it more
convenient to write code that seems to work in casual testing but has
subtly wrong semantics.
Although I support the idea of !in
(for the same reasons as T.J. Crowder
mentioned, plus it's useful for duck typing), what some browsers do isn't a
great argument as modern browsers follow the spec more closely with
to inherited accessors like this and you'd never be able to use !in
for
an older browser. However, you do have a point in that accessors can live
anywhere in the prototype chain and an object's properties may not
necessarily be 'owned' by the object in question:
document.createElement('input').hasOwnProperty('type')
// false
So far, the main argument against has been that hasOwnProperty()
is
recommended over in
as a more robust check. I don't see how this is a
valid concern, they clearly have different use cases and you're not going
to solve that problem be excluding a negated in
from the spec (!(a in b)
is still easier to type than !b.hasOwnProperty(a)
). There are plenty
of valid use cases for in
, not least duck typing as mentioned before:
function feed(duck) {
if ('quackPitch' in duck && duck.canSwim) {
duck.give(defrostedPeas);
}
}
!in
and !instanceof
would be great additions to the operator sets.
On Thu, Jul 19, 2018 at 7:32 AM Andy Earnshaw <andyearnshaw at gmail.com>
wrote:
Although I support the idea of
!in
(for the same reasons as T.J. Crowder mentioned, plus it's useful for duck typing), what some browsers do isn't a great argument as modern browsers follow the spec more closely with to inherited accessors like this and you'd never be able to use!in
for an older browser. However, you do have a point in that accessors can live anywhere in the prototype chain and an object's properties may not necessarily be 'owned' by the object in question:document.createElement('input').hasOwnProperty('type') // false
'string' === typeof document.createElement('input').type // true
So far, the main argument against has been that
hasOwnProperty()
is recommended overin
as a more robust check. I don't see how this is a valid concern, they clearly have different use cases and you're not going to solve that problem be excluding a negatedin
from the spec (!(a in b)
is still easier to type than!b.hasOwnProperty(a)
). There are plenty of valid use cases forin
, not least duck typing as mentioned before:
Agree re "not solving that problem by" When I'm duck typing, I typically am testing typeof or truthiness of a property, not just presence.
function feed(duck) {
if ('quackPitch' in duck && duck.canSwim) { duck.give(defrostedPeas); } }
This code is brittle. An attacker may be able to deny service by getting
{ "quackPitch": true, "canSwim": true }
to a JSON decoder, but would not
be able to trigger an exception if you tested with typeof instead of in.
function robustFeed(duck) { if (duck.canSwim && typeof duck.give === 'function') { duck.give(defrostedPeas); } }
in
provides a weak, confusable kind of duck typing.
JSON decoding attacks allow forging objects that satisfy in
predicates,
encouraging conflating
objects from an untrusted source with objects from trusted code.
These forgeries often would not pass stronger predicates that test for the
typeof required properties.
!in
and!instanceof
would be great additions to the operator sets.
Agree re !instanceof. I'm still not seeing where developers do and should
use in
or !in
on a regular basis.
'string' === typeof document.createElement('input').type // true
It should be noted this is a "loose check"; it does not determine whether
or not the property exists when its value equals undefined. It also
triggers getters, whereas in
reports whether or not the property exists
without triggering a getter.
On Thu, Jul 19, 2018 at 12:32 PM, Andy Earnshaw <andyearnshaw at gmail.com> wrote:
Although I support the idea of
!in
(for the same reasons as T.J. Crowder mentioned, plus it's useful for duck typing), what some browsers do isn't
a
great argument as modern browsers follow the spec more closely with
to inherited accessors like this and you'd never be able to use
!in
for
an
older browser.
Well, there's transpiling.
!in
and!instanceof
would be great additions to the operator sets.
AND, agreeing with Mike Samuel, an ergonomic operator for
hasOwnProperty
(that
doesn't have the issues of being overridden, not being inherited because Object.prototype
isn't in the object's prototype chain, etc.) would be a
great addition. I don't see any reason they couldn't all be part of the
same proposal as they touch the same parts of the spec and implementations.
And agree that the own property ones would be the more useful ones, but
there are use cases for both.
The immediate temptation is hasOwn
and !hasOwn
. My only concern is that
their operands would be in the opposite order to in
and !in
:
const o = Object.create({a: 1});
o.b = 2;
console.log("a" in o); // true
console.log(o hasOwn "a"); // false
console.log("b" in o); // true
console.log(o hasOwn "b"); // true
For me, hasOwn
with the different operand order isn't a problem, but
others may take a different view. Trying to keep the same order takes us
down a route like inOwn
which I can't say I care for.
But I wonder if all of this discussion is useful. If someone did take the time to work out the naming and do a proper draft proposal, perhaps a Babel plugin, is there really any likelihood of someone championing it? I'd see these contents:
name !in obj
,name notin obj
, or whatever it gets calledobj hasOwn name
,name inOwn obj
, or whatever it gets calledobj !hasOwn name
,name !inOwn obj
, or whatever it gets calledReflect.hasOwn
, the "own" version ofReflect.has
Proxy
trap forhasOwn
(even if the operator has a different name)
Is there anyone appropriately-placed who can say "Yeah, if someone spends the time to investigate this and put together a really proper proposal, I'm willing to look at that proposal with an eye toward possibly championing it." I mean, if there's a 10% chance or better, it may be worth someone's time. If there isn't, then...
-- T.J. Crowder
On Thu, Jul 19, 2018 at 9:26 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote:
'string' === typeof document.createElement('input').type // true
It should be noted this is a "loose check"; it does not determine whether or not the property exists when its value equals undefined. It also triggers getters, whereas
in
reports whether or not the property exists without triggering a getter.
Good point. in
does trigger "has" proxy handlers though, so neither is
side-effect free.
On Thu, Jul 19, 2018 at 2:44 PM, T.J. Crowder < tj.crowder at farsightsoftware.com> wrote:
Is there anyone appropriately-placed who can say "Yeah, if someone spends the time to investigate this and put together a really proper proposal,
I'm
willing to look at that proposal with an eye toward possibly championing it."
Apologies, Mike, I just realized you're a potential champion. Just in case you read that as seeming a bit off... :-)
-- T.J. Crowder
It was a contrived example I threw together to make a point in 30 seconds. It may surprise you to know that it is not real world code. You talk about an "attacker" as if any code working in such a way is sensitive, but that is often not the case and leaving your functions intentionally generic allows third parties more room when working with it.
'string' === typeof document.createElement('input').type // true
Again, you're missing the point, which is how that is one example of an object that doesn't 'own' all of its properties. As already mentioned, it also triggers a getter.
Let's not also forget that not all code has to adhere to best practices. I
use the in
operator far more in the REPL/console or when I'm prototyping
than I do when I'm performing robust checks in code.
I still prefer truthy checks when logically valid. Otherwise a truthy
check + [any other validity check in the logic]. I find in
far too quirky
and I'd rather never ever use it.
I think instanceof
is also a messy pattern. I've always preferred having
an overridden method on the object that performs the necessary logic for
that type, instead of if(x instanceof Y)... I find overridden method based
code shorter at the point of use, and scales to many instances /
permutations for type based logic.
Therefore, I'd rather TC39 let these concepts like in
and instanceof
die quietly, and always focus on the more/most powerful ways of getting
things done first.
On Thu, 19 Jul 2018 at 14:44 T.J. Crowder <tj.crowder at farsightsoftware.com>
wrote:
On Thu, Jul 19, 2018 at 12:32 PM, Andy Earnshaw <andyearnshaw at gmail.com> wrote:
Although I support the idea of
!in
(for the same reasons as T.J. Crowder mentioned, plus it's useful for duck typing), what some browsers do isn't a great argument as modern browsers follow the spec more closely withto inherited accessors like this and you'd never be able to use
!in
for an older browser.Well, there's transpiling.
Good point.
!in
and!instanceof
would be great additions to the operator sets.AND, agreeing with Mike Samuel, an ergonomic operator for
hasOwnProperty
(that doesn't have the issues of being overridden, not being inherited becauseObject.prototype
isn't in the object's prototype chain, etc.) would be a great addition. I don't see any reason they couldn't all be part of the same proposal as they touch the same parts of the spec and implementations. And agree that the own property ones would be the more useful ones, but there are use cases for both.The immediate temptation is
hasOwn
and!hasOwn
. My only concern is that their operands would be in the opposite order toin
and!in
:
I'm not against this at all. I just think the arguments against !in
aren't really arguments against it. Adding !hasOwn
would give even more
weight to an !in
proposal, I think.
For me,
hasOwn
with the different operand order isn't a problem, but others may take a different view. Trying to keep the same order takes us down a route likeinOwn
which I can't say I care for.
Nor me. I would argue for on
('a' on b
), but that is a huge typo
footgun (especially for Colemak users) and maybe isn't clear enough about
its semantics. I would argue that operators aren't camel cased in JS
though, so hasown
/inown
.
But I wonder if all of this discussion is useful. If someone did take the time to work out the naming and do a proper draft proposal, perhaps a Babel plugin, is there really any likelihood of someone championing it? I'd see these contents:
name !in obj
,name notin obj
, or whatever it gets calledobj hasOwn name
,name inOwn obj
, or whatever it gets calledobj !hasOwn name
,name !inOwn obj
, or whatever it gets calledReflect.hasOwn
, the "own" version ofReflect.has
Proxy
trap forhasOwn
(even if the operator has a different name)Is there anyone appropriately-placed who can say "Yeah, if someone spends the time to investigate this and put together a really proper proposal, I'm willing to look at that proposal with an eye toward possibly championing it." I mean, if there's a 10% chance or better, it may be worth someone's time. If there isn't, then...
!in
and !instanceof
are low hanging fruit (run the same steps as their
counterparts but flip the result), better off in their own proposal. I
don't know if you'd find a champion to do it, but I do think a lot of
people would find them convenient.
On Thu, 19 Jul 2018 at 14:48 Mike Samuel <mikesamuel at gmail.com> wrote:
On Thu, Jul 19, 2018 at 9:26 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote:
'string' === typeof document.createElement('input').type // true
It should be noted this is a "loose check"; it does not determine whether or not the property exists when its value equals undefined. It also triggers getters, whereas
in
reports whether or not the property exists without triggering a getter.Good point.
in
does trigger "has" proxy handlers though, so neither is side-effect free.
Except I'd argue this is a desirable side effect in the case of in
and
less so in the case of a getter. Getters are more likely to be an
intensive operation (for example, innerHTML) than a proxy has
operation
generally overriding the expected behaviour of the target object.
In most of the use cases of checking if a property is in the prototype chain (checking a protocol for example) you do want to trigger getters and Proxy traps. When interfacing with a object a developer doesn't need to be concerned with the implementation behind, if a getter or proxy trap does a intensive computation or has side effects, it's all bad class/object design fault. A common example when you want to check a property "return" rather than it's existence it's virtual properties or class refactoring:
// old implementation
class Foo {
constructor() {
this.foo = 46;
}
}
// post refactoring
class Foo {
// implements something different
// Product of refactoring, undefined it's a placeholder here
//it can be any value that makes sense in the refactoring
get foo() { return undefined; }
set foo(_) {}
}
Of course there's some use cases to use property check without triggering side effects (mostly when working with property descriptors), but the majority of "property existence check" lays on interface assertion and a typeof check it's ~generally~ the right choice.
About the !in
operator, I don't see any problem in it. Of couse the usage
of in
is most of the time is not recommended, but it has it place. Also
it puts precedence to future operators (like the hasOwn
proposed here).
On Thu, Jul 19, 2018 at 10:40 AM Augusto Moura <augusto.borgesm at gmail.com>
wrote:
Of couse the usage of
in
is most of the time is not recommended, but it has it place.
What places does it have?
I remain unconvinced that in
has significant enough use cases to warrant
high-level ergonomics
were it being proposed today.
It exists, and it'll probably never be removed from the language, but I don't think it should be taught as a good part of the language, and linters should probably flag it.
For me,
hasOwn
with the different operand order isn't a problem, butothers may take a different view. Trying to keep the same order takes us down a route like
inOwn
which I can't say I care for.Nor me. I would argue for
on
('a' on b
), but that is a huge typo footgun (especially for Colemak users) and maybe isn't clear enough about its semantics. I would argue that operators aren't camel cased in JS though, sohasown
/inown
.
For what it's worth I was also thinking of an "on" operator when reading this message, so this is intuitive to me. I also think that is a separate idea to propose though.
Of couse the usage of in
is most of the time is not recommended, but it
has it place.
What places does it have? I remain unconvinced that
in
has significant enough use cases to warrant high-level ergonomics were it being proposed today.It exists, and it'll probably never be removed from the language, but I don't think it should be taught as a good part of the language, and linters should probably flag it.
Maybe a radical thought, but does this not apply to hasOwnProperty? If you have strong type management why test for a property? The one case I can think of is parsing JSON but I handle that with destructuring. Are there significant use cases for it? Should linters flag it?
On Thu, Jul 19, 2018 at 11:56 AM Michael Theriot < michael.lee.theriot at gmail.com> wrote:
For me,
hasOwn
with the different operand order isn't a problem, butothers may take a different view. Trying to keep the same order takes us down a route like
inOwn
which I can't say I care for.Nor me. I would argue for
on
('a' on b
), but that is a huge typo footgun (especially for Colemak users) and maybe isn't clear enough about its semantics. I would argue that operators aren't camel cased in JS though, sohasown
/inown
.For what it's worth I was also thinking of an "on" operator when reading this message, so this is intuitive to me. I also think that is a separate idea to propose though.
"on" is a good name. Agree on separate.
Of couse the usage of
in
is most of the time is not recommended, but ithas it place.
What places does it have? I remain unconvinced that
in
has significant enough use cases to warrant high-level ergonomics were it being proposed today.It exists, and it'll probably never be removed from the language, but I don't think it should be taught as a good part of the language, and linters should probably flag it.
Maybe a radical thought, but does this not apply to hasOwnProperty? If you have strong type management why test for a property? The one case I can think of is parsing JSON but I handle that with destructuring. Are there significant use cases for it? Should linters flag it?
Good questions. Linters should flag x.hasOwnProperty(y) since if you're unsure whether x has an own property y you're probably unsure whether x has an own property 'hasOwnProperty'.
IIRC, destructuring traverses prototypes so see caveat about library code and monkeypatching below. const { toString: f } = {} typeof f // 'funciton'
You're right that in the middle of an application an instance's type should determine whether a property is present. There are use cases at the periphery.
Use cases in bleeding edge JS include
- As you noted, operations on parsed JSON. Even if you use revivers to parse JSON to Maps instead of Objects you either need own property checks or Object.setPrototypeOf(parsed, null) when populating the Map.
- Non-clobbering mass assignment. Like Object.assign but that doesn't overwrite existing properties in the assignee.
- Reliable hole detection in arrays: (1 !on [0, , 2])
- Adapters that use naming conventions between JS objects and external systems, like between database field names and JS property names.
- Distinguishing between user-code-defined properties on a host object like an HTML element and readonly host properties.
Many use cases for hasOwnProperty could be better supported by Map, Set, and Object.create(null) including:
- Objects used as lookup tables.
- Lookup tables with special purpose fallback logic like message bundles that fall back from locale 'aa-BB' to 'aa'.
- Configuration objects.
- Function options bundles.
but idiomatic JS hasn't caught up and some library code has to run on old
interpreters, so a transpiling
on
would be nice. The last two are mostly an issue in library code that needs to be resilient when loaded alongside monkeypatched prototypes.
Honorable mention:
- for(...in...) iteration broken solved by for(...of...):
"on" is a good name. Agree on separate.
Throwing another operator name out there: what about obj has "foo"
instead?
Either way, it's a new context-sensitive keyword in a very awkward position.
- Non-clobbering mass assignment. Like Object.assign but that doesn't overwrite existing properties in the assignee.
Could this be a new built-in function instead? Maybe, Object.defaults
?
Of course, this is a pretty hard tangent from the original thread.
Many use cases for hasOwnProperty could be better supported by Map, Set, and Object.create(null) including: ...
- Configuration objects.
- Function options bundles.
Not sure these would work well as maps, since it's far easier to just use dictionary objects as named arguments. Clojure and Ruby are the two main languages I know that use hash maps exclusively for named parameters with any frequency, and that's because map/hash lookup is as easy as property lookup.
- Java and C++ use builders. (Java separates the two, while C++ merges them.)
- C and Rust use structs.
- JavaScript and Lua use simple untyped objects.
- Kotlin, Scala, Swift, C#, Python, and Objective C use native named arguments that are resolved internally.
- Even Ruby added sugar to support its common idiomatic use of hashes as named arguments.
Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com
Throwing another operator name out there: what about
obj has "foo"
instead?
As someone who has just been casually following this issue, I'd much prefer
has
over on
. I was going to stay quiet, but only because I didn't have
a better alternative.
jhpratt
The only use that came to mind was detecting a property descriptor in a
prototype chain. Sure is not a day to day use case, but it's useful when
writing libraries that involve descriptor modifications (decorators, for
example, will largely involve it). Recently I had to get the descriptor of
properties in a potencial deep inheritance (current Object helpers only
return own descriptors), and used the in
operator to guard the prototype
recursive search.
const searchRecursivelyPropDescriptor = (obj, prop) =>
!obj
? undefined
: Object.getOwnPropertyDescriptor(obj, prop) ||
searchRecursivelyPropDescriptor(Object.getPrototypeOf(obj), prop);
const getPropertyDescriptor = (obj, prop) =>
prop in obj ? searchRecursivelyPropDescriptor(obj, prop) : undefined;
Anyways, we can't simply ignore the operator, if we are getting a
!instance
and opening precedence to future operators (!on
or !hasOwn
)
I don't see any problems with a !in
. Legacy bad design should not affect
language consistency of new features.
This reads a little oddly, but another syntax option would be prop in.own obj
(equivalent to obj.hasOwnProperty(prop)
) and then prop !in.own obj
.
Or perhaps in.own
should be Object.prototype.hasOwnProperty.call(obj,
prop)?
Though this makes me think it would be nice to have something like name in.map mymap
(equivalent to mymap.has(name)
)
On Tue, Jul 24, 2018 at 12:28 AM, Steve Fink <sphink at gmail.com> wrote:
Or perhaps
in.own
should be Object.prototype.hasOwnProperty.call(obj, prop)?
It should just use the abstract HasOwnProperty operation directly.
-- T.J. Crowder
I dont like to write: if ( !('x' in obj) && !('y' in obj) ) { doit() }
I was even tempted to write it that way: if ('x' in obj || 'y' in obj) { } else { doit() }
What about a !in operator to write it like this? if ('x' !in obj && 'y' !in obj) { doit() }