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?
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.
“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.
There are 3 ways of writing a function in JavaScript: Function Declaration. Function Expression. Arrow Function.
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.
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.
It is to make sure
instanceof
check)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
.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With