Proposal: Array.prototype.first() and Array.prototype.last()
This is well-traveled territory.
Whatever is or is not implemented, interfaces which have optional arguments and return scalars in one case and arrays in another case are a horrible idea.
To my knowledge no-one has ever explained why the following is a bad idea:
array.0
array.-1
Bob
Well, String.prototype.match()
does this, it returns an array of
matches or null
in case there are no matches. I agree that in some
situation this can be a source of errors, but the implementation would
be quite useful.
In case it's not acceptable, I would propose read-only members
Array.prototype.first
and Array.prototype.last
:
Object.defineProperties(Array.prototype, {
first: {
get: function () {
return this[0];
}
},
last: {
get: function () {
if (this.length) {
return this[this.length - 1];
} else {
return void 0;
}
}
}
});
It is obvious that ‘.’ before a number will be recognized as a decimal point, which is different from the point that gets a property. For example, .2===0.2 is true. What’s more, the array’s prototype is object, which means arr[-1] will get the value of -1 rather than length-1. The array can store values at -1 like other objects. If you use -1 to represent length-1, so how do we refer to the value of -1?
If you use
a.-1
to representa[length-1]
, so how do we refer to the value of the-1
property?
a[-1]
, as at present.
It is obvious that ‘.’ before a number will be recognized as a decimal point,
I don't think it's that obvious.
I'm not a parsing guy, but I don't think the parser works backward, looking
for dots preceding numbers.
I would assume simplistically that a dot following an expression currently
puts the parser into a mode where it is looking for the following
identifier, so that logic could conceivably be tweaked to look
alternatively for an integer index.
a.0
seems eminently parseable to me.
Reply to Bob Myers:
Maybe you are right. It's just most programmers are not used to variables or properties that starts with a number.
I don't think I've ever seen an arr[-1]
access that wasn't meant to grab
last value same as arr.slice(-1)
would do.
If you deal with arrays and you are treating them as generic objects and
you store numeric values like -1
I'm not sure why would you do that or
what's your use case, all I know is that this will never be solved in TC39
so you can use a module and live happily ever after (really).
www.npmjs.com/package/length-1
Reason for that:
- it's the most common use case
- nobody ever had issues with its implementation
Best
Reply to Bob Myers:
I don't think it's that obvious. I'm not a parsing guy, but I don't think the parser works backward, looking for dots preceding numbers. I would assume simplistically that a dot following an expression currently puts the parser into a mode where it is looking for the following identifier, so that logic could conceivably be tweaked to look alternatively for an integer index. a.0 seems eminently parseable to me.
It came to me that an expression is divided by the operator from lowest cn.bing.com/dict/search?q=priority&FORM=BDVSP6&mkt=zh-cn priority
to highest cn.bing.com/dict/search?q=priority&FORM=BDVSP6&mkt=zh-cn priority.
So, the 'a.-1' will first be splited into 'a.', '-', and '1'. Then the 'a.' is not legal. The priority is not to be changed because it will impact many things.
On 09/27/2016 05:38 AM, Bob Myers wrote:
To my knowledge no-one has ever explained why the following is a bad idea:
array.0 array.-1
Consider this already-valid code:
var first = array .0.toString();
This parses right now as
var first = array; (0.0).toString();
So your proposal would break existing code. We could imagine inserting a [no LineTerminator here] inside MemberExpression to permit "." NumericLiteral and "." "-" NumericLiteral to appear here, to be sure. But that's extra complexity, extra ASI-handling (having worked on ASI handling recently, I assure you there's nothing simple about it, and further complicating ASI is a strong demerit in my book), all for IMO dubious value.
Because foo.bar
is equivlant to foo['bar']
in JS so far, and
array.-1
could break this consistency.
On the other hand, array.first()
seems not necessary because
array[0]
is even more handy; array.last()
looks fine to me.
If someone prefer a more general solution, I recommand array.get(n)
:
- if n >= 0 && n < array.length: equivlant to array[n]
- if n < 0 && -n < array.length: equivlant to array[array.length + n]
- if n <= -array.length || n >= array.length: throw or return undefined
- if n is not a integer or not a number: throw or return undefined
The last 2 rules make array.get(n)
less error prone than array[n]
. I
prefer throwing, but maybe returning undefined is more JS-style?
在 2016/9/27 20:38, Bob Myers 写道:
Le 28 sept. 2016 à 07:38, 段垚 <duanyao at ustc.edu> a écrit :
Because
foo.bar
is equivlant tofoo['bar']
in JS so far, andarray.-1
could break this consistency.On the other hand,
array.first()
seems not necessary becausearray[0]
is even more handy;array.last()
looks fine to me.If someone prefer a more general solution, I recommand
array.get(n)
:
- if n >= 0 && n < array.length: equivlant to array[n]
- if n < 0 && -n < array.length: equivlant to array[array.length + n]
- if n <= -array.length || n >= array.length: throw or return undefined
- if n is not a integer or not a number: throw or return undefined
The last 2 rules make
array.get(n)
less error prone thanarray[n]
. I prefer throwing, but maybe returning undefined is more JS-style?
For consistency with the rest of the builtin library, array.get(n)
should be equivalent to array.slice(n)[0]
, which means: convert n
to an integer, and: return undefined
for out-of-bound index.
在 2016/9/28 14:42, Claude Pache 写道:
Le 28 sept. 2016 à 07:38, 段垚 <duanyao at ustc.edu <mailto:duanyao at ustc.edu>> a écrit :
Because
foo.bar
is equivlant tofoo['bar']
in JS so far, andarray.-1
could break this consistency.On the other hand,
array.first()
seems not necessary becausearray[0]
is even more handy;array.last()
looks fine to me.If someone prefer a more general solution, I recommand
array.get(n)
:
- if n >= 0 && n < array.length: equivlant to array[n]
- if n < 0 && -n < array.length: equivlant to array[array.length + n]
- if n <= -array.length || n >= array.length: throw or return undefined
- if n is not a integer or not a number: throw or return undefined
The last 2 rules make
array.get(n)
less error prone thanarray[n]
. I prefer throwing, but maybe returning undefined is more JS-style?For consistency with the rest of the builtin library,
array.get(n)
should be equivalent toarray.slice(n)[0]
, which means: convertn
to an integer, and: returnundefined
for out-of-bound index.
I regard such converting behavior a bad legacy of JS, and want to avoid it in new APIs.
I propose to standardize
Array.prototype.first()
andArray.prototype.last()
, very similar to underscore_.first()
and_.last()
.A very basic implementation:
Array.prototype.first = function (n) { if (!arguments.length) { return this[0]; } else { return this.slice(0, Math.max(0, n)); } }; Array.prototype.last = function (n) { if (!arguments.length) { return this[this.length - 1]; } else { return this.slice(Math.max(0, this.length - n)); } };