1

I study JavaScript Proxy Pattern, but I still do not get, where I can benefit from it. I would therefore like to provide you with two examples and kindly ask you to point at the difference between them.

Please, take a look at the code below:

  • What is the difference between the two addEventListener calls? One of them calls handleDrop in regular way. The other uses Proxy Pattern.
  • What will I gain using Proxy pattern approach?

I tested both functions, and they both call handleDrop successfully.

DndUpload.prototype.buildDropZone = function ()
{
    var self = this,

    this.dropZone.addEventListener('drop', function (e) { self.handleDrop.call(self, e) }, false);
    this.dropZone.addEventListener('drop', self.handleDrop, false);


    DndUpload.prototype.handleDrop = function (e)
    {
        alert("test");
        ...
    };
}

You can provide me with good reference which contains very clear explanation of Proxy Pattern in JavaScript.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77

3 Answers3

10

So what you're describing in your example isn't so much a demonstration of the Proxy pattern as much as a demonstration of confusion regarding the "calling object" and how it works in JavaScript.

In JavaScript, functions are "first-class." This essentially means that functions are data just like any other data. So let's consider the following situation:

var fn = (function () { return this.x; }),
    a = {
        x : 1,
        fn : fn,
    },
    x = 2,
    nothing = (function (z) { return z; });

So, we have an object a, which has two properties: fn and x. We also have variables x, fn (which is a function returning this.x), and nothing (which returns whatever it gets passed).

If we evaluate a.x, we get 1. If we evaluate x, we get 2. Pretty simple, eh? Now, if we evaluate nothing(a.x), then we get 1. That's also very simple. But it's important to realize that the value 1 associated with the property a.x is not in any way connected to the object a. It exists independently and can be passed around simply as a value.

In JavaScript, functions work the same way. Functions that are properties (often called "methods") can be passed as simple references. However, in doing so, they can become disconnected from their object. This becomes important when you use the this keyword inside a function.

The this keyword references the "calling object." That's the object that is associated with a function when that function is evaluated. There are three basic ways to set the calling object for a function:

  1. If the function is called using the dot operator (e.g. a.fn()), the relevant object (in the example, a) is set as the calling object.
  2. If the function is called using the function's call or apply properties, then you can explicitly set the calling object (we'll see why this is useful in a second).
  3. If no calling object is set through method 1 or method 2, the global object is used (in a browser, this is typically called window).

So, back to our code. If we call a.fn(), it will evaluate as 1. That's expected because the this keyword in the function will be set to a due to the use of the dot operator. However, if we call simply fn(), it will return 2 because it is referencing the x property of the global object (meaning our global x is used).

Now, here's where things get tricky. What if you called: nothing(a.fn)()? You might be surprised that the result is 2. This is because passing a.fn into nothing() passes a reference to fn, but does not retain the calling object!

This is the same concept as what's going on in your coding example. If your function handleDrop were to use the this keyword, you would find it has a different value depending on which handler form you use. This is because in your second example, you're passing a reference to handleDrop, but as with our nothing(a.fn)() example, by the time it gets called, the calling object reference is lost.

So let's add something else to the puzzle:

var b = {
    x : 3
};

You'll note that while b has an x property (and therefore satisfies the requirements for fn's use of this), it doesn't have a property referencing fn. So if we wanted to call the fn function with its this set to b, it might seem we need to add a new property to b. But instead we can use the aforementioned apply method on fn to explicitly set b as the calling object:

fn.apply(b); //is 3

This can be used to "permanently" bind a calling object to a function by creating a new function "wrapper." It's not really permanently binding, it's just creating a new function that calls the old function with the desired calling object. Such a tool is often written like so:

Function.prototype.bind = function (obj) {
    var self = this;
    return function() { 
        return self.apply(obj, arguments);
    };
};

So after executing that code, we could do the following:

nothing(a.fn.bind(a))(); //is 1.

It's nothing tricky. In fact, the bind() property is built into ES5 and works pretty much like the simple code above. And our bind code is actually a really complicated way to do something that we can do more simply. Since a has fn as a property, we can use the dot operator to call it directly. We can skip all the confusing use of call and apply. We just need to make sure when the function gets called, it gets called using the dot operator. We can see how to do it above, but in practice, it's far simpler and more intuitive:

nothing(function () { return a.fn(); })(); //is 1

Once you have an understanding of how data references can be stored in closure scope, how functions are first-class objects, and how the calling object works, this all becomes very simple to understand and reasonably intuitive.

As for "proxies," those also exploit the same concepts to hook into functions. So, let's say that you wanted to count the number of times a.fn gets called. You can do that by inserting a proxy, like so (making use of some concepts from our bind code from above):

var numCalls = (function () {
    var calls = 0, target = a.fn;

    a.fn = (function () {
         calls++;
         return target.apply(a, arguments);
    });

    return (function () {
        return calls;
    });
}());

So now, whenever you call numCalls(), it will return the number of times a.fn() was called without actually modifying the functionality of a.fn! which is pretty cool. However, you must keep in mind that you did change the function referenced by a.fn, so looking way back to the beginning of our code, you'll notice that a.fn is no longer the same as fn and can't be used interchangeably anymore. But the reasons should now be pretty obvious!

I know that was basically a week of JavaScript education in a couple pages of text, but that's about as simple as it gets. Once you understand the concepts, the functionality, usefulness, and power of many JavaScript patterns become very simple to understand.

Hope that made things clearer!

UPDATE: Thanks to @pimvdb for pointing out my unnecessary use of [].slice.call(arguments, 0). I have removed it because it's, well, unnecessary.

Pete
  • 2,538
  • 13
  • 15
  • +1, Hi Pete.. Your answer was shocking. Thank you for writing so much information. I have to re-read it several times and I need to obtain some practice. However you explained it in the best way. The examples were great. I would still like to keep this thread open for couple of days, to see, if somebody puts here some other information. but your post is candidate #1 for the Accepted Answer status. Thank you. – Bunkai.Satori Sep 13 '12 at 20:09
  • This is a very decent explanation. I just wanted to add that the `[].slice.call` is not necessary - you can just pass `arguments` to `.apply`. – pimvdb Sep 13 '12 at 20:28
  • 1
    @pimvdb I wasn't sure and I was doing most of this without actually testing my code (I know, that's naughty). So I was playing it safe. You never know just how "Array-ish" `arguments` is in any given circumstance. I'll update to fix (once I confirm you're correct). :-) – Pete Sep 13 '12 at 20:49
3

Basically, passing self.handleDrop directly is functionally equivalent to passing the following function:

function() {
  return self.handleDrop.apply(this, arguments);
}

because everything is passed through to the original function:

  • The this value
  • The arguments
  • The return value

With this in mind, compare your functions as follows:

function(e) { self.handleDrop.call(self, e) }
function() { return self.handleDrop.apply(this, arguments); }

The difference with your proxy way is:

  • It doesn't pass the return value through.
  • It doesn't pass all arguments through (only the first, e)
  • It doesn't pass the this value through, but uses a predefined one: self.

Now, the first two items don't make a difference here, because addEventListener doesn't care about the return value, and it also only passes one argument anyway.

But the third item is important: it sets a different this value in the function. By default, this is the element you bind the event to (it it set by the browser). Using the proxy way, you can set another this value.

Now, in your snippet it is not fully clear why you're setting a prototype function each time buildDropZone is called. Usually you define prototype functions only once. But when your handler handleDrop is called using the proxy way, this refers to the DndUpload instance, which is consistent with prototype functions in general.

pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • +1, hello and thank you very much for your explanation. it vas very good indeed. To summarize ti, Proxy Pattern gives me one more layer of control over the variables entered to the function. Before entering the variables to the function, execution of the function can be a bit postponed, and the variables can be pre-processed by Proxy funciton. Do I understand it correctly? – Bunkai.Satori Sep 13 '12 at 18:59
  • @Bunkai.Satori: Yes, that's a good description. The proxy function receives things (`this`, arguments) and can modify them before/after calling the original function. – pimvdb Sep 13 '12 at 19:01
2

Consider the code below:

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

var someObject = {
  performTest : function() {
    var self = this;

    someOtherObject.higherOrderFunction(printThis);
    someOtherObject.higherOrderFunction(function(){printThis.call(self)});
  }
}

var someOtherObject = {
  higherOrderFunction : function(f) {
      f();
  }
}

What will someOtherObject.higherOrderFunction(printThis) return? How about someOtherObject.higherOrderFunction(function(){printThis.call(self)})

The answer to the first question depends on who and how you call someObject.performTest(). If I just call someObject.performTest() from a global context, it will probably print Window.

The second one will always print the someObject instance no matter what.

The closures or 'proxy pattern' as you call it comes in handy when you want to control exactly the execution context of a function.

Note: this in javascript does not behave like it does in other languages(in Java for example).

Hyangelo
  • 4,784
  • 4
  • 26
  • 33
  • +1, hi and thanks for your answer. If I understand correctly this differ, when called as standard call `someOtherObject.higherOrderFunction(printThis);` versus proxy function `someOtherObject.higherOrderFunction(function(){printThis.call(self)});`. However, it comes to me as quite logical, because proxy function `function(){printThis.call(self)` is contained within the parent function. But, where is a practical benefit from it? Why would I wish to use Proxy pattern? – Bunkai.Satori Sep 13 '12 at 19:18
  • It is benefircial when you want to ensure that the function you just passed to the higherOrderFunction executes with self as the 'this'. Otherwise, 'this' will vary depending on how it is called by higherOrderFunction. – Hyangelo Sep 13 '12 at 19:21
  • Ah, so another benefit is if I wish to bring consistency to the code. Sometimes, `this` can vary, especially, if the function is a CALLBACK. however, if I wish to ensure, that the function will be called as a member of only one particular object, then it is good reason to use Proxy Pattern. – Bunkai.Satori Sep 13 '12 at 19:23
  • Hmm.. but anyay, I can still call a function for example as: `self.handleDrop`. This way, I can ensure, that the function will be a member of `self`, which is my specific and not changing parent object. – Bunkai.Satori Sep 13 '12 at 19:25
  • And as pimvdb added, you can do a bunch of useful stuff before calling the actual function in the proxy. You can do logging, instrumentation, add aditional processing before calling the actual function, etc. – Hyangelo Sep 13 '12 at 19:26
  • I see.. so if I understand correctly, Proxy Pattern helps, but it is not inevitable to use it to achieve particular goal. If I see a code that uses it, I can possibly write another code doing the same thing without Proxy pattern. – Bunkai.Satori Sep 13 '12 at 19:31