{"id":13527076,"url":"https://github.com/tc39/proposal-slice-notation","last_synced_at":"2025-04-04T20:12:38.865Z","repository":{"id":37561593,"uuid":"123481320","full_name":"tc39/proposal-slice-notation","owner":"tc39","description":null,"archived":false,"fork":false,"pushed_at":"2023-12-13T02:23:33.000Z","size":87,"stargazers_count":527,"open_issues_count":9,"forks_count":19,"subscribers_count":58,"default_branch":"master","last_synced_at":"2025-03-28T19:08:13.158Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://tc39.es/proposal-slice-notation/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tc39.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-01T19:17:28.000Z","updated_at":"2025-01-14T16:18:47.000Z","dependencies_parsed_at":"2024-01-10T06:12:15.189Z","dependency_job_id":"b4caa356-470f-446b-ba77-cd64cab94c5a","html_url":"https://github.com/tc39/proposal-slice-notation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-slice-notation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-slice-notation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-slice-notation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-slice-notation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-slice-notation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242680,"owners_count":20907134,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:40.403Z","updated_at":"2025-04-04T20:12:38.845Z","avatar_url":"https://github.com/tc39.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"# Slice notation\n\nThis repository contains a proposal for adding slice notation syntax\nto JavaScript. This is currently at stage 1 of the [TC39\nprocess](https://tc39.github.io/process-document/).\n\nChampions:\n\n- Sathya Gunasekaran (@gsathya)\n- HE Shi-Jun (@hax)\n\n\n## Introduction\n\nThe slice notation provides an ergonomic alternative to the various\nslice methods present on Array.prototype, TypedArray.prototype, etc.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[1:3];\n// → ['b', 'c']\n\narr.slice(1, 3);\n// → ['b', 'c']\n```\n\nThis notation can be used for slice operations on primitives\nlike Array and TypedArray.\n\n\n## Motivation\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\narr.slice(3);\n// → ['a', 'b', 'c'] or ['d'] ?\n```\n\nIn the above example, it's not immediately clear if the newly created\narray is a slice from the range `0` to `3` or from `3` to `len(arr)`.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\narr.slice(1, 3);\n// → ['b', 'c'] or ['b', 'c', 'd'] ?\n```\n\nAdding a second argument is also ambiguous since it's not clear if the\nsecond argument specifies an upper bound or the length of the new\nslice.\n\nProgramming language like Ruby and C++ take the length of the new\nslice as the second argument, but JavaScript's slice methods take the\nupper bound as the second argument.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\narr[3:];\n// → ['d']\n\narr[1:3];\n// → ['b', 'c']\n```\n\nWith the new slice syntax, it's immediately clear that the lower bound\nis `3` and the upper bound is `len(arr)`. It makes the intent\nexplicit.\n\nThe syntax is also much shorter and more ergonomic than a function\ncall.\n\n## Examples\n\nIn the following text, 'length of the object' refers to the `length`\nproperty of the object.\n\n### Default values\n\nThe lower bound and upper bound are optional.\n\nThe default value for the lower bound is 0.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[:3];\n// → ['a', 'b', 'c']\n```\n\nThe default value for the upper bound is the length of the object.\n\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\narr[1:];\n// → ['b', 'c', 'd']\n```\n\nOmitting all lower bound and upper bound value, produces a new copy of the object.\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[:];\n// → ['a', 'b', 'c', 'd']\n```\n\n### Negative indices\n\nIf the lower bound is negative, then the start index is computed as\nfollows:\n\n```js\nstart = max(lowerBound + len, 0)\n```\n\nwhere `len` is the length of the object.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[-2:];\n// → ['c', 'd']\n```\n\nIn the above example, `start = max((-2 + 4), 0) = max(2, 0) = 2`.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[-10:];\n// → ['a', 'b', 'c', 'd']\n```\n\nIn the above example, `start = max((-10 + 4), 0) = max(-6, 0) = 0`.\n\nSimilarly, if the upper bound is negative, the end index is computed\nas follows:\n\n```js\nend = max(upperBound + len, 0)\n```\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[:-2];\n// → ['a', 'b']\n\narr[:-10];\n// → []\n```\n\nThese semantics exactly match the behavior of existing slice\noperations.\n\n### Out of bounds indices\n\nBoth the lower and upper bounds are capped at the length of the object.\n\n```js\nconst arr = ['a', 'b', 'c', 'd'];\n\narr[100:];\n// → []\n\narr[:100];\n// → ['a', 'b', 'c', 'd']\n```\n\nThese semantics exactly match the behavior of existing slice\noperations.\n\n## Prior art\n\n### Python\n\nThis proposal is highly inspired by Python. Unsurprisingly, the\nPython syntax for slice notation is strikingly similar:\n\n```python\nslicing      ::=  primary \"[\" slice_list \"]\"\nslice_list   ::=  slice_item (\",\" slice_item)* [\",\"]\nslice_item   ::=  expression | proper_slice\nproper_slice ::=  [lower_bound] \":\" [upper_bound] [ \":\" [stride] ]\nlower_bound  ::=  expression\nupper_bound  ::=  expression\nstride       ::=  expression\n```\n\nExamples:\n\n```python\narr = [1, 2, 3, 4];\n\narr[1:3];\n// → [2, 3]\n\narr[1:4:2]\n// → [2, 4]\n```\n\n### CoffeeScript\n\nCoffeeScript provides a Range operator that is _inclusive_ with respect\nto the upper bound.\n\n```coffeescript\narr = [1, 2, 3, 4];\narr[1..3];\n// → [2, 3, 4]\n```\n\nCoffeeScript also provides another form the Range operator that is _exclusive_ with respect\nto the upper bound.\n\n```coffeescript\narr = [1, 2, 3, 4];\narr[1...3];\n// → [2, 3]\n```\n\n### Go\n\nGo offers [slices](https://gobyexample.com/slices):\n\n```go\narr := []int{1,2,3,4};\narr[1:3]\n// → [2, 3]\n```\n\nThere is also ability to *not* provide lower or upper bound:\n\n```go\narr := []int{1,2,3,4};\narr[1:]\n// → [2, 3, 4]\n\narr := []int{1,2,3,4};\narr[:3]\n// → [1, 2, 3]\n```\n\n### Ruby\n\nRuby seems to have two different ways to get a slice:\n\n* Using a Range:\n\n```ruby\narr = [1, 2, 3, 4];\narr[1..3];\n// → [2, 3, 4]\n```\n\nThis is similar to CoffeeScript. The `1..3` produces a Range object\nwhich defines the set of indices to be sliced out.\n\n* Using the comma operator:\n\n```ruby\narr = [1, 2, 3, 4];\narr[1, 3];\n// → [2, 3, 4]\n```\n\nThe difference here is that the second argument is actually the length\nof the new slice, not the upper bound index.\n\nThis is currently valid ECMAScript syntax which makes this a non\nstarter.\n\n```js\nconst s = 'foobar'\ns[1, 3]\n// → 'b'\n```\n\n\n## FAQ\n\n### Why pick the Python syntax over the Ruby/CoffeeScript syntax?\n\nThe Python syntax which excludes the upper bound index is\nsimilar to the existing slice methods in JavaScript.\n\nWe could use exclusive Range operator (`...`) from CoffeeScript, but\nthat doesn't quite work for all cases because it's ambiguous with the\nspread syntax. Example code from\n[getify](https://gist.github.com/getify/49ae9a1f2a6031d40f5deb5ea25faa62):\n\n```js\nObject.defineProperty(Number.prototype,Symbol.iterator,{\n  *value({ start = 0, step = 1 } = {}) {\n     var inc = this \u003e 0 ? step : -step;\n     for (let i = start; Math.abs(i) \u003c= Math.abs(this); i += inc) {\n        yield i;\n     }\n  },\n  enumerable: false,\n  writable: true,\n  configurable: true\n});\n\nconst range = [ ...8 ];\n// → [0, 1, 2, 3, 4, 5, 6, 7, 8]\n```\n\n### Why does this not use the iterator protocol?\n\nThe iterator protocol isn't restricted to index lookup making it\nincompatible with this slice notation which works only on\nindices.\n\nFor example, Map and Sets have iterators but we shouldn't be able to\nslice them as they don't have indices.\n\n### What about splice?\n\nCoffeeScript allows similar syntax to be used on the left hand side of\nan `AssignmentExpression` leading to splice operation.\n\n```coffeescript\nnumbers = [1, 2, 3, 4]\nnumbers[2..4] = [7, 8]\n// → [1, 2, 7, 8]\n```\n\nThis feature is currently omitted to limit the scope of the proposal,\nbut can be incorporated in a follow on proposal.\n\n### Why doesn't this include a step argument like Python does?\n\nThe step argument makes the slice notation ambiguous with the bind operator.\n\n```js\nconst x = [2];\nconst arr = [1, 2, 3, 4];\narr[::x[0]];\n```\n\nIs the above creating a new array with values `[1, 3]` or is it\ncreating a bound method?\n\n### Should this create a `view` over the array, instead of a creating new array?\n\nGo creates a `slice` over the underlying array, instead of allocating a new array.\n\n```go\narr := []int{1,2,3,4};\nv = arr[1:3];\n// → [2, 3]\n```\n\nHere, v is just descriptor that holds a reference to the original\narray `arr`. No new array allocation is performed. See [this blog\npost](https://blog.golang.org/go-slices-usage-and-internals) for more\ndetails.\n\nThis doesn't map to any existing construct in JavaScript and this would\nbe a step away from how methods work in JavaScript. To make this\nsyntax work well within the JavaScript model, such a `view` data\nstructure is not included in this proposal.\n\n### Should slice notation work on strings?\n\nThe `String.prototype.slice` method doesn't work well with unicode\ncharacters. [This blog\npost](https://mathiasbynens.be/notes/javascript-unicode) by Mathias\nBynens, explains the problem.\n\nGiven that the existing method doesn't work well, this proposal\ndoes not add `@@slice` to `String.prototype`.\n\n### How about combining this with `+` for append?\n\n```js\nconst arr = [1, 2, 3, 4] + [5, 6];\n// → [1, 2, 3, 4, 5, 6]\n```\n\nThis is not included in order to keep the proposal's scope maximally\nminimal.\n\nThe [operator overloading\nproposal](https://github.com/keithamus/ecmascript-operator-overloading-proposal)\nmay be a better fit for this.\n\n### Can you create a Range object using this syntax?\n\nThe slice notation only provides an ergonomic syntax for performing a slice\noperation. \n\nThe current slice notation doesn't preclude creating a range primitive in the \nfuture.\n\nA new Range primitive is being discussed here:\nhttps://github.com/tc39/proposal-Number.range/issues/22\n\n### Isn't it confusing that this isn't doing property lookup?\n\nThis is actually doing a property lookup using `[[Get]]` on the\nunderlying object. For example,\n\n```js\nconst arr = [1, 2, 3, 4];\n\narr[1:3];\n// → [2, 3]\n```\n\nThis is doing a property lookup for the keys `1` and `2`.\n\nBut, shouldn't it do a lookup for the string `'1:3'`?\n\n```js\nconst arr = [1, 2, 3, 4];\n\narr['1:3'];\n// → undefined\n```\n\nNo. The slice notation makes it analogous with how keyed lookup\nworks. The key is first evaluated to a value and then the lookup\nhappens using this value.\n\n```js\nconst arr = [1, 2, 3, 4];\nconst x = 0;\n\narr[x] !== arr['x'];\n// → true\n```\n\nThe slice notation works similarly. The notation is first evaluated to\na range of values and then each of the values are looked up.\n\n### There are already many modes where ':' mean different things. Isn't this confusing?\n\nDepending on context `a:b`, can mean:\n\n- `LabelledStatement` with `a` as the label\n- Property a with value b in an object literal: `{a: b }`\n- ConditionalExpression: `confused ? a : b`\n- Potential type systems (like TypeScript and Flow) that might make it\n  to JavaScript in the future.\n\nIs it a lot of overhead to disambiguate between modes with context?\nMajor mainstream programming languages like Python have all these\nmodes and are being used as a primary tool for teaching programming.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-slice-notation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-slice-notation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-slice-notation/lists"}