Function.bind.bind does not work in JavaScript
Akash Kava

Akash Kava @akashkava

About: Author of Web Atoms

Location:
India
Joined:
Jan 6, 2018

Function.bind.bind does not work in JavaScript

Publish Date: Feb 1 '19
13 19

To my surprise, I wrote the code,

function printThis() {
   console.log(this);
}

const f = printThis.bind(5).bind(7);
f(); // prints 5 instead of 7

f.call(9); // still prints 5

Enter fullscreen mode Exit fullscreen mode

This creates a problem when a function is already bound in event handlers and can cause nightmares if some function did this and broke the library. Ideally it should either thrown an error or there should be a way to detect if function is already bound.

So how will I know if function is already bound?

Though this is a good interview question, a part of JS I didn't know.

Comments 19 total

  • Davut KARA
    Davut KARAFeb 1, 2019

    When you use bind it does not run your function just it sets the data 5,

    So, printThis.bind(5) set 5 to 'this' then it does not return the exact function becuse it returns prototype undefined.

    const l = printThis.bind(5).prototype
    console.log(l) // undefined
    

    So you cannot use again, you can think like a constant variable, it's set one time then you can not set again.

    As a result, I write a function rbind means 're bind'.
    It simply run the function first then returns the function which is not changed.

    Function.prototype.rbind = function (o){
      this.bind(o)() // set and call
      return this;
    }
    
    function printThis() {
       console.log(this);
       return this
    }
    
    const f = printThis.rbind(5).rbind(7);
    f(); // prints 5, 7
    
    f.call(9); // prints 9
    
    • Akash Kava
      Akash KavaFeb 1, 2019

      That is interesting, I can check if prototype is undefined, that means function is already bound.

      • George Jempty
        George JemptyFeb 1, 2019

        No it doesn't mean that necessarily. Somebody could explicitly set the prototype to undefined for instance.

        • Andrei Gatej
          Andrei GatejFeb 14, 2019

          So if .prototype is already undefined it means that the function cannot be chained, right?

  • George Jempty
    George JemptyFeb 1, 2019

    This is a HORRIBLE interview question, not once in 20 years have I ever had to address anything like this.

    • Akash Kava
      Akash KavaFeb 1, 2019

      Doesn't that make it interesting? Nobody writes such thing, but when you are in chains of method calls and wondering what is wrong and why this isn't what it supposed to be, knowing advanced stuff helps in fixing issues easily. I found this when I was trying to optimize event handlers and I used bind without knowing it's side effects.

      • George Jempty
        George JemptyFeb 1, 2019

        Interesting, and useful in an interview for discerning Javascript aptitude, are two entirely separate things. Nobody would know this off the top of their head without digging. You (nor I) didn't even know, somebody else had to point out why. Better to stick to converting strings to numbers and such, to weed out the ones who have absolutely no idea how to do the simple things, rather than trying to find ones who know incredibly esoteric stuff. Once you've weeded out the truly awful, try to find who's intermediate and who's advanced, by asking PRACTICAL intermediate/advanced questions, not patently esoteric stuff.

    • JavaScript Joel
      JavaScript JoelFeb 1, 2019

      To play devil's advocate, I will disagree with this statement.

      While you will never run across code that looks like this:

      printThis.bind(5).bind(7);
      
      Enter fullscreen mode Exit fullscreen mode

      It is very possible (#1) you might run across a function that has been previously bound. And when you try to rebind this function, you might not be aware of why you bind statement is not working.

      It is also very possible (#2) that you need to return someone a bound function from a library you create. Not knowing that you are preventing someone down the road from rebinding that function.

      Being able to bind a context in JavaScript is a very powerful tool.

      const list = { 0: 'first', 1: 'second', 2: 'third', length: 3 };
      [].map.call(list, o => o.toUpperCase()); //=> [ 'FIRST', 'SECOND', 'THIRD' ]
      //       \
      //         I can bind []'s `this` to `list`
      
      Enter fullscreen mode Exit fullscreen mode

      First let's imagine you have an object that uses this. Because I am unimaginative, I'll create this contrived example:

      // Contrived example just to demo `this`
      const MyArray = {
        0: 'first', 1: 'second', 2: 'third', length: 3,
        map(func) {
          const value = []
          for (let i = 0; i < this.length; i++) {
            value.push(func(this[i]))
          }
          return value
        }
      }
      
      Enter fullscreen mode Exit fullscreen mode

      and it works great!

      // 👍 This works great!
      MyArray.map(o => o.toUpperCase()) //=> [ 'FIRST', 'SECOND', 'THIRD' ]
      
      Enter fullscreen mode Exit fullscreen mode

      But we have a different use case (for reasons) that doesn't work.

      // 😢 This does not work!
      const map = MyArray.map
      map(o => o.toUpperCase()) //=> []
      
      Enter fullscreen mode Exit fullscreen mode

      This code doesn't work because calling map will use global as your this.

      So you add this creative line to keep map always bound to MyArray:

      const MyArray = {
        0: 'first', 1: 'second', 2: 'third', length: 3,
        map(func) {
          const value = []
          for (let i = 0; i < this.length; i++) {
            value.push(func(this[i]))
          }
          return value
        }
      }
      MyArray.map = MyArray.map.bind(MyArray)
      
      Enter fullscreen mode Exit fullscreen mode

      Making this code work as expected...

      // 👍 This works great!
      const map = MyArray.map
      map(o => o.toUpperCase()) //=> [ 'FIRST', 'SECOND', 'THIRD' ]
      
      Enter fullscreen mode Exit fullscreen mode

      But what you didn't know is you unknowingly created a bug somewhere else in your application:

      // 😢 This stopped working!
      const map = MyArray.map
      
      map.call([ 'left', 'middle', 'last' ], o => o.toUpperCase())
      //=> [ 'FIRST', 'SECOND', 'THIRD' ]
      //   ------------------------------
      //                                  \
      //                                    ?!?! 😵 WHAT 😵 ?!?!
      
      Enter fullscreen mode Exit fullscreen mode

      So to summarize:

      Knowing why this happens is important. Once a function is bound, all future bindings will be ignored. So you must always bind with care.

      Or you can do what I do and that is write JavaScript 100% free of this ;)

      Rethinking JavaScript: The complete elimination and eradication of JavaScript's this.

      • Akash Kava
        Akash KavaFeb 1, 2019

        Of course, no one would write code in f.bind(x).bind(y) point was that behavior was unpredictable. I was trying to optimize event handlers and I accidentally used bind on bound function and found out that I shouldn't have used bind.

        this is powerful, I know lot of people talking about not using this, but it is just unnecessary complicated syntax for no real benefit.

        • JavaScript Joel
          JavaScript JoelFeb 1, 2019

          point was that behavior was unpredictable

          I am in complete agreement!

          I wasn't in disagreement with your article, but the commenter.

          Cheers!

          • George Jempty
            George JemptyFeb 1, 2019

            Why even suggest eliminating/eradicating the use of this then? Are there any true Javascript experts recommending this? Douglas Crockford, John Resig, somebody at THAT level? John Resig even uses with as deemed necessary (Javascript templates, anyone?), despite everybody parroting howls of derision. There's no sense throwing a good tool such as this out, like a baby with the bath-water.

            • JavaScript Joel
              JavaScript JoelFeb 2, 2019

              Are there any true Javascript experts recommending this?

              Why does someone of influence have to recommend it for you to become a follower?

              There's no sense throwing a good tool such as this out, like a baby with the bath-water.

              There is no baby/bathwater. this is a cancer that needs to be excised.

              this in other languages like C# has no problems. But this in JavaScript much more complex in comparison for people to reason about. People come from other languages expecting Class to behave in a similar way to their language... and it doesn't.

              this is fairly complex to understand completely, which is demonstrated (in a small part) by this article and many many other articles.

              In JavaScript, this is nothing more than an optional argument. So if you need this argument, why not just supply it as an argument?

              You don't have to ever worry about what this is if you never use it ;)

              Never type console.log(this) ever again.

              • George Jempty
                George JemptyFeb 2, 2019

                I've heard of eating your own dogfood before, but not drinking your own Kool-Aid. Seriously I've had a look at "nothis". It's verbose, it's implicitly magical, all it does is replace this with ctx, etc. These are not formulas for success in my opinion.

                • JavaScript Joel
                  JavaScript JoelFeb 2, 2019

                  It promotes the context from the side loaded this to a full fledged variable.

                  This behaves in a way similar to ReasonReact.

                  It is used as a last resort for when you must use this.

                  The first and best option is to write your software without this.

                • Akash Kava
                  Akash KavaFeb 3, 2019

                  If this in JavaScript would be same as in other language, why would someone use JavaScript? Power of JavaScript is due to this, otherwise, all nothis and such things are also possible in other languages as well with some tweaks. There is no fun in not using this.

                  this is cancer only for you, not for everyone !! And it will not become cancer for everyone by shouting it out loud.

                  • JavaScript Joel
                    JavaScript JoelFeb 4, 2019

                    Power of JavaScript is due to this

                    Disagree. The power it JavaScript is it runs almost everywhere. It's power is also in it's flexibility on how you like to code. this causes more problems than it solves.

                    this is cancer only for you

                    False. Read stack overflow. Google. You'll find many thousands of posts with people having problems with this.

                    • George Jempty
                      George JemptyFeb 15, 2019

                      Just because thousands of programmers have a hard time reasoning about threads in Java, should they be eliminated?

                      • JavaScript Joel
                        JavaScript JoelFeb 15, 2019

                        If there are roads to take and one causes less flat tires, which road would you choose to take?

  • Mihail Malo
    Mihail MaloFeb 4, 2019

    Ignoring binding arguments, just .bind(thisArg) can be visualized as following:

    Function.prototype.bind = function bind(thisArg) {
      return (...args) => this.apply(thisArg, args)
    }
    

    Here it is clear that the returned function doesn't care about this.

    Same thing, without arrow function:

    Function.prototype.bind = function bind(thisArg) {
      const self = this
      return function(...args) { // could have access to `this`, but doesn't make use of it.
        return self.apply(thisArg, args)
      }
    }
    
Add comment