Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I can't understand how this javascript function works

I was reading the function definition of bind, but I can't 100% understand the code as written:

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                                        ? this                                         : oThis || window,                                      aArgs.concat(Array.prototype.slice.call(arguments)));             };          fNOP.prototype = this.prototype;         fBound.prototype = new fNOP();          return fBound;     }; } 

Specifically, I don't get the purpose of fNOP, and I don't understand why fBound's prototype needs to be set. I am also hung up at the fToBind.apply part (I can't figure out what this represents in this context).

Can someone can explain what is going on here?

like image 762
Davis Dimitriov Avatar asked Jan 10 '12 19:01

Davis Dimitriov


People also ask

How does JavaScript function work?

A function in JavaScript is similar to a procedure—a set of statements that performs a task or calculates a value, but for a procedure to qualify as a function, it should take some input and return an output where there is some obvious relationship between the input and the output.

How do I understand this in JavaScript?

“this” Refers to an Invoker Object (Parent Object) In JavaScript, the property of an object can be a method or a simple value. When an object's method is invoked, then this refers to the object which contains the method being invoked.

What are the 3 types of functions in JavaScript?

There are 3 ways of writing a function in JavaScript: Function Declaration. Function Expression. Arrow Function.

How do you test if a function is working in JavaScript?

Use the typeof operator to check if a function is defined, e.g. typeof myFunction === 'function' . The typeof operator returns a string that indicates the type of a value. If the function is not defined, the typeof operator returns "undefined" and doesn't throw an error.


2 Answers

Well, one reason fBound's prototype needs to be set is so that the result of calling bind on a function has the same prototype as that function. This is also where fNop seems to come in--it lets you set fBound's prototype using new fNop() without calling the original function which may have side effects.

The call to apply lets you both set this in the function and specify additional arguments. Since bind lets you "curry" arguments to the function, you have to combine both the arguments passed in when the function is bound and the arguments it is called with.

like image 175
Tikhon Jelvis Avatar answered Sep 23 '22 19:09

Tikhon Jelvis


It is to make sure

  • (1) the bound function can be used as a constructor, ignoring the binding. (hence the instanceof check)
  • (2) At the same time, you want to make sure that new g() inherits from f's prototype chain. (hence the .prototype = new fNop part)

Example:

function f() {     this.foo = 'bar'; } f.prototype = {     baz: 'yay!' };  var g = f.bind({}); var o = new g(); console.log(o.foo); // 'bar' - (1) console.log(o.baz); // 'yay!' - (2) 

At the moment you call new g(), the fBound function is called as a constuctor with a brand new object object (this) that is an instance of fNop.


Edit:

The ECMAScript5 standard defines a complicated algorithm for binding functions. Amongst others, the following assertions must hold true:

var DateJan2042 = Date.bind(null, 2042, 0);   /*1*/ console.assert(Function.prototype.bind.length == 1, 'bind should have a length of 1');  /*2*/ console.assert(typeof DateJan2042 == 'function', 'bind() should return a function');  /*3*/ console.assert(!DateJan2042.hasOwnProperty('prototype'), 'Bound function must not have a prototype');  /*4*/ console.assert(DateJan2042.length == Math.max(Date.length - 2, 0), 'Bound function should have a proper length');  /*5*/ console.assert(typeof DateJan2042() == 'string', 'Function call should return a string');  /*6*/ console.assert({}.toString.call(new DateJan2042()).indexOf('Date') != -1, 'Constructor call should return a new Date object');  /*7*/ console.assert(new DateJan2042() instanceof DateJan2042, 'Instanceof check should pass for constructor\'s return value');  /*8*/ console.assert((new DateJan2042()).getMonth() == 0, 'Constructor should be called with bound arguments');  /*9*/ console.assert((new DateJan2042(1)).getDate() == 1, 'Constructor should take additional arguments'); /*10*/ console.assert(!/^function *\( *[^ )]/.test(Function.prototype.toString.call(DateJan2042)), 'Bound function should have no formal arguments'); 

Since a properly bound function is not a real Function object, it's impossible to get it all right using a polyfill (Particularly numbers 2/3, and 4/10), but you can try to implement as much as possible.

The implementation in question tries to solve number 6 and number 7, by hooking into the prototype chain, but that's not enough.

Here is an alternative implementation that works a bit better, but is still not perfect: http://jsfiddle.net/YR6MJ/

like image 41
user123444555621 Avatar answered Sep 23 '22 19:09

user123444555621