└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Getting last item from Array 2 | 3 | [(Currently Stage 1)](https://github.com/tc39/proposals/blob/master/stage-1-proposals.md) 4 | 5 | This proposal's champion (@keithamus) does not plan to advance to Stage 2 for now. Other proposals ([Array.prototype.item](https://github.com/tabatkins/proposal-item-method) and [Array Slice Notation](https://github.com/tc39/proposal-slice-notation)) also sufficiently solve this problem, and are advancing quicker through the standards track. Should one of these proposals advance to Stage 3, this proposal will be dropped. 6 | 7 | ## Rationale 8 | 9 | Currently the only way to get the last element of an Array is to subtract one from the `.length` property and use this value in the property lookup. Typically this looks like `someArray[someArray.length - 1]`. 10 | 11 | For such a common operation, it is easy to forget to `-1` from the `length` property, or to overcook it, and as such there should be a simpler syntax for doing this. 12 | 13 | ```js 14 | myArray[myArray.length] // oops, index out of bounds, return `undefined`, scratch head for hours from silly mistake 15 | 16 | 17 | myIndex = myArray.length - 1 18 | myArray[myIndex - 1] // oops, overcooked index, returns last-but-one not last, scratch head for hours from silly mistake 19 | ``` 20 | 21 | ## High Level Proposal 22 | 23 | Array.prototype.lastItem is a property with a getter/setter function that returns the last item of the Array. 24 | Array.prototype.lastIndex is a property with a getter function that returns the last index of the Array. 25 | 26 | ## Specification 27 | 28 | ``` 29 | 22.1.3.xx Array.prototype.lastItem 30 | 31 | It is a property where the attributes are { [[Enumerable]]: false, [[Configurable]]: false, [[Get]]: GetLastArrayItem, [[Set]]: SetLastArrayItem }. 32 | 33 | 22.1.3.xx GetLastArrayItem 34 | 35 | When the GetLastArrayItem method is called, the following steps are taken: 36 | 37 | Let O be ? ToObject(this value). 38 | Let len be ? ToLength(? Get(O, "length")). 39 | If len is zero, then 40 | Return undefined. 41 | Else len > 0, 42 | Set len to len-1. 43 | Let index be ! ToString(len). 44 | Let element be ? Get(O, index). 45 | Return element. 46 | 47 | The GetLastArrayItem function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. 48 | 49 | 22.1.3.xx SetLastArrayItem (value) 50 | 51 | When the SetLastArrayItem method is called, the following steps are taken: 52 | 53 | Let O be ? ToObject(this value). 54 | Let len be ? ToLength(? Get(O, "length")). 55 | If len > 0, then 56 | Set len to len-1. 57 | Let index be ! ToString(len). 58 | Return ? Set(O, index, value). 59 | 60 | Note 1 61 | 62 | The SetLastArrayItem function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. 63 | 64 | 22.1.3.xx Array.prototype.lastIndex 65 | 66 | It is a property where the attributes are { [[Enumerable]]: false, [[Configurable]]: false, [[Get]]: GetLastArrayIndex }. 67 | 68 | 22.1.3.xx GetLastArrayIndex 69 | 70 | When the GetLastArrayIndex method is called, the following steps are taken: 71 | 72 | Let O be ? ToObject(this value). 73 | Let len be ? ToLength(? Get(O, "length")). 74 | If len > 0, then 75 | Return len-1. 76 | Return 0. 77 | 78 | Note 1 79 | 80 | The GetLastArrayIndex function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. 81 | ``` 82 | 83 | ## Polyfill 84 | 85 | A polyfill is available in the [core-js](https://github.com/zloirock/core-js) library. You can find it in the [ECMAScript proposals section](https://github.com/zloirock/core-js#getting-last-item-from-array). 86 | 87 | It's a polyfill with usage abstract ECMAScript operations: 88 | ```js 89 | import { ToString, ToObject, ToLength } from 'es-abstract' 90 | // This polyfill tries to stick as close to the spec as possible. There are polyfills which could use less code. 91 | Object.defineProperty(Array.prototype, 'lastItem', { 92 | enumerable: false, 93 | configurable: false, 94 | get() { 95 | let O = ToObject(this) 96 | let len = ToLength(O.length) 97 | if (len === 0) { 98 | return undefined 99 | } else if (len > 0) { 100 | len = len -1 101 | let index = ToString(len) 102 | let element = O[index] 103 | return element 104 | } 105 | }, 106 | set(value) { 107 | let O = ToObject(this) 108 | let len = ToLength(O.length) 109 | if (len > 0) { 110 | len = len -1 111 | } 112 | let index = ToString(len) 113 | return O[index] = value 114 | }, 115 | }) 116 | Object.defineProperty(Array.prototype, 'lastIndex', { 117 | enumerable: false, 118 | configurable: false, 119 | get() { 120 | let O = ToObject(this) 121 | let len = ToLength(O.length) 122 | if (len > 0) { 123 | return len - 1 124 | } 125 | return 0 126 | }, 127 | }) 128 | ``` 129 | 130 | #### Other considered options 131 | 132 | - `Array.prototype.last`/`Array.prototype.last()` was originally proposed but has web-compatibility issues. 133 | - `Array.prototype.end()` as a method. The downside being that setting the last element of an array is still uses awkward syntax. 134 | - `Array.prototype.end` as a getter. However `lastItem` was a more popular name choice. 135 | - `Array.prototype.peek()` was considered (marries well with `push`, `pop`) but `peek` as a setter is unintuitive. 136 | - `Array.prototype.prePop()` [was mentioned](https://github.com/keithamus/proposal-array-last/issues/11#issuecomment-372933559) but suffers from the same issues as `peek` - it is unintuitive for setting, and potentially surprising if it _doesn't_ mutate. 137 | - [A bunch of other names](https://github.com/keithamus/proposal-array-last/issues/11#issuecomment-362246040). Most importantly see the following rational for a naming rubric: 138 | - Not have webcompat issues 139 | - Within the top 1000 most popular english words (both last and end are) 140 | - Should be intuitive for both getting and setting 141 | - Ideally not be a compound word (like lastItem) 142 | --------------------------------------------------------------------------------