Function#methodize

50 views
Skip to first unread message

petrob

unread,
Apr 15, 2012, 1:34:09 PM4/15/12
to Prototype & script.aculo.us
Hi,

I would like to understand the working of the Function#methodize
method and yes, I have read this post:
http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/d9c5444eaa66bdaf/e8287ff38260f0a9?lnk=gst&q=methodize#e8287ff38260f0a9
, but it is still unclear for me.

I broke it down to something like a minimal case like this:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://
www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<meta http-equiv="Content-type" content="text/
html;charset=iso-8859-1" />
<title>Test methodize</title>

<script language="javascript" type="text/
javascript">Function.prototype.update = function(array, args){

var array_length = array.length, length = args.length;

while(length --){
array[array_length + length] = args[length];
}
return array;
}
/*
Function.prototype.methodize = function(){

if(this._methodized){ return this._methodized;}

var __method = this;

return this._methodized = function(){
console.log(this);
var a = Function.prototype.update([], arguments);
return __method.apply(this, a);
};
}
*/


Function.prototype.methodize = function(){

if(this._methodized){ return this._methodized;}

var __method = this;

return this._methodized = function(){
console.log(this);
var a = Function.prototype.update([this], arguments);
return __method.apply(null, a);
};


}


window.addEventListener('load', function(){


var obj_a = {
value : 2,
name : 'obj_a'
};

var obj_b = {
value : 5,
name : 'obj_b'
}

var multiplier = function(times){

times = isNaN(times) ? 2 : times;
if(typeof this.value != 'undefined'){

return this.value * times;
}
else{ alert(this + ': does not have a value property!');}
};

obj_a.multiply = multiplier.methodize();

console.log(obj_a.multiply(3));

console.log(obj_a.multiply(10));

obj_b.multiply = multiplier.methodize();
console.log(obj_b.multiply(5));


}, false);
</script>

</head>
<body>
<div>Nothing to see here</div>
</body>
</html>


If you run the above code, you will get a series of alert messages as
you should, I think.

In my understanding calling methodize on any function for the first
time means creating a static _methodized method on that particular
function that did not exist so far, while any later call to it returns
a reference to this static method regardless where the call originates
from.

if(this._methodized){ return this._methodized;}
return this._methodized = function(){}

Through assignments any number of objects can have any named methods
to refer to _methodized, but the ‘this’ keyword inside it will always
points to that particular object from which I am calling it.

Following this argumentation replacing methodize with this makes the
above code work as expected:

Function.prototype.methodize = function(){

if(this._methodized){ return this._methodized;}

var __method = this;
return this._methodized = function(){
console.log(this);
var a = Function.prototype.update([], arguments);
return __method.apply(this, a);
};
}

Could someone explain why and how the null value for the thisArg
argument in the original version is used if it is always resolved to
the window object in a browser and how methodize works?

Victor

unread,
Apr 16, 2012, 3:49:19 AM4/16/12
to prototype-s...@googlegroups.com
Where is the second argument in multiplier()? I'll try to explain in example:

function multiplier(obj, times) {
  times = isNaN(times) ? 2 : times;
  if (typeof obj.value != 'undefined') {
    return obj.value * times;
  } else {
    alert(obj + ': does not have a value property!');
  }
}

obj_a.multiply = multiplier.methodize();
multiplier(obj_a, 2); // same as obj_a.multiply(2);
obj_a.multiply(2); // same as multiplier(obj_a, 2);

petrob

unread,
Apr 16, 2012, 4:14:47 PM4/16/12
to Prototype & script.aculo.us
The whole point of using the methodize Function:prototype method is
NOT to have to pass an object in any function as a second argument,
but to call the function in the scope of any arbitrary object, isn't
it? At least, this is what the documentation explains.

petrob

unread,
Apr 16, 2012, 4:20:46 PM4/16/12
to Prototype & script.aculo.us

petrob

unread,
Apr 17, 2012, 3:14:26 AM4/17/12
to Prototype & script.aculo.us
Yes, of course, you are right! Oh, God, I just don’t believe this.
It’s because it was so long ago I wrote procedural code that I would
never have thought someone would ever want to use it with a library.
After all people (should) learn proper object oriented programming
practices first before they would start to use libraries..
In object oriented style functions are grouped together with the data
in objects that they will work on as methods and usually the context
object is adjusted if necessary to give a different meaning to the
‘this’ keyword inside them. This is why I would never ever try to pass
an object as an argument to a function if I wanted to make it a method
of that object. Awkward!
It’s like using a USB to PS2 adapter. You give up a large part of
higher functionality and flexibility to serve the needs of something
much worse.
In the correct way ‘methodize’ would be an alternative of ‘bind’, with
the exception of not explicitly passing the object in the closure like
in ‘bind’ to be preserved and used as a new context object of the
function, but passing a newly created static method back to become a
method of the object and gain access to it through the ‘this’ keyword,
as I described.
In this way proper object oriented code could exploit the this context
object inside the function and old procedural code could also be
adapted with something as simple as this:

Outdated_function(param:2, param:3){
obj_param_1 = this;
/*
Some code I don’t want to rewrite that uses the original passed-in
obj_param_1.
*/
Reply all
Reply to author
Forward
0 new messages