The Function javascript. prototye. bind

  • 2020-06-19 09:49:27
  • OfStack

Function binding (Function binding) is probably the least of your concerns when you start using JavaScript, but when you realize that you need a solution to how to maintain this context in another function, what you really need is Function.prototype.bind (), you may still not realize it.

The first time you encounter this problem, you may prefer to set this to a variable so that you can refer to it after changing the context. Many people choose to use self, _this, or context as variable names (others use that). These are all useful, and there's nothing wrong with them. But there are better, more specialized ways.

What are the real problems we need to solve?
In the following example code, we can legitimately cache the context into a variable:


var myObj = {
 
  specialFunction: function () {
 
  },
 
  anotherSpecialFunction: function () {
 
  },
 
  getAsyncData: function (cb) {
    cb();
  },
 
  render: function () {
    var that = this;
    this.getAsyncData(function () {
      that.specialFunction();
      that.anotherSpecialFunction();
    });
  }
};
 
myObj.render();

If we simply called the method using this.specialFunction (), we would get the following error:

Uncaught TypeError: Object [object global] has no method 'specialFunction'
We need to keep a reference to the myObj object context for the execution of the callback function. Calling that.specialFunction () allows us to maintain scope context and execute our function correctly. However, Function. prototype. bind() can be used in a more concise and clean way:


render: function () {
 
  this.getAsyncData(function () {
 
    this.specialFunction();
 
    this.anotherSpecialFunction();
 
  }.bind(this));
 
}

What did we just do?
.bind () creates a function whose this keyword is set to the value passed in when the function is called (in this case, the parameter passed in when calling bind()). So, we pass in the context that we want, this, to the.bind () function. Then, when the callback function is executed, this points to the myObj object.

If you're interested in what and how ministers in Function.prototype.bind () work, here's a very simple example:


Function.prototype.bind = function (scope) {
  var fn = this;
  return function () {
    return fn.apply(scope);
  };
}

There is also a very simple use case:


var foo = {
  x: 3
}
 
var bar = function(){
  console.log(this.x);
}
 
bar(); 
// undefined
 
var boundFunc = bar.bind(foo);
 
boundFunc(); 
// 3

We create a new function that, when executed, will have its this set to foo -- not the global scope we had when we called bar().

Browser support
Browser Version support
Chrome 7
Firefox (Gecko) 4.0 (2)
Internet Explorer 9
Opera 11.60
Safari 5.1.4
As you can see, Function.prototype.bind is unfortunately not supported in IE8 and below, so if you don't have a backup plan, you may run into problems.

Fortunately, Mozilla Developer Network (a great repository) provides a foolproof alternative for browsers that do not implement their own.bind () method:


if (!Function.prototype.bind) {
 Function.prototype.bind = function (oThis) {
  if (typeof this !== "function") {
   
// closest thing possible to the ECMAScript 5 internal IsCallable function
   throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
  }
 
  var aArgs = Array.prototype.slice.call(arguments, 1), 
    fToBind = this, 
    fNOP = function () {},
    fBound = function () {
     return fToBind.apply(this instanceof fNOP && oThis
                 ? this
                 : oThis,
                aArgs.concat(Array.prototype.slice.call(arguments)));
    };
 
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
 
  return fBound;
 };
}

Applicable model

When learning a technical point, I find it useful not only to thoroughly study and understand the concept, but also to see if there is a place for it or something close to it in the work at hand. I hope that some of the following examples apply to your code or solve the problem you are facing.

CLICK HANDLERS (click handler)
One purpose is to record a click event (or to perform an action after a click), which may require storing information in an object, such as:


var logger = {
  x: 0,    
  updateCount: function(){
    this.x++;
    console.log(this.x);
  }
}

We might specify the click handler as follows and then call the updateCount() method in the logger object.


document.querySelector('button').addEventListener('click', function(){
  logger.updateCount();
});

But we have to create an extra anonymous function to ensure that the this keyword in the updateCount() function has the correct value.

We can use the following cleaner ways:

document.querySelector('button').addEventListener('click', logger.updateCount.bind(logger));
We cleverly used the convenient.bind () function to create a new function and bind its scope to the logger object.

SETTIMEOUT
If you have used a template engine (such as Handlebars) or in particular some of the MV* frameworks (from my experience I can only talk about ES119en.js), then you probably know the issues discussed below when accessing a new DOM node immediately after rendering a template.

Suppose we want to instantiate an jQuery plug-in:


var myView = {
 
  template: '/* 1 containing  <select />  Template string */',
 
  $el: $('#content'),
 
  afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    this.afterRender();
  }
}
 
myView.render();

You may find that it works -- but not every time, because there are problems. It's a competitive problem: only those who arrive first win. Sometimes rendering comes first, and sometimes plug-in instantiation comes first. [Note: If the rendering process is not complete (DOM Node has not been added to the DOM tree), then find(' select') will not be able to find the appropriate node to instantiate.]

Now, perhaps not widely known, we can use slight hack based on setTimeout() to solve the problem.

Let's rewrite our code a little bit and securely instantiate our jQuery plug-in just after the DOM node is loaded:


afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    setTimeout(this.afterRender, 0);    
  }

However, we get an error message that the function.afterRender () cannot find.

What we're going to do next is use.bind () in our code:


//
 
  afterRender: function () {
    this.$el.find('select').myPlugin();
  },
 
  render: function () {
    this.$el.html(this.template());
    setTimeout(this.afterRender.bind(this), 0);    
  }
 
//

This is the end of this article, I hope you enjoy it.


Related articles: