Hacker News new | past | comments | ask | show | jobs | submit login
ECMAScript bind operator proposal (github.com/tc39)
107 points by networked on Dec 3, 2016 | hide | past | favorite | 46 comments



The "this" keyword in JS has very bad characteristics and is full of traps.

I really wish we could stop building on top of this abomination (fake ES6 classes, binding, etc)

The |> operator (chaining of regular functions) would be so much better to program with. We don't need both.

https://github.com/mindeavor/es-pipeline-operator


It's a pipe dream though. I mean, I wish an omnipotent, omni-benevolent dictator would force everyone to stop using `this` and refactor it out of all existing code as much as the next guy, but until that happens, the best path forward is to pave the cow paths and make the ugly parts easier to deal with.


Its just not taught correctly. `this` is the hidden function argument passed by putting it left of the dot; from that, everything else follows.


Which is, of course, the same way `this` or `self` works in virtually every OO language.

I honestly thought this was common knowledge, and not that hard to grasp. How else is `this` being taught?


> I honestly thought this was common knowledge

For whatever reason it isn't common knowledge in JS-land.

> How else is `this` being taught?

Sadly it very seldom is taught this way. It tends to be a realization people come to after using the language for a while [1].

[1] https://twitter.com/greim/status/778793044174704640


The difference is the JS has late binding.

  myObj.myMethod();
Has different semantics than

  var x = myObj.myMethod; 
  x();
Which causes code like:

  myList.map(myObj.myMethod);
to behave somewhat unintuitively.


Your examples don't need late binding to have different semantics — dynamic dispatch will suffice. That's not different from any other OO language (except perhaps in C++ for non-virtual methods). That's how we get polymorphism out of subtypes, after all: methods are resolved based on the dynamic value of the receiver at runtime, yielding single dynamic dispatch. Regardless of language, the `this` or `self` reference within a method always semantically behaves as if it were an additional parameter (except in typed languages where the variance under the subtyping relation is reversed, iirc), and is often implemented as such, albeit usually implicitly.

Of course, late binding implies dynamic dispatch, but the reverse isn't true. On the other hand, almost every untyped OO language uses late binding (I can't even think of one that doesn't, off the top of my head...), as do some typed languages (those that target the .NET CLR, for example), so even with your stronger assertion JS far from unique in that regard.

That behavior is standard fare for OO. If anyone finds it tricky, I find it more likely that the struggling programmer has yet to internalize OO than that JS is doing something strange (in this particular case, I mean... JS does plenty of strange shit otherwise ;) ).


The difference is that JS has no binding, yet it still allows for first class methods. Thus the OO "illusion" (object owns the methods) is broken: there are no methods, just functions that take an argument named `this`, in a slightly weird way, only when attached to records.


> The difference is that JS has no binding ... just functions that take an argument named `this`

Associating a name with a value is precisely what binding is.

Frankly, though, I don't understand this comment at all. Care to clarify for me?


In Python

  b = foo.bar
  b()
correctly invokes the bar method of the foo object. In JavaScript, you have to write

  let b = foo.bar.bind(foo)
because foo.bar gives you a function (not a method) that doesn't remember which object it came from.


I'm aware of what you have to write in JS, and I'd argue that that's the behavior that's to be expected. I'm actually more surprised by Python's behavior here. Lua works the same way JS does, as does Common Lisp.


At one point I had memorized the corner cases* of `this` for the pointless reason of stumping people who thought they knew it. The only thing it really taught me was to avoid it.

*My favorite is "what does `this` point to when running `eval()`"? The current context. "What if you assign `eval` to a variable and then use that?" The global context.


With arrow functions released and class properties likely to be approved soon, this has minimal value and is probably not worth the cost of the extra complexity to the language.


I respectfully disagree.

It all has to do with this line: "By providing syntactic sugar for these use cases we will enable a new class of "virtual method" library, which will have usability advantages over the standard adapter patterns in use today."

Essentially, you write functions that provide a certain behavior without being coupled to a specific class, so you can have a 'map' method that is called the same way no matter what you're mapping over.

Right now functional libraries like static-land, ramda, sanctuary, etc. are working hard to provide these functional primitives but it's still painful to write readable code that uses these.

It's also worth pointing out that directly importing methods here is more efficient than importing entire classes if you're not using one of those emerging build systems that does very good tree-shaking.

This is the closest JavaScript is going to get to letting you write your behavior as a suite of functions separately from specific classes—it's like a step removed from typeclasses.


> It all has to do with this line: "By providing syntactic sugar for these use cases we will enable a new class of "virtual method" library, which will have usability advantages over the standard adapter patterns in use today."

There is nothing that this proposal does that cannot be done today. It brings nothing new to the table aside from operator noise. I hope the committee pushes against these exotic features, like the bind operator or the pipe operator.

> Right now functional libraries like static-land, ramda, sanctuary, etc. are working hard to provide these functional primitives but it's still painful to write readable code that uses these.

No it's not. explicit != painful .

Do people really want to end up with a spec of the size of C++ spec ?


"explicit" is in the eye of the beholder. Passing a this argument to any function even if its not attached to the object seems more explicit to me.


Wouldn't a pipeline operator [1] be a more interesting solution for this instead of adding syntax that relies on the `this` keyword?

[1] https://github.com/mindeavor/es-pipeline-operator


No. Pipelining is handy, but not necessary. This binding is a well-known pain point in JS.


Yes! Elm has it and it's very nice.


Even with arrow functions, it's pretty frequent to have to write things like `...on('event', evt => this.onEvent(evt))` where `on('event', ::onEvent)` would be quite nice.


Neat! Extension methods and getting rid of "var that = this;" in one!

It seems that it also extends to full expressions on the right-hand side of ::, so you can even do, for using Array operations over things that look like arrays but not quite:

    document.querySelectorAll('p')::([].map)(f)
instead of:

    [].map.call(document.querySelectorAll('p'), f)
(replace [].map with Array.prototype.map if it feels less gross)


This also works, by the way:

    [...document.querySelectorAll('p')].map(f)
In the future, NodeList could extend Array which would make these workarounds redundant. Extending Array was made possible by ES6.

Anyhow, I think having some kind of extension methods would be great.


> In the future, NodeList could extend Array which would make these workarounds redundant.

It would break the web, unfortunately. (We've experimented with NodeList.prototype.__proto__ being Array.prototype instead of Object.prototype and it breaks sites, and ES6's extends notion implies that.)


Why do that in the callback way at all? Why not simple

    for(const el of document.querySelectorAll('p')) {}
Why have the overhead of a callback to begin with. It reads SO much better as well.


It has different semantics. Your code is equivalent to a `forEach` call, not `map`. Calling `map` returns a new array containing the results of the callback, while a for loop or a `forEach` don't.


I find ".bind(this)" to be preferable over "var that = this" anyway.


Wow, what an improvement! In fact, why don't we just drop keywords altogether and adopt APL-like syntax for better code concision? Here, let me offer an example of how superior APL is:

  (~R∊R∘.×R)/R←1↓ιR


Amongst other things, the bind operator is very useful for React, so instead of having to call this.handleChange.bind(this) you can just call ::this.handlechange. As in, you can type this: <input onChange={::this.handleChange} />. It's a minor thing, but I prefer it a lot more than the explicit bind syntax. There are other cases as well, but since I'm doing a lot of work in React at the moment, having that is really convenient.


I've found using properties (which auto-bind) to be an easier solution.


With getters you can simply do { get handlerFn() { return (e) => ... } }

And then in template <a onClick={this.handlerFn} />


This proposal really deserves the "kill it with fire" label. Adding new operators to do things that are already possible will only serve to make the language more complicated and code written in JS more difficult to understand and maintain. JS's minimalism is its greatest strength.


"Minimalism." Right.

Lua is minimal. Scheme is minimal. Forth is minimal. Hell, Brainfuck is minimal. JS isn't.


Using that argument, you could say that every mathematical operation can boil down to addition and subtraction, because multiplication, exponentiation, and division are just flavors of those two operators. However, they're really convenient and I think that if an operator adds enough convenience it's worth the overhead of a new operator.

You can argue that the new bind operator isn't so convenient compared to the .bind syntax, but your argument is a bit weak.


I don't think Ecma-script needs new operators ,especially the kind of operators that will make it look like C++ , just to save a few key types. There needs to be a balance. A balance between all these tricks and readability.


This. – More important than any language features is the conceptual space of a given language. JS used to be (very) good at this, but is losing terrain lately.


This proposal has been around for a while. I try keeping an open mind, since I've been wrong about many other syntactic features... But I just don't see this being that useful; none of the examples seem particularly compelling.

It's also worth noting that most (?) modern browsers already bind log to console. At least Chrome, Firefox, and Safari all support it. I don't have a Windows machine so I can't confirm if Edge supports it. Same goes for Node.

    [1, 2, 3].forEach(console.log)


> It's also worth noting that most (?) modern browsers already bind log to console.

And it should be in all modern browsers soon[1], as `window.console` is now a namespace: https://console.spec.whatwg.org/#console-namespace

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platfor...


Thinking about new language features in terms of, what percentage of JS developers will understand it and what percentage will be confused by it, there's an argument for just leaving things be.

I would bet 3 years from now less than half of developers will be able to explain :: in an interview. Not that a much higher percentage could explain 'this' but what's done is done.


I don't know, I kinda like ".bind(this)" to bind the current scope to a callback.

  class Vector{
  	constructor(x = 0, y = 0, z = 0){
  		this.x = x;
  		this.y = y;
  		this.z = z;
  		
  		setTimeout(function(){
  			console.log(this.x + ", " + this.y + ", " + this.z);
  		}.bind(this), 10);
  	}
  };
  
  var v = new Vector(1, 2, 3); // prints 1, 2, 3
Edit: Ok I can see this be potentially useful for function chaining, I just don't do that very much.


Your own example is more concisely covered by arrows, which are already available:

  class Vector {
    constructor(x=0, y=0, z=0) {
      this.x = x;
      this.y = y;
      this.z = z;
      setTimeout(() => console.log(this.x + ", " + this.y + ", " + this.z), 10);
    }
  }



Already had some time on the front page on HN today, but I just released https://zaphod.surge.sh/ which uses it.


It also makes Reactive Extensions for JavaScript (RxJS) very nice: https://github.com/ReactiveX/rxjs


Does anyone know the history behind "this"?

Is there any other language that uses "this" to denote "current context"?


There are plenty of languages which have an (implicit or explicit) argument called something like "this" or "self" where the semantics of a x.y() call are "look up y on the x object, call it with x as the this argument". C++ fundamentally works like that (the look-up is compile time in the case of non-virtual functions, but like JS at runtime for virtual functions), Python, Java, etc.

The oddity with JavaScript is two-fold: firstly, the fact that it passes the global object (in non-strict code) given an z() style call (v. x.y() above); secondly, the fact that you can call a function with an explicit this argument using Array.prototype.call or Array.prototype.array.

What feels far more odd than that, though, is what the DOM does. It often acts as if there's been a method call, whereas in reality it's just called with an explicit this argument from C++. If you view a method dispatch as being a message (à la Smalltalk, a large influence on JS) then obviously an event's target is invoked as a message to the event. I obviously can't comment if that's what Brendan was thinking, but it seems like an obvious analogue.


Personally i'd much prefer a currying syntax to a binding syntax.

const add1 = add::1

const createUser = post::('/user', authToken)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: