P
Peter Michaux
Hi,
The following doesn't work as intended because "this" refers to
"window" when the event fires.
var bert = {
name: 'Bert',
speak: function() {alert(this.name);}
};
bertDiv.addEventListener('click', bert.speak, false);
------------------
I posted to the ECMAScript4 mailing list, a casual proposal for
function support for partial application that also fixes execution
scope. With some JavaScript library you would write the above example
as this to fix the scope
bertDiv.addEventListener('click', bert.speak.bind(bert), false);
Most JavaScript libraries repeat this functionality to bind event
listener callbacks to a particular object. Dojo, Mootools, Prototype,
and MochiKit are the first four libraries I looked at and they all
implement something like this. YUI does the same thing with event
handlers but with different syntax.
------------------
Brendan Eich responded to me that he likes the idea and asked for me
to make a
"proposal for Function.partial (let's call it), a static method of
Function, whose first parameter is the callable to partially apply; as
with the static generics there would be a Function.prototype.partial
taking just the arguments and using |this| as the callable to
partially apply."
The "static generics" page:
http://wiki.ecmascript.org/doku.php?id=proposals:static_generics
-------------------
Below is my proposed functionality. Surely there are many ways to
write this in JavaScript but this would likely be implemented in C so
only the correct behavior is important here.
I've defined partialApply and partialCall because frequently I find
Function.prototype.apply functionality so useful and more general.
Brendan can take and/or leave any parts he likes, of course.
Function.partialApply = function(f, scope, argums) {
// make a local shallow copy of the argums array
var args = [];
for (var i=0; i<argums.length; i++) {
args.push(argums);
}
return function() {
for (var i=0; i<arguments.length; i++) {
args.push(arguments);
}
return f.apply(scope, args);
}
};
Function.partialCall = function(f, scope) {
// convert extra arguments to an array
// so can reuse Function.partialApply
var args = [];
for (var i=2; i<arguments.length; i++) {
args.push(arguments);
}
return Function.partialApply(f, scope, args)
};
Function.prototype.partialApply = function(scope, argums) {
return Function.partialApply(this, scope, argums);
};
Function.prototype.partialCall = function(scope) {
var args = [];
for (var i=1; i<arguments.length; i++) {
args.push(arguments);
}
return Function.partialApply(this, scope, args);
};
// example uses
function foo(a, b, c, d) {
alert(this.name + a + b + c + d);
}
var bert = {name:'Bert'};
var partiallyCalledFoo = foo.partialCall(bert, 1, 2);
partiallyCalledFoo(3, 4); // alert says "Bert1234"
var partiallyAppliedFoo = foo.partialApply(bert, [1, 2]);
partiallyAppliedFoo(3, 4); // alert says "Bert1234"
var partiallyCalledFoo = Function.partialCall(foo, bert, 1, 2);
partiallyCalledFoo(3, 4); // alert says "Bert1234"
var partiallyAppliedFoo = Function.partialApply(foo, bert, [1, 2]);
partiallyAppliedFoo(3, 4); // alert says "Bert1234"
// second partial applicaiton doesn't (and shouldn't)
// change scope of execution of the original function.
var partiallyAppliedFoo = foo.partialApply(bert, [1, 2]);
var matilda = {name:'Matilda'};
var doublelyPartiallyAppliedFoo =
partiallyAppliedFoo.partialCall(matilda, 5)
doublelyPartiallyAppliedFoo(7); // alert says "Bert1257"
-------------------------------
// in a loop is big payoff
for (var i=0; i<people.length; i++) {
var person = people;
person.div.addEventListener('click',
person.speak.bind(person, 1), false);
}
-------------------------------
// here is another common situation
function handler(position) {
alert('item ' + position + ': ' + this.innerHTML);
}
var items =
document.getElementById('myList').getElementsByTagName('li');
// instead of this mess
for (var i=0; i<items.length; i++) {
var item = items;
item.addEventListener('click', (function(item, i) {
return function(){
return handler.call(item, i);
}
})(item, i), false);
}
// we could write
for (var i=0; i<items.length; i++) {
var item = items;
item.addEventListener('click', handler.partialCall(item, i),
false);
}
-------------------------------
Naturally, I believe c.l.j readers are the best group from which to
solicit well-considered comments before I go ahead and make a proposal
to screw up JavaScript
Thanks,
Peter
The following doesn't work as intended because "this" refers to
"window" when the event fires.
var bert = {
name: 'Bert',
speak: function() {alert(this.name);}
};
bertDiv.addEventListener('click', bert.speak, false);
------------------
I posted to the ECMAScript4 mailing list, a casual proposal for
function support for partial application that also fixes execution
scope. With some JavaScript library you would write the above example
as this to fix the scope
bertDiv.addEventListener('click', bert.speak.bind(bert), false);
Most JavaScript libraries repeat this functionality to bind event
listener callbacks to a particular object. Dojo, Mootools, Prototype,
and MochiKit are the first four libraries I looked at and they all
implement something like this. YUI does the same thing with event
handlers but with different syntax.
------------------
Brendan Eich responded to me that he likes the idea and asked for me
to make a
"proposal for Function.partial (let's call it), a static method of
Function, whose first parameter is the callable to partially apply; as
with the static generics there would be a Function.prototype.partial
taking just the arguments and using |this| as the callable to
partially apply."
The "static generics" page:
http://wiki.ecmascript.org/doku.php?id=proposals:static_generics
-------------------
Below is my proposed functionality. Surely there are many ways to
write this in JavaScript but this would likely be implemented in C so
only the correct behavior is important here.
I've defined partialApply and partialCall because frequently I find
Function.prototype.apply functionality so useful and more general.
Brendan can take and/or leave any parts he likes, of course.
Function.partialApply = function(f, scope, argums) {
// make a local shallow copy of the argums array
var args = [];
for (var i=0; i<argums.length; i++) {
args.push(argums);
}
return function() {
for (var i=0; i<arguments.length; i++) {
args.push(arguments);
}
return f.apply(scope, args);
}
};
Function.partialCall = function(f, scope) {
// convert extra arguments to an array
// so can reuse Function.partialApply
var args = [];
for (var i=2; i<arguments.length; i++) {
args.push(arguments);
}
return Function.partialApply(f, scope, args)
};
Function.prototype.partialApply = function(scope, argums) {
return Function.partialApply(this, scope, argums);
};
Function.prototype.partialCall = function(scope) {
var args = [];
for (var i=1; i<arguments.length; i++) {
args.push(arguments);
}
return Function.partialApply(this, scope, args);
};
// example uses
function foo(a, b, c, d) {
alert(this.name + a + b + c + d);
}
var bert = {name:'Bert'};
var partiallyCalledFoo = foo.partialCall(bert, 1, 2);
partiallyCalledFoo(3, 4); // alert says "Bert1234"
var partiallyAppliedFoo = foo.partialApply(bert, [1, 2]);
partiallyAppliedFoo(3, 4); // alert says "Bert1234"
var partiallyCalledFoo = Function.partialCall(foo, bert, 1, 2);
partiallyCalledFoo(3, 4); // alert says "Bert1234"
var partiallyAppliedFoo = Function.partialApply(foo, bert, [1, 2]);
partiallyAppliedFoo(3, 4); // alert says "Bert1234"
// second partial applicaiton doesn't (and shouldn't)
// change scope of execution of the original function.
var partiallyAppliedFoo = foo.partialApply(bert, [1, 2]);
var matilda = {name:'Matilda'};
var doublelyPartiallyAppliedFoo =
partiallyAppliedFoo.partialCall(matilda, 5)
doublelyPartiallyAppliedFoo(7); // alert says "Bert1257"
-------------------------------
// in a loop is big payoff
for (var i=0; i<people.length; i++) {
var person = people;
person.div.addEventListener('click',
person.speak.bind(person, 1), false);
}
-------------------------------
// here is another common situation
function handler(position) {
alert('item ' + position + ': ' + this.innerHTML);
}
var items =
document.getElementById('myList').getElementsByTagName('li');
// instead of this mess
for (var i=0; i<items.length; i++) {
var item = items;
item.addEventListener('click', (function(item, i) {
return function(){
return handler.call(item, i);
}
})(item, i), false);
}
// we could write
for (var i=0; i<items.length; i++) {
var item = items;
item.addEventListener('click', handler.partialCall(item, i),
false);
}
-------------------------------
Naturally, I believe c.l.j readers are the best group from which to
solicit well-considered comments before I go ahead and make a proposal
to screw up JavaScript
Thanks,
Peter