"Construct" with variable arguments

  • Thread starter Michael Haufe (\TNO\)
  • Start date
M

Michael Haufe (\TNO\)

Am I correct that in ES3, this is the most straightforward way to call
a constructor with variable arguments?
----------
function Point(){
if(!(this instanceof Point)){
var args = [];
for(var i = 0, len = arguments.length; i < len; i++)
args = "arguments[" + i + "]";
return eval("new Point(" + args + ")");
}
this.x = arguments[0];
this.y = arguments[1];
}
Point.prototype.toString = function(){
return "(" + this.x + "," + this.y + ")";
}

var p1 = new Point(1,2);
var p2 = Point(3,4);

WScript.Echo(p1.toString())
WScript.Echo(p2.toString())
 
V

VK

Am I correct that in ES3, this is the most straightforward way to call
a constructor with variable arguments?
----------
function Point(){
    if(!(this instanceof Point)){
        var args = [];
        for(var i = 0, len = arguments.length; i < len; i++)
            args = "arguments[" + i + "]";
        return eval("new Point(" + args + ")");
    }


This part left me puzzled both in "constructor with variable
arguments" and the overall purpose aspects. Do you mean a polymorphic
constructor that would act as a constructor and constructor only -
either called as a constructor or as a simple function? There is a
much simple way than that.

As of "arguments" yes, if you cannot predict the exact number of
arguments, "arguments" is one of ways. The most straightforward or not
depends on circumstances.
 
D

Dmitry A. Soshnikov

You may use `bind` for that (own in ES3). And a special `construct`
function which accepts a list of arguments and creates a bound function
(with these partial arguments) from original one.

Dmitry.
 
M

Michael Haufe (\TNO\)

You may use `bind` for that (own in ES3). And a special `construct`
function which accepts a list of arguments and creates a bound function
(with these partial arguments) from original one.


Something like this perhaps?

Function.construct = function(C){
var args = [];
for(var i = 1, len = arguments.length; i < len; i++)
args[i - 1] = "arguments[" + i + "]";
return eval("new C(" + args + ")");
}

function Point(){
if(!(this instanceof Point))
return Function.construct.apply(this,
[Point].concat(Array.slice(arguments)));
this.x = arguments[0];
this.y = arguments[1];
}

how would .bind(...) work?
 
M

Michael Haufe (\TNO\)

Am I correct that in ES3, this is the most straightforward way to call
a constructor with variable arguments?
----------
function Point(){
    if(!(this instanceof Point)){
        var args = [];
        for(var i = 0, len = arguments.length; i < len; i++)
            args = "arguments[" + i + "]";
        return eval("new Point(" + args + ")");
    }


This part left me puzzled both in "constructor with variable
arguments" and the overall purpose aspects. Do you mean a polymorphic
constructor that would act as a constructor and constructor only -
either called as a constructor or as a simple function? There is a
much simple way than that.

As of "arguments" yes, if you cannot predict the exact number of
arguments, "arguments" is one of ways. The most straightforward or not
depends on circumstances.



The idea is to avoid having an api require the use of arrays for
everything, and to always be a constructor. A more realistic example:

var s = SymbolTable(
Infix("+",10,add), Infix("-",10,sub),
Infix("*",20,add), Infix("/",20,div),
Literal(/\d+/,int), Prefix("-",100,neg),
...
);

or..

var note = XElement("note"
XAttr("from","Bob"), XAttr("to","Alice"),
XElement("msg", "Meeting at 07:00"),
....
)

etc...
 
T

Thomas 'PointedEars' Lahn

Michael said:
Dmitry A. Soshnikov said:
You may use `bind` for that (own in ES3). And a special `construct`
function which accepts a list of arguments and creates a bound function
(with these partial arguments) from original one.

Something like this perhaps?

Function.construct = function(C){
var args = [];
for(var i = 1, len = arguments.length; i < len; i++)
args[i - 1] = "arguments[" + i + "]";
return eval("new C(" + args + ")");
}

Looks a lot like Function.prototype.construct() in JSX:eek:bject.js :)
function Point(){
if(!(this instanceof Point))
return Function.construct.apply(this,
[Point].concat(Array.slice(arguments)));

That's awfully complicated (and inefficient), but probably a necessary
result of your making construct() a "static" method instead of a "dynamic"
one (e.g. a prototype method). Keep in mind that functions are (first-
class) objects, and inherit from Function.prototype. Applying that
knowledge leads quite naturally to

if (!(this instanceof Point))
{
return Point.construct(arguments);
}
this.x = arguments[0];
this.y = arguments[1];
}

how would .bind(...) work?

So far it would not work in JScript, which your WScript.Echo() suggests. As
for ES5-conforming implementations, fun.bind(foo, bar) is more or less a
more concise way to say

(function(f, thisArg) {
var args = Array.prototype.call.slice(arguments, 2)
return function() {
return f.apply(thisArg, args);
};
}(fun, foo, bar));

I do not see how that could help with a constructor call.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Michael said:
The idea is to avoid having an api require the use of arrays for
everything, and to always be a constructor. A more realistic example:

var s = SymbolTable(
Infix("+",10,add), Infix("-",10,sub),
Infix("*",20,add), Infix("/",20,div),
Literal(/\d+/,int), Prefix("-",100,neg),
...
);

or..

var note = XElement("note"
XAttr("from","Bob"), XAttr("to","Alice"),
XElement("msg", "Meeting at 07:00"),
....
)

Isn't this just a little bit too inefficient only to have a more pythonic
language?


PointedEars
 
M

Michael Haufe (\TNO\)

Something like this perhaps?
Function.construct = function(C){
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++)
        args[i - 1] = "arguments[" + i + "]";
    return eval("new C(" + args + ")");
}

Looks a lot like Function.prototype.construct() in JSX:eek:bject.js :)

One of these days I'm going to have to sit down and read through your /
scripts/ directory...
function Point(){
    if(!(this instanceof Point))
        return Function.construct.apply(this,
            [Point].concat(Array.slice(arguments)));

That's awfully complicated (and inefficient), but probably a necessary
result of your making construct() a "static" method instead of a "dynamic"
one (e.g. a prototype method).  Keep in mind that functions are (first-
class) objects, and inherit from Function.prototype.  Applying that
knowledge leads quite naturally to

  if (!(this instanceof Point))
  {
    return Point.construct(arguments);
  }
    this.x = arguments[0];
    this.y = arguments[1];
}

Yes, much better. I guess if I'm already augmenting I might as well do
so on the prototype.
 
T

Thomas 'PointedEars' Lahn

Michael said:
function Point(){
if(!(this instanceof Point))
return Function.construct.apply(this,
[Point].concat(Array.slice(arguments)));

There is no Array.slice() method.
this.x = arguments[0];
this.y = arguments[1];
}

And why not simply name your arguments?

function Point(x, y)
{
if(!(this instanceof Point))
{
return Function.construct.apply(this,
[Point].concat(Array.prototype.slice(arguments)));
}

this.x = x;
this.y = y;
}


PointedEars
 
T

Thomas 'PointedEars' Lahn

Michael said:
Thomas said:
Michael said:
Function.construct = function(C){
var args = [];
for(var i = 1, len = arguments.length; i < len; i++)
args[i - 1] = "arguments[" + i + "]";
return eval("new C(" + args + ")");
}

Looks a lot like Function.prototype.construct() in JSX:eek:bject.js :)

One of these days I'm going to have to sit down and read through your /
scripts/ directory...

I am currently in the process of reorganizing and documenting its contents.
The next commit will already begin with the former one.


PointedEars
 
M

Michael Haufe (\TNO\)

Isn't this just a little bit too inefficient only to have a more pythonic
language?

Potentially, yes. But in the examples listed only SymbolTable and
XElement would need .construct(...) as the others would have a fixed
number of arguments:

function XAttr(name,value){
if(!(this instanceof XAttr))
return new XAttr(name,value);
...
}

for what use cases do you use Function.prototype.construct() in
JSX:eek:bject.js?
 
M

Michael Haufe (\TNO\)

Michael said:
function Point(){
    if(!(this instanceof Point))
        return Function.construct.apply(this,
            [Point].concat(Array.slice(arguments)));

There is no Array.slice() method.
    this.x = arguments[0];
    this.y = arguments[1];
}

And why not simply name your arguments?

  function Point(x, y)
  {
    if(!(this instanceof Point))
    {
      return Function.construct.apply(this,
        [Point].concat(Array.prototype.slice(arguments)));
    }

    this.x = x;
    this.y = y;
  }

This was just an example to illustrate the idea, not an instance of
something I'd do with a Point object (see the bottom of my original
message).
 
T

Thomas 'PointedEars' Lahn

Michael said:
Thomas said:
Michael said:
The idea is to avoid having an api require the use of arrays for
everything, and to always be a constructor. A more realistic example:
[…]
var note = XElement("note"

Did you forget a comma here?
Potentially, yes. But in the examples listed only SymbolTable and
XElement would need .construct(...) as the others would have a fixed
number of arguments:

function XAttr(name,value){
if(!(this instanceof XAttr))
return new XAttr(name,value);
...
}

That is still one unnecessary layer on the stack for the price of a simple
`new'.
for what use cases do you use Function.prototype.construct() in
JSX:eek:bject.js?

If Eclipse JSDT is right, currently only in httprequest.js:

/* line 128 */
jsx.HTTPRequest = function (sURL, sMethod, bAsync, fSuccessListener,
fErrorListener) {
/* Enables factory use */
var me = arguments.callee;
if (this.constructor !== me)
{
return me.construct(arguments);
}

this.setURL(sURL, true);
this.setMethod(sMethod);
this.setAsync(bAsync);
this.setSuccessListener(fSuccessListener);
this.setErrorListener(fErrorListener);
this.setData();
this.setRequestType();
};

But I think I have only used it there as proof-of-concept. Do you think I
should remove it?


Regards,

PointedEars
 
M

Michael Haufe (\TNO\)

Michael said:
Thomas said:
Michael Haufe ("TNO") wrote:
The idea is to avoid having an api require the use of arrays for
everything, and to always be a constructor. A more realistic example:
[…]
var note = XElement("note"

Did you forget a comma here?
Yes.

Potentially, yes. But in the examples listed only SymbolTable and
XElement would need .construct(...) as the others would have a fixed
number of arguments:
function XAttr(name,value){
if(!(this instanceof XAttr))
return new XAttr(name,value);
...
}

That is still one unnecessary layer on the stack for the price of a simple
`new'.

It is certainly unnecessary if I can be sure that XAttr would always
be called as a constructor, but besides that I'm quite partial to
being able to write:

var contacts = XElement("Contacts",
XElement("Contact",
XElement("Name", "Patrick Hines"),
XElement("Phone", "206-555-0144"),
XElement("Address",
XElement("Street1", "123 Main St"),
XElement("City", "Mercer Island"),
XElement("State", "WA"),
XElement("Postal", "68042")
)
)
);

vs.

var contacts = new XElement("Contacts",
new XElement("Contact",
new XElement("Name", "Patrick Hines"),
new XElement("Phone", "206-555-0144"),
new XElement("Address",
new XElement("Street1", "123 Main St"),
new XElement("City", "Mercer Island"),
new XElement("State", "WA"),
new XElement("Postal", "68042")
)
)
);
If Eclipse JSDT is right, currently only in httprequest.js:

/* line 128 */
jsx.HTTPRequest = function (sURL, sMethod, bAsync, fSuccessListener,
fErrorListener) {
/* Enables factory use */
var me = arguments.callee;
if (this.constructor !== me)
{
return me.construct(arguments);
}

this.setURL(sURL, true);
this.setMethod(sMethod);
this.setAsync(bAsync);
this.setSuccessListener(fSuccessListener);
this.setErrorListener(fErrorListener);
this.setData();
this.setRequestType();

};

But I think I have only used it there as proof-of-concept. Do you think I
should remove it?

I would say yes only in the name of library consistency if it is the
only one. (Why can I only do "new jsx.HTTPRequest(...)" but not "new
jsx.OtherObject(...)"?);
Not having seen jsx in any detail I can't make a better judgement.
 
M

Michael Haufe (\TNO\)

Michael said:
function Point(){
    if(!(this instanceof Point))
        return Function.construct.apply(this,
            [Point].concat(Array.slice(arguments)));

There is no Array.slice() method.

I left out the implementation for the sake of brevity
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Michael said:
Dmitry A. Soshnikov said:
You may use `bind` for that (own in ES3). And a special `construct`
function which accepts a list of arguments and creates a bound function
(with these partial arguments) from original one.

[…]
how would .bind(...) work?

So far it would not work in JScript, which your WScript.Echo() suggests.
As for ES5-conforming implementations, fun.bind(foo, bar) is more or less
a more concise way to say

(function(f, thisArg) {
var args = Array.prototype.call.slice(arguments, 2)

var args = Array.prototype.slice.call(arguments, 2);
 
L

Lasse Reichstein Nielsen

Michael Haufe (\"TNO\") said:
Am I correct that in ES3, this is the most straightforward way to call
a constructor with variable arguments?

If you insist on calling the constructor with the arguments, then you
need to create code to do so, yes. There is no apply for new-calls.
Maybe in Harmony, using rest parameters.

Alternatives could be to:
1. Use a different constructor that takes an array, and always let
your Point delegate to that:
function Point() {
var args = Array.prototype.slice.call(arguments, 0, arguments.length);
return new PointFromArray(args);
}

2. Have a neutral behavior for zero arguments, and just use that to create
an uninitialized object, and then continue initializing normally:
function Point() {
var self = this;
if (!(self instanceof Point)) {
self = new Point(); // Neutral object creation.
} else {
// Do initialization that always have to happen,
// even for arguments.length == 0.
}
if (arguments.length > 0) {
// Do remaining initialization.
self.x = arguments[0];
self.x = arguments[1];
}
}

It might not be possible to separate the code so cleanly into
that which uses arguments and that which doesn't, e.g., if the
behavior is markedly different for zero arguments..

3. Create the Point object without using the Point constructor:
function Point() {
var self = this;
if (!(self instanceof Point)) {
self = new UninitializedPoint;
}
// initialize self
self.x = arguments[0];
self.y = arguments[1];
}
function UninitializedPoint() {}
UninitializedPoint.prototype = Point.prototype;

This is just a specialized version of using clone(Point.prototype)
where clone is defined as:
function clone(proto) {
function Empty(){}
Empty.prototype = proto;
return new Empty;
}
or using ES5 Object.create(Point.prototype) to create the new Point
object.


Generally I would recommend against using eval. You are doing it
correctly (which is rare to begin with), but it's still overly
complicated and hard to maintain and debug.
Personally, I'd go for the third alternative.

Best of luck.
/L
 
T

Thomas 'PointedEars' Lahn

Michael said:
Thomas said:
Michael said:
Thomas 'PointedEars' Lahn wrote:
Michael Haufe ("TNO") wrote:
The idea is to avoid having an api require the use of arrays for
everything, and to always be a constructor. A more realistic
example:
[…]
function XAttr(name,value){
if(!(this instanceof XAttr))
return new XAttr(name,value);
...
}

That is still one unnecessary layer on the stack for the price of a
simple `new'.

It is certainly unnecessary if I can be sure that XAttr would always
be called as a constructor,

You could throw an exception if that were not so, to avoid augmenting the
Global Object.
but besides that I'm quite partial to being able to write:

var contacts = XElement("Contacts",
XElement("Contact",
XElement("Name", "Patrick Hines"),
XElement("Phone", "206-555-0144"),
XElement("Address",
XElement("Street1", "123 Main St"),
XElement("City", "Mercer Island"),
XElement("State", "WA"),
XElement("Postal", "68042")
)
)
);

[…]

OK :)
for what use cases do you use Function.prototype.construct() in
JSX:eek:bject.js?

If Eclipse JSDT is right, currently only in httprequest.js:
[…]
But I think I have only used it there as proof-of-concept. Do you think
I should remove it?

I would say yes only in the name of library consistency if it is the
only one. (Why can I only do "new jsx.HTTPRequest(...)" but not "new
jsx.OtherObject(...)"?);

ACK, I have removed it (in httprequest.js renamed to http.js).
I might re-add the feature later when all interfaces have become
stable. Until then, Pythons can write their own wrappers ;-)

BTW, the reorg has been committed as well. You might find
the new revisions a bit easier to read through. See also
<http://PointedEars.de/scripts/websvn> pp.
which provides syntax highlighting, among other features.


Regards,

PointedEars
 
D

Dmitry A. Soshnikov

You may use `bind` for that (own in ES3). And a special `construct`
function which accepts a list of arguments and creates a bound function
(with these partial arguments) from original one.


Something like this perhaps?

Function.construct = function(C){
var args = [];
for(var i = 1, len = arguments.length; i< len; i++)
args[i - 1] = "arguments[" + i + "]";
return eval("new C(" + args + ")");
}

function Point(){
if(!(this instanceof Point))
return Function.construct.apply(this,
[Point].concat(Array.slice(arguments)));
this.x = arguments[0];
this.y = arguments[1];
}

how would .bind(...) work?

E.g.:

Function.prototype.construct = function (args) {

var
boundArgs = [].concat.apply([null], args),
boundFn = this.bind.apply(this, boundArgs);

return new boundFn();

}

function Point(x, y) {
this.x = x;
this.y = y;
}

var point = Point.construct([2, 4]);

Additional info:

http://dmitrysoshnikov.com/notes/note-1-ecmascript-bound-functions/
https://developer.mozilla.org/en/JavaScript/bind

Dmitry.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top