Elf M. Sternberg wrote:
> One of the complaints about prototype.js (google for it if you're not
> familiar with it) is that it's poorly documented. I have this inkling
> that the key to understanding prototype.js is in the bind function.
I didn't know the file you refer too, at first sight it looks promising
and deserves more time - exciting moments for next Sunday
> Function.prototype.bind = function(object) {
> var method = this;
> return function() {
> method.apply(object, arguments);
> }
> }
An excellent example which should illustrate perfectly how javascript
handles prototypes and closures.
> As I read this, it states that all functions (which are themselves
> objects) will, in the future, have an associated method called "bind".
Indeed; this is permitted by javascript prototypes.
In javascript, functions can be executed in two manners:
- in the "constructor way", when called with the "new" keyword,
- in the "call way", when called without the "new" keyword.
When called as a constructor, a function executes the following steps
(roughly):
- create an empty object, which will be the instance, and which will be
referred to "this" in the function's body that's going to be executed,
- associate a prototype to this instance; normally the link to this
prototype is not enumerable on the instance (this is the Ecma
[[prototype]] property, made accessible in Mozilla under the "__proto__"
property"); the prototype associated is a simple object, which can be
defined by the programmer with the "prototype" property of the
*constructor* (this property not being the prototype of the constructor
itself, but truly the prototype to be associated to instances
constructed by the constructor),
- execute the function's body, as in a "call way".
Each function is, in javascript, an object, created by a regular
constructor called "Function". This means that each function has an
"internal link" __proto__ to a unique object, Function.prototype.
When we call foo.bind(), the identifier resolution will first look at
properties defined for the function "foo"; since it has no "bind"
property, it will look up the prototype and search the property on the
prototype; if the prototype does not possess the property, then the
identifier will be searched on the prototype's prototype, and so on...
until the ultimate prototype (null) is reached - this is the "prototype
chain". When the "bind" function is found, it is executed, and the
"this" value associated to the execution context is the function/object
"foo".
---
function Foo(){}
Foo.prototype.bar=42;
var f1=new Foo();
var f2=new Foo();
f1.bar=50;
alert(f1.bar); //50
alert(f2.bar); //42
Foo.prototype.bar=70;
alert(f1.bar); //50
alert(f2.bar); //70
delete f1.bar; //remove the property on f1
alert(f1.bar); //70
---
More information on prototypes can be found in the ECMAScript
specification ECMA262-3, §15.
> The "this" there refers to the function object associated with the call
> to bind(), right? But the word "arguments" there refers to the
> arguments passed to the function object *generated by* the call to
> bind().
That's right; the code you've provided nicely exploits the way functions
work in javascript
:
- functions can be created in several ways, either using the Function
constructor, or using function expressions; the way a function is
created is important in regards of the scope it will have access to,
- when creating a function using a function expression/declaration, we
can nest functions inside functions; an inner function will have access
to the "container function" variables, even if the "container function"
has been executed and has returned (closures / lexical scoping),
- the "this" value available in a function depends on the way the
function is called; it is either the global object if the function is
called directly, or the instance to which the function is bound as a
method, if called as a method; the "this" value can even be set by the
programmer, using "apply" or "call".
More information on Function.prototype.apply can be found in the
ECMAScript specification, ECMA262-3, §15.3.4.3.
More information on the "this" value, "Arguments" object, and more
generally execution contexts, can be found in the ECMAScript
specification, ECMA262-3, §10.
> In every example within prototype.js, bind() is called either in a
> constructor or within a method contexted to a javascript object, and is
> always called with "this" as its argument, e.g.:
>
> this.options.onComplete = this.updateContent.bind(this);
>
> As I read the code it seems to be stating that the "this" object
> referred to within bind()'d functions are being coerced into always
> referring to the current instantiated object.
>
> But isn't this always the case anyway? Is this a rather confusing
> attempt to ensure "this" purity whereby the call
>
> method.apply(object, arguments)
>
> is forced to always have the reference to the containing object present?
Well, if the author always passes "this" to the "bind" calls then indeed
there's no added value, the "binding" process is useless, brings some
confusion and even gets you a slight memory overhead.
However I can understand the will of the author to have something
generic enough, so that the function can be used with other "this"
values (it's possibly better to define the "this" value explicitly in
the call rather than calling the bind() function in a context with an
appropriate "this" value).
> I think I've got it. Bind() generates uniq functions that contain live
> references to the objects to which they belong, such that the function
> object can then be passed to setTimeout() or onMouseOver(), handlers
> that accept functions but not objects.
>
> Have I got this right?
Definitely yes!
Reading the following should clear all your doubts:
<URL:http://www.jibbering.com/faq/faq_notes/closures.html>
Regards,
Yep.