Question

(Question 1) In Flanagan's JS Definitive Guide, he defines the Function method bind() in case it's not available (wasn't available n ECMAScript 3).

It looks like this:

function bind(f, o) {
    if (f.bind) return f.bind(o);     // Use the bind method, if there is one
    else return function() {          // Otherwise, bind it like this
        return f.apply(o, arguments);
    };
}

He illustrates the use of it with an example (which I have modified to change the 3rd line from f.bind(o)):

function f(y) { return this.x + y; } // This function needs to be bound
var o = { x : 1 };                   // An object we'll bind to
var g = bind(f, o);                  // Calling g(x) invokes o.f(x)
g(2)                                 // => 3

When I first saw this, I thought "Wouldn't arguments refer to the arguments variable within the bind function we're defining? But we want the arguments property of the function we eventually apply it to, like g in the example above..."

I verified that his example did indeed work and surmised that the line return f.apply(o, arguments) isn't evaluated until var g = bind(f, o) up above. That is, I thought, when you return a function, are you just returning the source code for that function, no? Until its evaluated? So I tested this theory by trying out a slightly different version of bind:

function mybind2(f, o) {
    var arguments = 6;
    return function() {          // Otherwise, bind it like this
        return f.apply(o, arguments);
    };
}

If it's simply returning tbe unevaluated function source, there's no way that it stores arguments = 6 when later evaluated, right? And after checking, I still got g(2) => 3. But then I realized -- if it's just returning unevaluated code, how is the o in return f.apply(o, arguments) getting passed?

So I decided that what must be happening is this:

The o and the arguments variables (even when arguments equals 6) are getting passed on to the function. It's just that when the function g is eventually called, the arguments variable is redefined by the interpreter to be the arguments of g (in g(2)) and hence the original value of arguments that I tried to pass on was replaced. But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?

(Question 2) Earlier on the same page, he defines the following function which makes use the apply method to trace a function for debugging:

function trace(o, m) {
    var original = o[m];  // Remember original method in the closure.
    o[m] = function() {   // Now define the new method.
        console.log(new Date(), "Entering:", m);      // Log message.
        var result = original.apply(this, arguments); // Invoke original.
        console.log(new Date(), "Exiting:", m);       // Log message.
        return result;                                // Return result.
    };
}

Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?

Was it helpful?

Solution

Question 1

For your first question, let's simplify the example so it's clear what being done:

function bind(func, thisArg) {
    return function () {
        return func.apply(thisArg, arguments);
    };
}

What happens here is that a closure is created that allows the access of the original function and the value of this that is passed. The anonymous function returned will keep the original function in its scope, which ends up being like the following:

var func = function () {};
var thisArg = {};
func.apply(thisArg, [/*arguments*/]);

About your issue with arguments, that variable is implicitly defined in the scope of all functions created, so therefore the inner arguments will shadow the outer arguments, making it work as expected.

Question 2

Your problem is to your misunderstanding of the late binding of this -- this is one of the more confusing things about JavaScript to people used to more object-oriented languages that also have their own this keyword. The value of this is only set when it is called, and where it is called defines the value of this when it is called. It is simply the value of the parent object from where it is at the time the function is called, with the exception of cases where the this value is overridden.

For example, see this:

function a() {return this;};
a(); // global object;
var b = {};
b.a = a;
b.a(); // object b

If this was set when the function was defined, calling b.a would result in the global object, not the b object. Let's also simplify what happens with the second function given:

function trace(obj, property) {
    var method = obj[property];  // Remember original method in the closure.
    obj[property] = function () {   // Now define the new method.
        console.log(1); // Log message.
        // Invoke original method and return its result
        return original.apply(this, arguments);
    };
}

What happens in this case is that the original method is stored in a closure. Assigning to the object that the method was originally in does not overwrite the method object. Just like a normal method assignment, the principles of the this value still work the same -- it will return the parent object, not the function that you've defined. If you do a simple assignment:

var obj = {};
obj.foo = function () { return this; };
obj.foo(); // obj

It does what was expected, returns the parent object at the time of calling. Placing your code in a nested function makes no difference in this regard.

Some good resources

If you'd like to learn more about writing code in JavaScript, I'd highly recommend taking a look at Fully Understanding the this Keyword by Cody Lindley -- it goes into much more detail about how the this keyword behaves in different contexts and the things you can do with it.

OTHER TIPS

But this implies that it was sort of storing the function as text up until then, because otherwise o and arguments would have simply been data in the program, rather than variables that could be overwritten. Is this explanation correct?

No. this and arguments are just special variables which are implicitly set when a function is executed. They don't adhere to normal "closure rules". The function definition itself is still evaluated immediately and bind returns a function object.
You can easily verify this with:

var g = bind(f, o);
console.log(typeof g);

Here is a simpler example which doesn't involve higher order functions:

var arguments = 42;
function foo() {
    console.log(arguments);
}
foo(1, 2);

I think you see that the definition of foo is evaluated like you'd expect. Yet, console.log(arguments) logs [1, 2] and not 42.

Wouldn't the this here refer to the function that we're defining, rather than the object o? Or are those two things one and the same?

this never refers to the function itself (unless you explicitly set it so). The value of this is completely determined by how the function is called. That's why this is often called "the context". The MDN documentation provides extensive information about this.


Reading material:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top