I'm getting deja vu again On Tue, 19 Mar 2019 at 14:31 Isiah Meadows <[email protected]> wrote:
> UX workflows aren't all of JS. Classes exist for many more reasons than > that, and 99% of my classes are for abstracting non-trivial business logic > and ensuring *those* are easily testable, not directly UI/UX. > > ----- > > Isiah Meadows > [email protected] > www.isiahmeadows.com > > > On Sun, Mar 17, 2019 at 2:35 AM kai zhu <[email protected]> wrote: > >> *rant warning* >> >> -1 because classes generally have little-value in UX-workflow programming. >> >> the example class RequestManager (given in this discussion), >> realistically has little reusability-value -- its no better than employing >> a throwaway static-function (both are equally likely to get rewritten each >> time UX-workflow features are added.). >> >> for example, a common feature-request is adding visual-progress-bar. >> there is no "simple" way to extend RequestManager to do this, other than >> significant-refactoring of the base-class (and risk breaking >> class-dependencies downstream). >> >> some other common UX feature-requests that would likely invalidate >> "reusability" of your class-based design include: >> >> 1. needing to upload a binary-file (social-images, receipt-signatures, >> screenshots, etc...) >> 2. needing to upload multiple binary-files in parallel (keeping track of >> timeouts of each) >> 3. needing to upload multiple binary-files in parallel (keeping track of >> timeouts of each), >> and then download their thumbnail-previews from server to visually >> confirm uploads were correct >> 4. needing to make parallel http-requests from 3rd-party sources and >> "joining" the response-data >> 5. needing the sign-up-page to additionally pre-validate >> email / username / mobile-number / credit-card / etc... before >> form-submission to server >> >> many frontend-engineers with experience "extending" products with >> additional UX-workflow features, know its rarely as simple as modifying >> some class-methods and be done with it -- it oftentimes require rewriting >> nearly every-piece-of-code that touches the given workflow needing >> enhancement. >> >> p.s. -- here's a "simple" fully-working UX-example [1] on how to add >> visual-progress-bar to http-requests. if i had to additionally add some of >> the other UX-features mentioned above, it would likely entail me completely >> rewriting the throwaway static-function, rather than waste time trying to >> extend it. >> >> [1] https://jsfiddle.net/kaizhu256/t9ubdenf/ >> >> ```html >> <style> >> /* jslint utility2:true */ >> /* csslint ignore:start */ >> *, >> *:after, >> *:before { >> box-sizing: border-box; >> } >> /* csslint ignore:end */ >> body { >> background: #eee; >> font-family: Arial, Helvetica, sans-serif; >> font-size: small; >> } >> input { >> width: 100%; >> } >> textarea { >> font-family: Consolas, Menlo, monospace; >> font-size: smaller; >> overflow: auto; >> width: 100%; >> } >> </style> >> >> <div id="ajaxProgressDiv1" style="background: #d00; height: 5px; left: 0; >> margin: 0; padding: 0; position: fixed; top: 0; transition: background >> 500ms, width 1500ms; width: 0%; z-index: 1;"></div> >> >> <label>ajax-request</label><br> >> <textarea id="input1" style="height: 10rem;">{ >> "method": "GET", >> "url": "https://api.github.com/orgs/octokit/repos", >> "headers": { >> "accept": "application/vnd.github.v3+json" >> }, >> "data": "hello world!" >> }</textarea><br><br> >> >> <button id="submit1">submit ajax-request</button><br><br> >> >> <label>ajax-response</label><br> >> <textarea id="output1" style="height: 20rem;"></textarea><br><br> >> >> <script> >> /*jslint browser*/ >> (function () { >> "use strict"; >> var local; >> local = {}; >> window.local = local; >> >> local.ajax = function (opt, onError) { >> /* >> * simple, throwaway ajax-function that can be easily rewritten >> * to accomodate new [async] ux-features >> */ >> var resHandler; >> var xhr; >> opt.headers = opt.headers || {}; >> opt.method = opt.method || "GET"; >> xhr = new XMLHttpRequest(); >> // open url >> xhr.open(opt.method, opt.url); >> // set req-headers >> Object.entries(opt.headers).forEach(function (entry) { >> xhr.setRequestHeader(entry[0], entry[1]); >> }); >> // send data >> xhr.send(opt.data); >> // init request-handling >> resHandler = function (evt) { >> /* >> * this function will handle ajax-response >> */ >> switch (evt.type) { >> case "abort": >> case "error": >> // decrement ajaxProgressCounter >> local.ajaxProgressCounter = Math.max( >> local.ajaxProgressCounter - 1, >> 0 >> ); >> onError(new Error(evt.type), xhr); >> break; >> case "load": >> // decrement ajaxProgressCounter >> local.ajaxProgressCounter = Math.max( >> local.ajaxProgressCounter - 1, >> 0 >> ); >> onError(null, xhr); >> break; >> } >> // increment ajax-progress-bar >> local.ajaxProgressUpdate(); >> }; >> // increment ajaxProgressCounter >> local.ajaxProgressCounter = local.ajaxProgressCounter || 0; >> local.ajaxProgressCounter += 1; >> // increment ajax-progress-bar >> local.ajaxProgressUpdate(); >> xhr.addEventListener("abort", resHandler); >> xhr.addEventListener("error", resHandler); >> xhr.addEventListener("load", resHandler); >> xhr.addEventListener("loadstart", resHandler); >> xhr.addEventListener("progress", resHandler); >> }; >> >> local.ajaxProgressUpdate = function () { >> /* >> * this function will update ajax-progress-bar >> */ >> var ajaxProgressDiv1; >> // init state >> local.ajaxProgressCounter = local.ajaxProgressCounter || 0; >> local.ajaxProgressState = local.ajaxProgressState || 0; >> ajaxProgressDiv1 = document.querySelector( >> "#ajaxProgressDiv1" >> ); >> // init ajaxProgressDiv1StyleBackground >> local.ajaxProgressDiv1StyleBackground = ( >> local.ajaxProgressDiv1StyleBackground >> || ajaxProgressDiv1.style.background >> ); >> // show ajaxProgress >> ajaxProgressDiv1.style.background = ( >> local.ajaxProgressDiv1StyleBackground >> ); >> // increment ajaxProgress >> if (local.ajaxProgressCounter > 0) { >> local.timerIntervalAjaxProgressHide = ( >> local.timerIntervalAjaxProgressHide >> || setInterval(local.ajaxProgressUpdate, 2000) >> ); >> // this algorithm will indefinitely increment ajaxProgressBar >> // with successively smaller increments without ever reaching >> 100% >> if ((ajaxProgressDiv1.style.width.slice(0, -1) | 0) > 95) { >> ajaxProgressDiv1.style.width = "0%"; >> local.ajaxProgressState = 0; >> } >> local.ajaxProgressState += 1; >> ajaxProgressDiv1.style.width = Math.max( >> 100 - 75 * Math.exp(-0.125 * local.ajaxProgressState), >> ajaxProgressDiv1.style.width.slice(0, -1) | 0 >> ) + "%"; >> } else { >> // finish ajaxProgress >> ajaxProgressDiv1.style.width = "100%"; >> } >> // cleanup timerTimeout >> clearTimeout(local.timerTimeoutAjaxProgressHide); >> // hide ajaxProgress >> local.timerTimeoutAjaxProgressHide = setTimeout(function () { >> ajaxProgressDiv1.style.background = "transparent"; >> local.ajaxProgressCounter = 0; >> local.ajaxProgressState = 0; >> // reset ajaxProgress >> clearInterval(local.timerIntervalAjaxProgressHide); >> local.timerIntervalAjaxProgressHide = null; >> setTimeout(function () { >> if (!local.ajaxProgressState) { >> ajaxProgressDiv1.style.width = "0%"; >> } >> }, 500); >> }, ( >> local.ajaxProgressCounter > 0 >> ? local.timeoutDefault >> : 1000 >> )); >> }; >> >> // init event-handling >> document.querySelector( >> "#submit1" >> ).addEventListener("click", function () { >> var output1; >> output1 = document.querySelector( >> "#output1" >> ); >> // reset #output1 >> output1.innerHTML = ""; >> try { >> local.ajax(JSON.parse(document.querySelector( >> "#input1" >> ).textContent), function (err, xhr) { >> // handler err >> if (err) { >> output1.textContent = err.stack; >> return; >> } >> // output response-headers >> output1.textContent = ( >> "status-code:\n" >> + xhr.status >> + "\n\nresponse-headers:\n" >> + xhr.getAllResponseHeaders() >> + "\n\nresponse-text:\n" >> + xhr.responseText >> ); >> }); >> // handler errCaught >> } catch (errCaught) { >> output1.textContent = errCaught.stack; >> } >> }); >> }()); >> </script> >> ``` >> >> >> On 12 Mar 2019, at 15:49, guest271314 <[email protected]> wrote: >> >> ```class RequestManager { >> __THIS__(method) { >> return new Proxy(method, { >> apply: (target, thisArg, argumentsList) => { >> return method.apply(thisArg, [...argumentsList, { >> THIS: this >> }]) >> } >> }) >> } >> constructor() { >> this.successMessage = "Xhr successful."; >> } >> makeRequest() { >> var oReq = new XMLHttpRequest(); >> oReq.addEventListener("load", this.__THIS__(this.responseHandler)); >> oReq.open("GET", "data:,"); >> oReq.send(); >> } >> >> responseHandler(e, {THIS} = {}) { >> console.log(this, e); >> window.alert(THIS.successMessage) >> } >> } >> >> var reqManager = new RequestManager(); >> >> reqManager.makeRequest();``` >> >> On Mon, Mar 11, 2019 at 8:59 AM john larson <[email protected]> >> wrote: >> >>> First of all, thank you all for taking the time to review the proposal >>> and sharing your valuable opinions. I would like to note that the proposal >>> aims not to add a new capability that was not possible to do before but >>> rather move the standard forward on the way for a modern, better and easier >>> to use language for all the developers. Advancements of the language, or >>> any language in that matter, throughout the last decade also followed a >>> similar path because we were already able to do everything in one way or >>> the other. We actually strive for the same thing, a better Javascript. >>> >>> >>> That being said, let me try to clarify my proposal further by walking >>> you through my thought process: >>> >>> >>> Normally if I try to write a class similar to the sample code I have >>> given in my first email using an object oriented programming language like >>> Java, C# etc., I would be writing something similar to the following: >>> >>> class RequestManager >>> { >>> string successMessage = "Xhr successful."; >>> >>> >>> void makeRequest() >>> { >>> var oReq = new XMLHttpRequest(); >>> oReq.addEventListener("load", responseHandler); >>> oReq.open("GET", "*www.google.com*"); >>> oReq.send(); >>> } >>> >>> >>> void responseHandler() >>> { >>> window.alert(successMessage); >>> } >>> >>> } >>> >>> >>> As you can see, I do not even have to use a special keyword for >>> referring to methods from inside the class. Because they are already in >>> lexical scope. Now, if this can be accomplished in Javascript without >>> hitting some limitation/restriction due to the current state of the >>> language, I think it would be the ideal solution. (This limitation might be >>> the class syntax being just a syntactical sugar or some other reason that >>> I cannot foresee right now and that would require a breaking change.) And >>> I would happily change the proposal that way: “A no-keyword alternative for >>> the “this””. If I should summarize this approach, I can say that every >>> method of the class is going to assume the behavior we now have with arrow >>> functions, but without requiring the use of the “this” and the arrow >>> function syntax. >>> >>> >>> As contrary to the ideal solution, the last thing I would want would be >>> to use a context-dependant keyword like the “this” to refer to >>> methods/properties of the object and then try to set the context right by >>> using binding or arrow functions. This referral should be lexical, not >>> context-dependant. If I have the intent of referring to the instance >>> method/property, that intent should manifest itself right there where I am >>> using this method/property. I shouldn’t be looking at if this takes place >>> inside an arrow function, or if the enclosing method is called with a >>> binding or not. Why should I care about the enclosing of the call, >>> right? >>> >>> By the way, MDN also mentions the following about the use of arrow >>> functions: “Arrow function expressions are ill suited as methods”. >>> >>> *@Yulia:* Thanks for pointing out the decorator approach. But that also >>> seems to deal with the enclosing and tries to solve the problem with a >>> “context binding” approach. The only difference is the way it determines >>> the binding. I am against this binding approach all together. Only the >>> lexical scope of the code should be taken into consideration. >>> >>> >>> >>> So far, I have laid out what I think the ideal solution is and what I >>> think the problematic state we are in right now. And as a middle-ground, >>> in case the ideal solution cannot be applied, I proposed a new keyword to >>> use instead of the “this” so that it will always refer to the instance, >>> regardless of execution context binding. In which case, when you >>> replace the “this” in the problematic sample code in my initial email, it >>> will work just fine. Let’ assume for the sake of this example that the new >>> keyword is “self”: >>> >>> class RequestManager{ >>> >>> >>> constructor(){ >>> *self*.successMessage = "Xhr successful."; >>> } >>> >>> >>> >>> >>> makeRequest() { >>> var oReq = new XMLHttpRequest(); >>> oReq.addEventListener("load", *self*.responseHandler); >>> oReq.open("GET", "*www.google.com*"); >>> oReq.send(); >>> } >>> >>> >>> responseHandler() { >>> window.alert(*self*.successMessage); >>> } >>> } >>> >>> >>> var reqManager = new RequestManager(); >>> reqManager.makeRequest(); >>> >>> >>> As you can see, self.responseHandler will always point to the >>> responseHandler method no matter whether the enclosing is a method, an >>> arrow function or if it is called using a bind syntax or not. >>> >>> I would be happy to further address your concerns about this explanation >>> if you have any. >>> >>> >>> >>> On Sun, Mar 10, 2019 at 10:30 PM guest271314 <[email protected]> >>> wrote: >>> >>>> This is probably not the pattern that is being proposed though outputs >>>> the expected result >>>> >>>> ```class RequestManager { >>>> constructor() { >>>> this.successMessage = "Xhr successful."; >>>> RequestManager.THIS = this; >>>> } >>>> >>>> makeRequest() { >>>> var oReq = new XMLHttpRequest(); >>>> oReq.addEventListener("load", this.responseHandler); >>>> oReq.open("GET", ""); >>>> oReq.send(); >>>> } >>>> >>>> responseHandler(e) { >>>> console.log(e, this); // `e`: event, `this`: XMLHttpRequest >>>> instance >>>> console.log(RequestManager.THIS.successMessage); >>>> } >>>> >>>> } >>>> >>>> var reqManager = new RequestManager(); >>>> >>>> reqManager.makeRequest();``` >>>> >>>> On Sat, Mar 9, 2019 at 11:42 AM john larson <[email protected]> >>>> wrote: >>>> >>>>> *Summary of the problem:* >>>>> >>>>> “this” keyword in Javascript is context dependent. And this is one of >>>>> the culprits of most subtle and latent errors in Javascript. Moreover, use >>>>> of “this” cannot be avoided if we are using classes and trying to >>>>> reference >>>>> instance properties. >>>>> >>>>> When “this” is used in callback functions or in functions given >>>>> to forEach as argument, IDEs rightfully cannot raise any design-time >>>>> errors, giving developers the false sense of security, but we get run-time >>>>> errors because “this” is undefined. >>>>> >>>>> There seem to be two work-arounds: >>>>> 1. Using arrow functions >>>>> >>>>> 2. Using .bind(this) syntax >>>>> >>>>> Just assuming we forgot to use an arrow function or a .bind(), the IDE >>>>> will not be able to raise an error and we will encounter the error in >>>>> run-time. >>>>> >>>>> >>>>> *What I propose:* >>>>> >>>>> I am proposing a new keyword that will be the alternative of "this" >>>>> and will always point to the instance of the class. The name of the new >>>>> keyword can be chosen with consensus from the community such that it would >>>>> minimize/eliminate collision in existing codebases. >>>>> >>>>> >>>>> Here is a sample js code: >>>>> >>>>> >>>>> class RequestManager{ >>>>> >>>>> >>>>> >>>>> constructor(){ >>>>> >>>>> this.successMessage = "Xhr successful."; >>>>> >>>>> } >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> makeRequest() { >>>>> >>>>> var oReq = new XMLHttpRequest(); >>>>> >>>>> oReq.addEventListener("load", this.responseHandler); >>>>> >>>>> oReq.open("GET", "www.google.com"); >>>>> >>>>> oReq.send(); >>>>> >>>>> } >>>>> >>>>> >>>>> >>>>> responseHandler() { >>>>> >>>>> window.alert(this.successMessage); >>>>> >>>>> } >>>>> >>>>> } >>>>> >>>>> >>>>> >>>>> var reqManager = new RequestManager(); >>>>> >>>>> reqManager.makeRequest(); >>>>> >>>>> >>>>> >>>>> This piece of code will alert “undefined” because “this” is undefined >>>>> in the callback function in strict mode. >>>>> >>>>> Now let’s assume a new keyword is used insetead of “this” that will >>>>> always point to the class instance. >>>>> >>>>> As per its implementation, as described on >>>>> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes >>>>> : >>>>> >>>>> *“JavaScript classes, introduced in ECMAScript 2015, are primarily >>>>> syntactical sugar over JavaScript's existing prototype-based inheritance. >>>>> The class syntax does not introduce a new object-oriented inheritance >>>>> model >>>>> to JavaScript.”* >>>>> >>>>> So with the new keyword introduced, behind the scenes, previous class >>>>> could be interpreted as a piece of code along the lines of: >>>>> >>>>> >>>>> var RequestManager = function () { >>>>> >>>>> var self = this; >>>>> >>>>> self.successMessage = "Xhr successful"; >>>>> >>>>> >>>>> >>>>> self.makeRequest = function () { >>>>> >>>>> var oReq = new XMLHttpRequest(); >>>>> >>>>> oReq.addEventListener("load", responseHandler); >>>>> >>>>> oReq.open("GET", "www.google.com"); >>>>> >>>>> oReq.send(); >>>>> >>>>> }; >>>>> >>>>> >>>>> >>>>> var responseHandler = function () { >>>>> >>>>> window.alert(self.successMessage); >>>>> >>>>> }; >>>>> >>>>> }; >>>>> >>>>> var reqManager = new RequestManager(); >>>>> >>>>> >>>>> >>>>> reqManager.makeRequest(); >>>>> >>>>> >>>>> >>>>> I believe this way, we would not have to resort to work-arounds for >>>>> such a fundamental construct of the language and this would ease >>>>> developers’ lives as someone forgetting to have used an arrow function or >>>>> the .bind(this) syntax will not be a problem anymore. >>>>> >>>>> >>>>> Best Regards, >>>>> >>>>> John >>>>> >>>>> >>>>> <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=icon> >>>>> Virus-free. >>>>> www.avast.com >>>>> <https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=link> >>>>> _______________________________________________ >>>>> es-discuss mailing list >>>>> [email protected] >>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>> >>>> _______________________________________________ >> es-discuss mailing list >> [email protected] >> https://mail.mozilla.org/listinfo/es-discuss >> >> >> _______________________________________________ >> es-discuss mailing list >> [email protected] >> https://mail.mozilla.org/listinfo/es-discuss >> > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

