"static" constructor functions?

N

nick

So, I was making a simple constructor function for a List pseudoclass
(correct terminology?), and it looked like this:
....

function List()
{
for (var i=arguments.length; i--; )
this[arguments] = true;
}

....
This is kind of useful; now I can can create simple lists and use
for...in to iterate over them, where the variable on the left hand
side of 'in' is the value I actually want, not an index to the value I
want. But now I thought, what if someone does not know that they need
to use 'new' when calling a function-as-constructor, and they try
something like this?

var trees = List('maple', 'oak', 'palm');

In this case 'this' would reference the parent object of the function,
not the new List object being constructed. The variable 'trees' would
not contain a list, and the parent object would have all kinds of tree
names as properties now. Not good. So I thought this might be safer:
....

// constructor for simple list
function List()
{
if (className(this)!='List')
return List.apply(new List(), arguments);
for (var i=arguments.length; i--; )
this[arguments] = true;
return this;
}

// class name
function className (o)
{
var noname = '(undefined)';
if (o===null || o===undefined || !o.constructor)
return noname;
var c = String(o.constructor);
var n = /function\s+(\w+)\b/.exec(c);
return n && n[1] ? n[1] : noname;
}

....
This seems to work great, and now lists can be created by using 'new
List(...)' or just 'List(...)', interchangeably. To me, this seems
like a more foolproof solution with less potential for accidental
misuse. The question is, am I going about it the right way? Extracting
the class name from the constructor seems a little hackish. Is there a
better way to tell what "class" an object is, or some way to determine
whether a function was called with the 'new' keyword or not? TIA for
any advice.

-- Nick
 
G

Garrett Smith

nick said:
So, I was making a simple constructor function for a List pseudoclass
(correct terminology?), and it looked like this:
...

function List()
{
for (var i=arguments.length; i--; )
this[arguments] = true;
}


If you use an array for a parameter, it makes it easier for
implementations to optimize the call.

function List(array) {
for (var i=0; i < array.length; i++) {
this[array] = true;
}
}
...
This is kind of useful; now I can can create simple lists and use
for...in to iterate over them, where the variable on the left hand
side of 'in' is the value I actually want, not an index to the value I
want. But now I thought, what if someone does not know that they need
to use 'new' when calling a function-as-constructor, and they try
something like this?

var trees = List('maple', 'oak', 'palm');

That would create three properties of the global object all having value
`true`, leaving `trees` with value `undefined`.
In this case 'this' would reference the parent object of the function,
not the new List object being constructed. The variable 'trees' would
not contain a list, and the parent object would have all kinds of tree
names as properties now. Not good. So I thought this might be safer:
...
The properties would be of the `global` object. I don't know what you
mean by "parent" object.
// constructor for simple list
function List()
{
if (className(this)!='List')
return List.apply(new List(), arguments);
for (var i=arguments.length; i--; )
this[arguments] = true;
return this;
}

// class name
function className (o)
{
var noname = '(undefined)';
if (o===null || o===undefined || !o.constructor)
return noname;
var c = String(o.constructor);
var n = /function\s+(\w+)\b/.exec(c);
return n && n[1] ? n[1] : noname;
}

...
This seems to work great, and now lists can be created by using 'new
List(...)' or just 'List(...)', interchangeably. To me, this seems
like a more foolproof solution with less potential for accidental
misuse. The question is, am I going about it the right way? Extracting
the class name from the constructor seems a little hackish. Is there a
better way to tell what "class" an object is, or some way to determine
whether a function was called with the 'new' keyword or not? TIA for
any advice.

There are other ways to go about doing that. You could use `this
instanceof List`, to start with.

The errors are easy enough to avoid and should be immediately obvious.
Just use `new`.
 
N

nick

If you use an array for a parameter, it makes it easier for
implementations to optimize the call.

Thanks for pointing that out. I'm really thinking more about concise
syntax here than optimization... I'm pretty sure calling List without
'new' in the second example will result in a total of 3 calls to List
-- the initial call, a constructor call with 'new' and another call
using 'apply'. So optimization is pretty much out the window here
anyway, although I suppose I should optionally allow an array as the
first parameter.
The properties would be of the `global` object. I don't know what you
mean by "parent" object.

Oops, I did say "in this case" didn't I? I was assuming the List
constructor would be in some library, so it would be a property of
some other object than the global object... that's all I meant by the
'parent' object.
There are other ways to go about doing that. You could use `this
instanceof List`, to start with.

Ah, instanceof is perfect, thank you!
The errors are easy enough to avoid and should be immediately obvious.
Just use `new`.

That's certainly true, I'm using 'new' with my lists anyway. I wanted
a way to be sure a constructor was being called with new, and I
figured instead of throwing an error I'd just have it call itself with
new if it was not being called with new, since that would be the only
possible legitimate intended use of that syntax. Thanks for pointing
me towards instanceof, it does exactly what I need.

-- Nick
 
J

John G Harris

On Sat, 23 Jan 2010 at 23:27:31, in comp.lang.javascript, nick wrote:

a List pseudoclass
(correct terminology?),
<snip>

Whatever you call it someone will complain. If you want to be posh and
mathematically correct you could call it a class of List objects.

John
 
A

Asen Bozhilov

nick said:
  function List()
  {
    for (var i=arguments.length; i--; )
      this[arguments] = true;
  }


What is the benefit from using native object created from List
[[Construct]] method?

function list()
{
var o = {};
for (var i = arguments.length; i--;)
{
o[arguments] = true;
}
return o;
}
 
N

nick

What is the benefit from using native object created from List
[[Construct]] method?

Nothing, really. I guess there's no way I can add any methods to List
(or function properties to List's prototype, you know what I mean)
without breaking for/in, so it probably shouldn't be a class anyway.
Still, I learned something useful. :)
 
A

Asen Bozhilov

nick said:
Nothing, really. I guess there's no way I can add any methods to List
(or function properties to List's prototype, you know what I mean)
without breaking for/in, so it probably shouldn't be a class anyway.
Still, I learned something useful. :)

At all this design for my is broken, because you mix your own
properties and allow user to inject properties in your own object. See
below:

function List()
{
for (var i = arguments.length; i--;)
{
this[arguments] = true;
}
}
List.prototype.get = function(){};
List.prototype.forEach = function(){};


var o = new List('prop1', 'prop2', 'prop3');
o.get(); //fine
o.forEach(); //fine

var o1 = new List('get', 'forEach');
try {
o1.get();
}catch(e) {
window.alert(e instanceof TypeError); //true
}

try {
o1.forEach();
}catch(e) {
window.alert(e instanceof TypeError); //true
}

So user of your constructor, can shadow properties in Prototype chain,
and that can be harmful and dangerous. You can use different
approach.

function List()
{
this._list = {};
for (var i = arguments.length; i--;)
{
this._list[arguments] = true;
}
}
List.prototype.get = function(){};
List.prototype.forEach = function(){};


var o = new List('get', 'forEach', 'test');
o.get();
o.forEach();

Regards ;)
 
R

RobG

What is the benefit from using native object created from List
[[Construct]] method?

Nothing, really. I guess there's no way I can add any methods to List
(or function properties to List's prototype, you know what I mean)
without breaking for/in,

Simple for..in is made more difficult but it's not fundamentally
broken. A bigger issue is items in your list shaddowing methods on the
prototype chain. If your List object has a sort method on it's
prototype, what happens when you create a list that has a member
'sort'?

so it probably shouldn't be a class anyway.

It can be implemented a number of ways, a "class" is just one. A list
instance could be an object with a list property that is an array that
is the list. That way, members of the list don't get in the way of the
object's properties and methods.

But then you might as well just use an array and have "list" functions
that operate on it. :)
 
G

Garrett Smith

nick said:
Thanks for pointing that out. I'm really thinking more about concise
syntax here than optimization... I'm pretty sure calling List without
'new' in the second example will result in a total of 3 calls to List
-- the initial call, a constructor call with 'new' and another call
using 'apply'. So optimization is pretty much out the window here
anyway, although I suppose I should optionally allow an array as the
first parameter.


Oops, I did say "in this case" didn't I? I was assuming the List
constructor would be in some library, so it would be a property of
some other object than the global object... that's all I meant by the
'parent' object.
I think by "parent" you mean the Base object.

If a function is called with no Base or with activation object as base,
then base is null. When Base is null, in ES3, the global object is used
as the `this` value for the call.

In ES5 strict mode, the `this` value is not coerced to an object; the
global object is not used implicitly. From the specification:

| If this is evaluated within strict mode code, then the this value is
| not coerced to an object. A this value of null or undefined is not
| converted to the global object and primitive values are not converted
| to wrapper objects. The this value passed via a function call
| (including calls made using Function.prototype.apply and
| Function.prototype.call) do not coerce the passed this value to an
| object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).

var isPrimitiveThis = (function(a){"use strict";
return a === this;
})();

In ES5 implementation, this should return true.
 
T

Thomas 'PointedEars' Lahn

Garrett said:
I think by "parent" you mean the Base object.

Which is not a proper name as well, thus must be written either lowercase or
with leading uppercase in all words, to avoid the ambiguity that `Base'
would be the proper name. Cf. "host object", which you also write wrong
despite being told several times.


PointedEars
 

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,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top