├── .gitattributes ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── index.html ├── package.json └── spec.emu /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slice notation 2 | 3 | This repository contains a proposal for adding slice notation syntax 4 | to JavaScript. This is currently at stage 1 of the [TC39 5 | process](https://tc39.github.io/process-document/). 6 | 7 | Champions: 8 | 9 | - Sathya Gunasekaran (@gsathya) 10 | - HE Shi-Jun (@hax) 11 | 12 | 13 | ## Introduction 14 | 15 | The slice notation provides an ergonomic alternative to the various 16 | slice methods present on Array.prototype, TypedArray.prototype, etc. 17 | 18 | ```js 19 | const arr = ['a', 'b', 'c', 'd']; 20 | 21 | arr[1:3]; 22 | // → ['b', 'c'] 23 | 24 | arr.slice(1, 3); 25 | // → ['b', 'c'] 26 | ``` 27 | 28 | This notation can be used for slice operations on primitives 29 | like Array and TypedArray. 30 | 31 | 32 | ## Motivation 33 | 34 | ```js 35 | const arr = ['a', 'b', 'c', 'd']; 36 | arr.slice(3); 37 | // → ['a', 'b', 'c'] or ['d'] ? 38 | ``` 39 | 40 | In the above example, it's not immediately clear if the newly created 41 | array is a slice from the range `0` to `3` or from `3` to `len(arr)`. 42 | 43 | ```js 44 | const arr = ['a', 'b', 'c', 'd']; 45 | arr.slice(1, 3); 46 | // → ['b', 'c'] or ['b', 'c', 'd'] ? 47 | ``` 48 | 49 | Adding a second argument is also ambiguous since it's not clear if the 50 | second argument specifies an upper bound or the length of the new 51 | slice. 52 | 53 | Programming language like Ruby and C++ take the length of the new 54 | slice as the second argument, but JavaScript's slice methods take the 55 | upper bound as the second argument. 56 | 57 | ```js 58 | const arr = ['a', 'b', 'c', 'd']; 59 | arr[3:]; 60 | // → ['d'] 61 | 62 | arr[1:3]; 63 | // → ['b', 'c'] 64 | ``` 65 | 66 | With the new slice syntax, it's immediately clear that the lower bound 67 | is `3` and the upper bound is `len(arr)`. It makes the intent 68 | explicit. 69 | 70 | The syntax is also much shorter and more ergonomic than a function 71 | call. 72 | 73 | ## Examples 74 | 75 | In the following text, 'length of the object' refers to the `length` 76 | property of the object. 77 | 78 | ### Default values 79 | 80 | The lower bound and upper bound are optional. 81 | 82 | The default value for the lower bound is 0. 83 | 84 | ```js 85 | const arr = ['a', 'b', 'c', 'd']; 86 | 87 | arr[:3]; 88 | // → ['a', 'b', 'c'] 89 | ``` 90 | 91 | The default value for the upper bound is the length of the object. 92 | 93 | 94 | ```js 95 | const arr = ['a', 'b', 'c', 'd']; 96 | arr[1:]; 97 | // → ['b', 'c', 'd'] 98 | ``` 99 | 100 | Omitting all lower bound and upper bound value, produces a new copy of the object. 101 | ```js 102 | const arr = ['a', 'b', 'c', 'd']; 103 | 104 | arr[:]; 105 | // → ['a', 'b', 'c', 'd'] 106 | ``` 107 | 108 | ### Negative indices 109 | 110 | If the lower bound is negative, then the start index is computed as 111 | follows: 112 | 113 | ```js 114 | start = max(lowerBound + len, 0) 115 | ``` 116 | 117 | where `len` is the length of the object. 118 | 119 | ```js 120 | const arr = ['a', 'b', 'c', 'd']; 121 | 122 | arr[-2:]; 123 | // → ['c', 'd'] 124 | ``` 125 | 126 | In the above example, `start = max((-2 + 4), 0) = max(2, 0) = 2`. 127 | 128 | ```js 129 | const arr = ['a', 'b', 'c', 'd']; 130 | 131 | arr[-10:]; 132 | // → ['a', 'b', 'c', 'd'] 133 | ``` 134 | 135 | In the above example, `start = max((-10 + 4), 0) = max(-6, 0) = 0`. 136 | 137 | Similarly, if the upper bound is negative, the end index is computed 138 | as follows: 139 | 140 | ```js 141 | end = max(upperBound + len, 0) 142 | ``` 143 | 144 | ```js 145 | const arr = ['a', 'b', 'c', 'd']; 146 | 147 | arr[:-2]; 148 | // → ['a', 'b'] 149 | 150 | arr[:-10]; 151 | // → [] 152 | ``` 153 | 154 | These semantics exactly match the behavior of existing slice 155 | operations. 156 | 157 | ### Out of bounds indices 158 | 159 | Both the lower and upper bounds are capped at the length of the object. 160 | 161 | ```js 162 | const arr = ['a', 'b', 'c', 'd']; 163 | 164 | arr[100:]; 165 | // → [] 166 | 167 | arr[:100]; 168 | // → ['a', 'b', 'c', 'd'] 169 | ``` 170 | 171 | These semantics exactly match the behavior of existing slice 172 | operations. 173 | 174 | ## Prior art 175 | 176 | ### Python 177 | 178 | This proposal is highly inspired by Python. Unsurprisingly, the 179 | Python syntax for slice notation is strikingly similar: 180 | 181 | ```python 182 | slicing ::= primary "[" slice_list "]" 183 | slice_list ::= slice_item ("," slice_item)* [","] 184 | slice_item ::= expression | proper_slice 185 | proper_slice ::= [lower_bound] ":" [upper_bound] [ ":" [stride] ] 186 | lower_bound ::= expression 187 | upper_bound ::= expression 188 | stride ::= expression 189 | ``` 190 | 191 | Examples: 192 | 193 | ```python 194 | arr = [1, 2, 3, 4]; 195 | 196 | arr[1:3]; 197 | // → [2, 3] 198 | 199 | arr[1:4:2] 200 | // → [2, 4] 201 | ``` 202 | 203 | ### CoffeeScript 204 | 205 | CoffeeScript provides a Range operator that is _inclusive_ with respect 206 | to the upper bound. 207 | 208 | ```coffeescript 209 | arr = [1, 2, 3, 4]; 210 | arr[1..3]; 211 | // → [2, 3, 4] 212 | ``` 213 | 214 | CoffeeScript also provides another form the Range operator that is _exclusive_ with respect 215 | to the upper bound. 216 | 217 | ```coffeescript 218 | arr = [1, 2, 3, 4]; 219 | arr[1...3]; 220 | // → [2, 3] 221 | ``` 222 | 223 | ### Go 224 | 225 | Go offers [slices](https://gobyexample.com/slices): 226 | 227 | ```go 228 | arr := []int{1,2,3,4}; 229 | arr[1:3] 230 | // → [2, 3] 231 | ``` 232 | 233 | There is also ability to *not* provide lower or upper bound: 234 | 235 | ```go 236 | arr := []int{1,2,3,4}; 237 | arr[1:] 238 | // → [2, 3, 4] 239 | 240 | arr := []int{1,2,3,4}; 241 | arr[:3] 242 | // → [1, 2, 3] 243 | ``` 244 | 245 | ### Ruby 246 | 247 | Ruby seems to have two different ways to get a slice: 248 | 249 | * Using a Range: 250 | 251 | ```ruby 252 | arr = [1, 2, 3, 4]; 253 | arr[1..3]; 254 | // → [2, 3, 4] 255 | ``` 256 | 257 | This is similar to CoffeeScript. The `1..3` produces a Range object 258 | which defines the set of indices to be sliced out. 259 | 260 | * Using the comma operator: 261 | 262 | ```ruby 263 | arr = [1, 2, 3, 4]; 264 | arr[1, 3]; 265 | // → [2, 3, 4] 266 | ``` 267 | 268 | The difference here is that the second argument is actually the length 269 | of the new slice, not the upper bound index. 270 | 271 | This is currently valid ECMAScript syntax which makes this a non 272 | starter. 273 | 274 | ```js 275 | const s = 'foobar' 276 | s[1, 3] 277 | // → 'b' 278 | ``` 279 | 280 | 281 | ## FAQ 282 | 283 | ### Why pick the Python syntax over the Ruby/CoffeeScript syntax? 284 | 285 | The Python syntax which excludes the upper bound index is 286 | similar to the existing slice methods in JavaScript. 287 | 288 | We could use exclusive Range operator (`...`) from CoffeeScript, but 289 | that doesn't quite work for all cases because it's ambiguous with the 290 | spread syntax. Example code from 291 | [getify](https://gist.github.com/getify/49ae9a1f2a6031d40f5deb5ea25faa62): 292 | 293 | ```js 294 | Object.defineProperty(Number.prototype,Symbol.iterator,{ 295 | *value({ start = 0, step = 1 } = {}) { 296 | var inc = this > 0 ? step : -step; 297 | for (let i = start; Math.abs(i) <= Math.abs(this); i += inc) { 298 | yield i; 299 | } 300 | }, 301 | enumerable: false, 302 | writable: true, 303 | configurable: true 304 | }); 305 | 306 | const range = [ ...8 ]; 307 | // → [0, 1, 2, 3, 4, 5, 6, 7, 8] 308 | ``` 309 | 310 | ### Why does this not use the iterator protocol? 311 | 312 | The iterator protocol isn't restricted to index lookup making it 313 | incompatible with this slice notation which works only on 314 | indices. 315 | 316 | For example, Map and Sets have iterators but we shouldn't be able to 317 | slice them as they don't have indices. 318 | 319 | ### What about splice? 320 | 321 | CoffeeScript allows similar syntax to be used on the left hand side of 322 | an `AssignmentExpression` leading to splice operation. 323 | 324 | ```coffeescript 325 | numbers = [1, 2, 3, 4] 326 | numbers[2..4] = [7, 8] 327 | // → [1, 2, 7, 8] 328 | ``` 329 | 330 | This feature is currently omitted to limit the scope of the proposal, 331 | but can be incorporated in a follow on proposal. 332 | 333 | ### Why doesn't this include a step argument like Python does? 334 | 335 | The step argument makes the slice notation ambiguous with the bind operator. 336 | 337 | ```js 338 | const x = [2]; 339 | const arr = [1, 2, 3, 4]; 340 | arr[::x[0]]; 341 | ``` 342 | 343 | Is the above creating a new array with values `[1, 3]` or is it 344 | creating a bound method? 345 | 346 | ### Should this create a `view` over the array, instead of a creating new array? 347 | 348 | Go creates a `slice` over the underlying array, instead of allocating a new array. 349 | 350 | ```go 351 | arr := []int{1,2,3,4}; 352 | v = arr[1:3]; 353 | // → [2, 3] 354 | ``` 355 | 356 | Here, v is just descriptor that holds a reference to the original 357 | array `arr`. No new array allocation is performed. See [this blog 358 | post](https://blog.golang.org/go-slices-usage-and-internals) for more 359 | details. 360 | 361 | This doesn't map to any existing construct in JavaScript and this would 362 | be a step away from how methods work in JavaScript. To make this 363 | syntax work well within the JavaScript model, such a `view` data 364 | structure is not included in this proposal. 365 | 366 | ### Should slice notation work on strings? 367 | 368 | The `String.prototype.slice` method doesn't work well with unicode 369 | characters. [This blog 370 | post](https://mathiasbynens.be/notes/javascript-unicode) by Mathias 371 | Bynens, explains the problem. 372 | 373 | Given that the existing method doesn't work well, this proposal 374 | does not add `@@slice` to `String.prototype`. 375 | 376 | ### How about combining this with `+` for append? 377 | 378 | ```js 379 | const arr = [1, 2, 3, 4] + [5, 6]; 380 | // → [1, 2, 3, 4, 5, 6] 381 | ``` 382 | 383 | This is not included in order to keep the proposal's scope maximally 384 | minimal. 385 | 386 | The [operator overloading 387 | proposal](https://github.com/keithamus/ecmascript-operator-overloading-proposal) 388 | may be a better fit for this. 389 | 390 | ### Can you create a Range object using this syntax? 391 | 392 | The slice notation only provides an ergonomic syntax for performing a slice 393 | operation. 394 | 395 | The current slice notation doesn't preclude creating a range primitive in the 396 | future. 397 | 398 | A new Range primitive is being discussed here: 399 | https://github.com/tc39/proposal-Number.range/issues/22 400 | 401 | ### Isn't it confusing that this isn't doing property lookup? 402 | 403 | This is actually doing a property lookup using `[[Get]]` on the 404 | underlying object. For example, 405 | 406 | ```js 407 | const arr = [1, 2, 3, 4]; 408 | 409 | arr[1:3]; 410 | // → [2, 3] 411 | ``` 412 | 413 | This is doing a property lookup for the keys `1` and `2`. 414 | 415 | But, shouldn't it do a lookup for the string `'1:3'`? 416 | 417 | ```js 418 | const arr = [1, 2, 3, 4]; 419 | 420 | arr['1:3']; 421 | // → undefined 422 | ``` 423 | 424 | No. The slice notation makes it analogous with how keyed lookup 425 | works. The key is first evaluated to a value and then the lookup 426 | happens using this value. 427 | 428 | ```js 429 | const arr = [1, 2, 3, 4]; 430 | const x = 0; 431 | 432 | arr[x] !== arr['x']; 433 | // → true 434 | ``` 435 | 436 | The slice notation works similarly. The notation is first evaluated to 437 | a range of values and then each of the values are looked up. 438 | 439 | ### There are already many modes where ':' mean different things. Isn't this confusing? 440 | 441 | Depending on context `a:b`, can mean: 442 | 443 | - `LabelledStatement` with `a` as the label 444 | - Property a with value b in an object literal: `{a: b }` 445 | - ConditionalExpression: `confused ? a : b` 446 | - Potential type systems (like TypeScript and Flow) that might make it 447 | to JavaScript in the future. 448 | 449 | Is it a lot of overhead to disambiguate between modes with context? 450 | Major mainstream programming languages like Python have all these 451 | modes and are being used as a primary tool for teaching programming. 452 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
Well-known symbols are built-in Symbol values that are explicitly referenced by algorithms of this specification. They are typically used as the keys of properties whose values serve as extension points of a specification algorithm. Unless otherwise specified, well-known symbols values are shared by all realms (
Within this specification a well-known symbol is referred to by using a notation of the form @@name, where “name” is one of the values listed in
1855 | Specification Name 1856 | | 1857 |1858 | [[Description]] 1859 | | 1860 |1861 | Value and Purpose 1862 | | 1863 |
---|---|---|
1866 | @@slice 1867 | | 1868 |
1869 | |
1871 | 1872 | A method that slices the receiver based on a start and end index. 1873 | | 1874 |
The initial value of Symbol.slice
is the well known symbol
This property has the attributes { [[Writable]]:
The initial value of the Array.prototype.slice
property.
The initial value of the %TypedArray%.prototype.slice
property.
The initial value of the ArrayBuffer.prototype.slice
property.
The initial value of the SharedArrayBuffer.prototype.slice
property.
The abstract operation EvaluateSliceNotation takes as arguments a baseValue (an ECMAScript langauge value), startExpression (a
With parameters baseValue and baseReference.
2000 |2 | title: Slice notation 3 | status: proposal 4 | stage: 1 5 | location: https://github.com/tc39/proposal-slice-notation 6 | copyright: false 7 | contributors: Sathya Gunasekaran 8 |9 | 10 |
Well-known symbols are built-in Symbol values that are explicitly referenced by algorithms of this specification. They are typically used as the keys of properties whose values serve as extension points of a specification algorithm. Unless otherwise specified, well-known symbols values are shared by all realms (
Within this specification a well-known symbol is referred to by using a notation of the form @@name, where “name” is one of the values listed in
19 | Specification Name 20 | | 21 |22 | [[Description]] 23 | | 24 |25 | Value and Purpose 26 | | 27 |
---|---|---|
30 | @@slice 31 | | 32 |33 | *"Symbol.slice"* 34 | | 35 |36 | A method that slices the receiver based on a start and end index. 37 | | 38 |
The initial value of `Symbol.slice` is the well known symbol @@slice (
This property has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, [[Configurable]]: *false* }.
50 |The initial value of the @@slice property is the same function object as the initial value of the `Array.prototype.slice` property.
58 |The initial value of the @@slice property is the same function object as the initial value of the `%TypedArray%.prototype.slice` property.
66 |The initial value of the @@slice property is the same function object as the initial value of the `ArrayBuffer.prototype.slice` property.
74 |The initial value of the @@slice property is the same function object as the initial value of the `SharedArrayBuffer.prototype.slice` property.
82 |The abstract operation EvaluateSliceNotation takes as arguments a _baseValue_ (an ECMAScript langauge value), _startExpression_ (a Parse Node), and _endExpression_ (a Parse Node). It performs the following steps:
151 |With parameters _baseValue_ and _baseReference_.
192 |