├── transpiler.js ├── ecmascript.js ├── PITCHME.md └── README.md /transpiler.js: -------------------------------------------------------------------------------- 1 | var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 2 | var ARGUMENT_NAMES = /([^\s,]+)/g; 3 | function getParamNames(func) { 4 | var fnStr = func.toString().replace(STRIP_COMMENTS, ''); 5 | var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); 6 | if(result === null) 7 | result = []; 8 | return result; 9 | } 10 | 11 | function a(b, c) { 12 | } 13 | 14 | let params = getParamNames(a); 15 | 16 | console.log(params); -------------------------------------------------------------------------------- /ecmascript.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | // let index = fs.readFileSync("ecmascript.json"); 4 | let index = fs.readFileSync("html5.json"); 5 | let types = eval('x=' + index); 6 | 7 | // console.log(types.definitions); 8 | 9 | let result = []; 10 | 11 | for (let i in types.definitions) { 12 | let def = types.definitions[i]; 13 | // console.log(def.type); 14 | for (let j in def.properties) { 15 | // console.log(j); 16 | if (def.properties[j].type == "function") { 17 | let parameters = def.properties[j].parameter; 18 | let func = j; 19 | let object = i; 20 | // console.log(i); 21 | // console.log(`${object}.${func} with ${parameters.length}`); 22 | let optional = 0; 23 | parameters.forEach(param => { if (param.optional) { optional++ } }); 24 | // if (optional > 0) { 25 | // console.log(`${JSON.stringify(parameters)}`); 26 | // return; 27 | // } 28 | result.push({name: `${object}.${func}`, parameters: parameters.length, optionals: optional}); 29 | } 30 | } 31 | } 32 | 33 | // result.sort(function(a, b) { return a.parameters - b.parameters }); 34 | result.sort(function(a, b) { return a.optionals - b.optionals }); 35 | 36 | // console.log(`|API| Number of Parameters|`); 37 | 38 | for (let p = 0; p < result.length; p++) { 39 | let api = result[result.length - 1 - p]; 40 | 41 | if (api.optionals < 2) { 42 | break; 43 | } 44 | 45 | console.log(`* ${api.name}: ${api.optionals} optional [of ${api.parameters}]`); 46 | } 47 | -------------------------------------------------------------------------------- /PITCHME.md: -------------------------------------------------------------------------------- 1 | # Named Parameters 2 | ## Seeking Stage 1 3 | 4 | --- 5 | 6 | ### Overview 7 | ### Use Cases 8 | ### Areas of Investigation 9 | 10 | --- 11 | 12 | ### Overview 13 | 14 | +++ 15 | 16 | ### An opt-in mechanism to expose named parameters. 17 | 18 | ```javascript 19 | // ... whe you write this ... 20 | function dostuff(b as b, c as c) { 21 | } 22 | 23 | // ... you enable callers like this ... 24 | dostuff(b: 1, c: false); 25 | ``` 26 | 27 | @[1-4] (You use "as" to expose named parameters ...) 28 | @[5-6] (... so your callers can use them ...) 29 | 30 | --- 31 | 32 | ### Use Cases 33 | 34 | +++ 35 | 36 | ### Long parameters list 37 | 38 | ```javascript 39 | // Signature: 40 | // Date.UTC(year, month[, day[, hour[, minute[, second[, millisecond]]]]]) 41 | 42 | // ... without named parameters ... 43 | var utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0)); 44 | 45 | // ... with named parameters ... 46 | var utcDate = new Date(Date.UTC( 47 | year: 96, 48 | month: 11, 49 | day: 1, 50 | hour: 0, 51 | minute: 0, 52 | millisecond: 0 53 | )); 54 | ``` 55 | 56 | @[1-2] 57 | @[4-5] 58 | @[7-15] 59 | 60 | +++ 61 | 62 | ### WebGL 63 | 64 | ```javascript 65 | // ... what does this mean? ... 66 | ctx.drawImage(image, 33, 71, 104, 124, 21, 20, 87, 104); 67 | 68 | // ... ah, much clearer ... 69 | ctx.drawImage( 70 | image: image, 71 | sx: 33, 72 | sy: 71, 73 | swidth: 104, 74 | sHeight: 124, 75 | dx: 21, 76 | dy: 20, 77 | dWidth: 87, 78 | dHeight: 104 79 | ); 80 | ``` 81 | 82 | @[1-2] 83 | @[4-15] 84 | 85 | +++ 86 | 87 | ### Uncommon parameters 88 | 89 | ```javascript 90 | // ... is it bubble or capture? ... 91 | el.addEventListener("mouseup", listener, true) 92 | 93 | // ... ah, ok, capturing ... 94 | el.addEventListener("mouseup", listener, capture: true) 95 | ``` 96 | 97 | @[1-2] 98 | @[3-5] 99 | 100 | +++ 101 | 102 | ### Parameters of the same type 103 | 104 | ```javascript 105 | // as opposed to move(100, 200) is the 100 x or y? 106 | move(x: 100, y: 200); 107 | 108 | // as opposed to resize(20, 30) is 20 the width or height? 109 | resize(width: 20, height: 30); 110 | ``` 111 | 112 | +++ 113 | 114 | ### Multiple optional parameters 115 | 116 | ```javascript 117 | // Signature: 118 | blob.slice([start [, end [, contentType]]]); 119 | 120 | // Awkward way to change the mime type: 121 | let blob = source.slice( 122 | undefined, undefined, "image/jpeg"); 123 | 124 | // Error-prone way to change the mime type: 125 | let blob = source.slice( 126 | 0, source.size, "image/jpeg"); 127 | 128 | // With named parameters: 129 | let blob = source.slice(contentType: "image/jpeg") 130 | ``` 131 | 132 | @[1-2] 133 | @[4-6] 134 | @[8-10] 135 | @[12-13] 136 | 137 | --- 138 | 139 | ### Areas of Investigation 140 | 141 | +++ 142 | 143 | ### mixed parameters 144 | 145 | ```javascript 146 | // Can we intermingle named parameters with positional? 147 | fetch("url.json", option: { 148 | ... 149 | }); 150 | ``` 151 | 152 | --- 153 | 154 | ### Stage 1? -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | * Early reviewers: @adamk, @domenic, @slightlyoff, @erights, @waldemarhorwats, @dimvar 4 | 5 | This is a [stage 0](https://tc39.github.io/process-document/) proposal to add named parameters to Javascript. 6 | 7 | In this formulation, an **opt-in** mechanism is introduced to expose parameter names with a new keyword ```as```: 8 | 9 | ```javascript 10 | function dostuff(b as b, c as c) { 11 | } 12 | ``` 13 | 14 | That enables callers of your function to use named parameters: 15 | 16 | ```javascript 17 | dostuff(b: 1, c: false); 18 | ``` 19 | 20 | ## Keeping the contract 21 | 22 | The opt-in mechanism ```as``` serves as an explicit and deliberate decision of the author of the function to expose parameter names, ***keeping*** the contract that parameter names are erased by default. 23 | 24 | Breaking that contract can be harmful, notably with the interaction with minifiers: exposing (without consent) the parameters now forces library authors to maintain them and their backwards compatibility. 25 | 26 | We expect (encourage, really) functions that have a large number of parameters to be the exception rather than the norm, so an opt-in mechanism works well too. 27 | 28 | # Use cases 29 | 30 | ## Legacy Web APIs 31 | 32 | The most immediate application of named parameter is with web APIs that were created prior to destructuring parameters and options. So, for example, we could make ```Date.UTC``` more ergonomic by exposing named parameters: 33 | 34 | ```javascript 35 | // Signature: 36 | // Date.UTC(year, month[, day[, hour[, minute[, second[, millisecond]]]]]) 37 | ``` 38 | Gets currently called as: 39 | ```javascript 40 | // Wow, remind me again what do these mean? 41 | var utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0)); 42 | ``` 43 | With named parameters: 44 | ```javascript 45 | // Ah, ok, got it: 46 | var utcDate = new Date(Date.UTC( 47 | year: 96, 48 | month: 11, 49 | day: 1, 50 | hour: 0, 51 | minute: 0, 52 | millisecond: 0 53 | )); 54 | ``` 55 | 56 | You can find here a list of web apis sorted by [number of parameters](#by-number-of-parameters). 57 | 58 | ## Documentation 59 | 60 | Named parameters work well too even with as few as three parameters and extremely popular functions, when one of the parameters is generally more obscure. For example: 61 | 62 | ```javascript 63 | // Remind me again whether the third parameter is to 64 | // capture or to bubble? 65 | el.addEventListener("mouseup", listener, true) 66 | ``` 67 | 68 | One can write: 69 | 70 | ```javascript 71 | // Ah, ok, we'll capture it rather bubble it. 72 | el.addEventListener("mouseup", listener, capture: true). 73 | ``` 74 | 75 | You can enable both users that are confident with your API (and would rather use positional arguments for efficiency / readability) as well as new users that are still getting the hang of your APIs: 76 | 77 | ```javascript 78 | // NOTE(newbie): moveTo takes an x and y coordinate. 79 | moveTo(x: 10, y: 20) 80 | ``` 81 | 82 | ## Multiple Required Parameters 83 | 84 | In addition to making code more readable, it also has applications when parameter types are of the same type and are all required. Example, take Web GLs ```drawImage```: 85 | 86 | ```javascript 87 | // What do all of these integers mean? 88 | ctx.drawImage(image, 33, 71, 104, 124, 21, 20, 87, 104); 89 | ``` 90 | With named parameters: 91 | ```javascript 92 | // Ah, ok, got it: 93 | ctx.drawImage( 94 | image: image, 95 | sx: 33, 96 | sy: 71, 97 | swidth: 104, 98 | sHeight: 124, 99 | dx: 21, 100 | dy: 20, 101 | dWidth: 87, 102 | dHeight: 104 103 | ); 104 | ``` 105 | 106 | ## Multiple Optional Parameters 107 | 108 | There are many (arguably) valid function signatures with multiple optional parameters where it is error prone / awkward to use parameters positionally. 109 | 110 | For example, take [```Blob.slice```](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice)'s signature: 111 | 112 | ```javascript 113 | // Signature: 114 | blob.slice([start [, end [, contentType]]]); 115 | ``` 116 | If you wanted to change the ```source``` mimetype, you could: 117 | ``` 118 | // Awkward way to change the mime type: 119 | let blob = source.slice( 120 | undefined, undefined, "image/jpeg"); 121 | ``` 122 | or 123 | ```javascript 124 | // Error-prone way to change the mime type: 125 | let blob = source.slice( 126 | 0, source.size, "image/jpeg"); 127 | ``` 128 | Whereas with named parameters: 129 | ```javascript 130 | // With named parameters: 131 | let blob = source.slice(contentType: "image/jpeg") 132 | 133 | ``` 134 | 135 | You can find here a list of web apis sorted by [number of optional parameters](#by-number-of-optional-parameters). 136 | 137 | # Cross Cutting Considerations 138 | 139 | Generally speaking, we would want all features (past and future) that are applied to parameters to apply to named parameters. 140 | 141 | ## Default Values 142 | 143 | ```javascript 144 | function a(b = 123 as b, c as c) { 145 | } 146 | a(c: false) // b takes 123 147 | ``` 148 | 149 | ## Destructuring Parameters 150 | 151 | Destructuring works the same way, except that the parameter can be referred nominally rather than (or, in addition to) positionally. For example: 152 | 153 | ```javascript 154 | function fetch(url, {method, headers, mode} as options) { 155 | ... 156 | } 157 | ``` 158 | Enables it to be called as: 159 | ```javascript 160 | fetch("url.txt", options: { 161 | method: 'GET', 162 | headers: myHeaders, 163 | mode: 'cors', 164 | cache: 'default' 165 | }); 166 | ``` 167 | Destructuring can also be more generally used to name parameters. For example: 168 | 169 | ```javascript 170 | function foo({arg1, arg2, ..., argN}) { 171 | } 172 | ``` 173 | Also enables callers to use: 174 | 175 | ```javascript 176 | foo({arg1: val1, arg2: val2, ..., argN: valN}) 177 | ``` 178 | But, as opposed to naming parameters, doesn't enable callers to use positional parameters ... 179 | 180 | ```javascript 181 | foo(val1, val2, ..., valN) 182 | ``` 183 | ... nor to start their APIs positionally and extend it to nominally afterwards ... 184 | ```javascript 185 | function foo(arg1, arg2, {arg3, ..., argN}) { 186 | } 187 | ``` 188 | 189 | # Areas of Investigation 190 | 191 | * can you intermingle positional arguments with named arguments? 192 | 193 | # Prior art 194 | 195 | * https://esdiscuss.org/topic/named-paramters 196 | * https://github.com/lukescott/es-named-arguments 197 | 198 | # Annex 199 | 200 | ## Web APIs 201 | 202 | Here are the functions on the Web platform with the most paramters (data from http://html5index.org/json/): 203 | 204 | Notable examples: 205 | 206 | * HTMLInputElement.setRangeText: 4 207 | * Document.open: 4 208 | * EventTarget.addEventListener: 4 209 | 210 | ## By number of parameters 211 | 212 | All Web APIs with 4 or more parameters, in descending order: 213 | 214 | * WebGLRenderingContext.texSubImage2D: 9 215 | * CanvasRenderingContext2D.drawImage: 9 216 | * WebGLRenderingContext.texImage2D: 9 217 | * WebGLRenderingContext.copyTexSubImage2D: 8 218 | * WebGLRenderingContext.copyTexImage2D: 8 219 | * WebGLRenderingContext.compressedTexSubImage2D: 8 220 | * CanvasRenderingContext2D.ellipse: 8 221 | * Path2D.ellipse: 8 222 | * WebGLRenderingContext.compressedTexImage2D: 7 223 | * Date.UTC: 7 224 | * CanvasRenderingContext2D.arcTo: 7 225 | * WebGLRenderingContext.readPixels: 7 226 | * CanvasRenderingContext2D.putImageData: 7 227 | * SVGPathElement.createSVGPathSegArcAbs: 7 228 | * Path2D.arcTo: 7 229 | * SVGPathElement.createSVGPathSegArcRel: 7 230 | * SVGPathElement.createSVGPathSegCurvetoCubicAbs: 6 231 | * Path2D.addText: 6 232 | * CanvasRenderingContext2D.bezierCurveTo: 6 233 | * Path2D.addPathByStrokingText: 6 234 | * WebGLRenderingContext.vertexAttribPointer: 6 235 | * SVGPathElement.createSVGPathSegCurvetoCubicRel: 6 236 | * CanvasRenderingContext2D.setTransform: 6 237 | * Path2D.bezierCurveTo: 6 238 | * CanvasRenderingContext2D.createRadialGradient: 6 239 | * Path2D.arc: 6 240 | * AudioListener.setOrientation: 6 241 | * CanvasRenderingContext2D.transform: 6 242 | * CanvasRenderingContext2D.arc: 6 243 | * WorkerGlobalScope.createImageBitmap: 5 244 | * WebGLRenderingContext.uniform4f: 5 245 | * WebGLRenderingContext.framebufferTexture2D: 5 246 | * WebGLRenderingContext.vertexAttrib4f: 5 247 | * WebGLRenderingContext.uniform4i: 5 248 | * IDBKeyRange.bound: 4 249 | * CanvasRenderingContext2D.clearRect: 4 250 | * Path2D.rect: 4 251 | * Path2D.quadraticCurveTo: 4 252 | * WebGLRenderingContext.colorMask: 4 253 | * WebGLRenderingContext.stencilFuncSeparate: 4 254 | * CanvasRenderingContext2D.strokeText: 4 255 | * CanvasRenderingContext2D.strokeRect: 4 256 | * WebGLRenderingContext.scissor: 4 257 | * Date.setUTCHours: 4 258 | * WebGLRenderingContext.renderbufferStorage: 4 259 | * WebGLRenderingContext.clearColor: 4 260 | * WebGLRenderingContext.uniform3f: 4 261 | * CustomEvent.initCustomEvent: 4 262 | * Date.setHours: 4 263 | * CanvasRenderingContext2D.rect: 4 264 | * CanvasRenderingContext2D.quadraticCurveTo: 4 265 | * SVGPaint.setPaint: 4 266 | * CanvasRenderingContext2D.isPointInPath: 4 267 | * CanvasRenderingContext2D.getImageData: 4 268 | * CanvasRenderingContext2D.fillText: 4 269 | * CanvasRenderingContext2D.fillRect: 4 270 | * WebGLRenderingContext.drawElements: 4 271 | * WebGLRenderingContext.uniform3i: 4 272 | * HTMLTextAreaElement.setRangeText: 4 273 | * WebGLRenderingContext.blendFuncSeparate: 4 274 | * WebGLRenderingContext.framebufferRenderbuffer: 4 275 | * WebGLRenderingContext.blendColor: 4 276 | * SVGPathElement.createSVGPathSegCurvetoCubicSmoothAbs: 4 277 | * SVGPathElement.createSVGPathSegCurvetoCubicSmoothRel: 4 278 | * Document.open: 4 279 | * WebGLRenderingContext.vertexAttrib3f: 4 280 | * SVGPathElement.createSVGPathSegCurvetoQuadraticAbs: 4 281 | * HTMLInputElement.setRangeText: 4 282 | * SVGPathElement.createSVGPathSegCurvetoQuadraticRel: 4 283 | * CanvasRenderingContext2D.createLinearGradient: 4 284 | * WebGLRenderingContext.viewport: 4 285 | * WebGLRenderingContext.stencilOpSeparate: 4 286 | 287 | ## By number of optional parameters 288 | 289 | And here is the same list but sorted by number of optional parameters: 290 | 291 | * CanvasRenderingContext2D.drawImage: 6 optional [of 9] 292 | * Date.UTC: 5 optional [of 7] 293 | * Document.open: 4 optional [of 4] 294 | * CanvasRenderingContext2D.putImageData: 4 optional [of 7] 295 | * Date.setUTCHours: 3 optional [of 4] 296 | * Date.setHours: 3 optional [of 4] 297 | * WebGLRenderingContext.texImage2D: 3 optional [of 9] 298 | * HTMLTextAreaElement.setRangeText: 3 optional [of 4] 299 | * AudioBufferSourceNode.start: 3 optional [of 3] 300 | * HTMLInputElement.setRangeText: 3 optional [of 4] 301 | * Blob.slice: 3 optional [of 3] 302 | * CanvasRenderingContext2D.isPointInPath: 2 optional [of 4] 303 | * IDBIndex.openKeyCursor: 2 optional [of 2] 304 | * IDBIndex.openCursor: 2 optional [of 2] 305 | * CanvasRenderingContext2D.arcTo: 2 optional [of 7] 306 | * CanvasRenderingContext2D.clip: 2 optional [of 2] 307 | * JSON.stringify: 2 optional [of 3] 308 | * IDBObjectStore.openCursor: 2 optional [of 2] 309 | * HTMLMediaElement.addTextTrack: 2 optional [of 3] 310 | * Date.setUTCMinutes: 2 optional [of 3] 311 | * CanvasRenderingContext2D.fill: 2 optional [of 2] 312 | * Document.createNodeIterator: 2 optional [of 3] 313 | * WebGLRenderingContext.texSubImage2D: 2 optional [of 9] 314 | * Date.setUTCFullYear: 2 optional [of 3] 315 | * Date.setMinutes: 2 optional [of 3] 316 | * Date.setFullYear: 2 optional [of 3] 317 | * WebSocket.close: 2 optional [of 2] 318 | * Document.execCommand: 2 optional [of 3] 319 | * Path2D.addPathByStrokingText: 2 optional [of 6] 320 | * Path2D.addText: 2 optional [of 6] 321 | * Document.createTreeWalker: 2 optional [of 3] 322 | * Path2D.arcTo: 2 optional [of 7] 323 | * IDBKeyRange.bound: 2 optional [of 4] --------------------------------------------------------------------------------