Creating an Object that extends Array functionality

W

wilq

Hello Usenet,

I got an interesting question and maybe some of you might have idea or
at least want to have some riddle to solve... whatever. I would like
to create own Object that would behave similar to Array Object, but
would have defined some methods that are not in current Array
implementation. It need to not touch .prototype of an Array, so it
should work like this:

var a = [2,4,6];

a.someAddedFunction(); // error - no Array .prototype extending

var c = new SuperArray([2,4,6]);
for (var i=0,l=c.length;i<l;i++)
{
alert(c); // alerts 2, then 4, then 6
}

c.someAddedFunction(); // calls function


Have you got any idea how to do that? Is this possible at all? Thanks
for any answers here.
 
E

Evertjan.

wilq wrote on 27 okt 2009 in comp.lang.javascript:
Hello Usenet,

Try:

Hello NG,
I got an interesting question and maybe some of you might have idea or
at least want to have some riddle to solve... whatever. I would like
to create own Object that would behave similar to Array Object, but
would have defined some methods that are not in current Array
implementation.

You cannot add a method to the array in it's strict sense,
but to the object that it is as well.
It need to not touch .prototype of an Array, so it
should work like this:

var a = [2,4,6];

a.someAddedFunction(); // error - no Array .prototype extending

No error here if you do:

var a = [2,4,6];
a.fx = function(n){return this[n]};
alert( a.fx(1) ); // 4


Chrome tested.
 
R

Richard Cornford

Hello Usenet,

I got an interesting question and maybe some of you might
have idea or at least want to have some riddle to solve...
whatever. I would like to create own Object that would
behave similar to Array Object,

Much of the behaviour of an Array in javascript codes from its special
[[Put]] method (which has special handling for 'array index' and -
length - property names), this method cannot be inherited through a
prototype chain or transferred between objects so an object that
behaves like an Array is probably going to have to be an Array, though
possibly a modified one.
but would have defined some methods that are not in current
Array implementation.

So what might be called an 'extended array'.
It need to not touch .prototype of an Array, so it

Good. That reduces your options to creating array instances and
assigning functions to their named properties to provide additional
method (and not using for-in loops on those object, or filtering the
for-in loops used so they don't act on the added methods).
should work like this:

var a = [2,4,6];

a.someAddedFunction(); // error - no Array .prototype extending

var c = new SuperArray([2,4,6]);

//Order of execution matters here.
var getSuperArrayInstance = (function(){
function forSomeAddedFunction(){
// Code that can use - this - to refer to the array (itself).
}
return (function(array){
/* This could either modify the array argument or return a new
array that is a copy of the original array. Here only the
former will be done.
*/
/*Next a reference to a (by now) existing function is assigned
to a named property of the array passed in, giving it an
additional method.
*/
array.someAddedFunction = forSomeAddedFunction;
/* The modified array is returned. Essential if an internally
created copy of the original array had been used but
possibly not required if this function's task is only to
add a new interface to an object passed in.
*/
return array;
});
})():

var c = getSuperArrayInstance([2,4,6]);
for (var i=0,l=c.length;i<l;i++)
{
alert(c); // alerts 2, then 4, then 6

}

c.someAddedFunction(); // calls function

Have you got any idea how to do that?


As above, and variations on the theme.
Is this possible at all?

Up to a point, that satisfies practical requirements.
Thanks for any answers here.

Richard.
 
R

Richard Cornford

Richard said:
Hello Usenet, [...]
should work like this:
var a = [2,4,6];
a.someAddedFunction();  // error - no Array .prototype extending
var c = new SuperArray([2,4,6]);
 //Order of execution matters here.
 var getSuperArrayInstance = (function(){
    function forSomeAddedFunction(){
        // Code that can use - this - to refer to the array (itself).
    }
    return (function(array){

I've never seen anyone else wrapping function expression in return
statements with parentheses. Are there any environments that fail
otherwise or is this a convention/habit?
<snip>

I am not aware of any problematic environments, I am just in the habit
of wrapping expressions that get returned in parenthesise if they are
anything but the simplest expressions, and muti-line function
expressions certainly do not qualify as simple expressions.

Richard.
 
J

Jorge

Much of the behaviour of an Array in javascript codes from its special
[[Put]] method (which has special handling for 'array index' and -
length - property names), this method cannot be inherited through a
prototype chain or transferred between objects so an object that
behaves like an Array is probably going to have to be an Array, though
possibly a modified one.

Isn't it possible (by any means other than Array.prototype or
Object.protoype) to force an [] to *inherit* .someAddedMethod() ?
There's no way to insert an additional object in its prototype chain ?
 
J

Jorge

Jorge said:
Much of the behaviour of an Array in javascript codes from its special
[[Put]] method (which has special handling for 'array index' and -
length - property names), this method cannot be inherited through a
prototype chain or transferred between objects so an object that
behaves like an Array is probably going to have to be an Array, though
possibly a modified one.
Isn't it possible (by any means other than Array.prototype or
Object.protoype) to force an [] to *inherit* .someAddedMethod() ?

There's no way to inherit "special" [[Put]]. That's the actual "problem"
here.

Yes yes I understand that. But if you could insert an additional
object (with the .someAddedMethod()) in the prototype chain of an [],
you wouldn't need to add any own properties in order to convert it
into a superArray instance...
 
J

Jorge

Jorge said:
Yes yes I understand that. But if you could insert an additional
object (with the .someAddedMethod()) in the prototype chain of an [],
you wouldn't need to add any own properties in order to convert it
into a superArray instance...

Oh, you mean something like this?

var arr = [1,2,3];

arr.__proto__ = {
   last: function() {
     return this[this.length-1];
   },
   __proto__: Array.prototype

};

arr.last(); // 3
arr.push('foo');

arr.length; // 4

:)

but can't use __proto__ ... : an Array.create(prototypeObject).
 
J

Jorge

What's `Array.create`?

If at all possible, it ought to be like Crockford's Object.create
(prototypeObject); but for Arrays: something like this:

Array.create= function (o) {
function f () { return []; }
f.prototype= o;
return new f();
}

that works (unlike this).
I actually don't see why you would want to have method in proto chain of
an object instead of just assigning that method to an object directly.

Because that's what inheritance is there for: to share a single method
among all the instances of a class.
Why not in Array.prototype ?
Because that would turn each and every Array into an instance of
superArray.
Why not to augment instead ?
Because we've got inheritance for a reason: this reason.
Latter one is simpler and much more compatible.

In the pre-ES5 era, yes.
 
R

Richard Cornford

On Oct 27, 6:42 pm, kangax wrote:

In the pre-ES5 era, yes.

No, it is the post ES 3 era in which the latter is not more
compatible. Currently we are only in the post ES 2 era (where try-
catch became tolerable, if not actually that useful).

Richard.
 
T

Thomas 'PointedEars' Lahn

kangax said:
var arr = [1,2,3];

arr.__proto__ = {
last: function() {
return this[this.length-1];
},
__proto__: Array.prototype
};

arr.last(); // 3
arr.push('foo');

arr.length; // 4

For a sensible test, that needs to be arr.last() == "foo";


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Jorge said:
Jorge wrote:
Yes yes I understand that. But if you could insert an additional
object (with the .someAddedMethod()) in the prototype chain of an [],
you wouldn't need to add any own properties in order to convert it
into a superArray instance...
Oh, you mean something like this?

var arr = [1,2,3];

arr.__proto__ = {
last: function() {
return this[this.length-1];
},
__proto__: Array.prototype

};

arr.last(); // 3
arr.push('foo');

arr.length; // 4

:)

but can't use __proto__ ... : an Array.create(prototypeObject).

What's `Array.create`?

I actually don't see why you would want to have method in proto chain of
an object instead of just assigning that method to an object directly.

Latter one is simpler

But less memory efficient with more than one instance.
and much more compatible.

Only if you use __proto__.


PointedEars
 
A

Asen Bozhilov

I got an interesting question and maybe some of you might have idea or
at least want to have some riddle to solve... whatever. I would like
to create own Object that would behave similar to Array Object, but
would have defined some methods that are not in current Array
implementation. It need to not touch .prototype of an Array, so it
should work like this:

var a = [2,4,6];

a.someAddedFunction(); š// error - no Array .prototype extending

When i define helper methods for arrays, i never use Array.prototype
to add new properties. I want to keep clear for-in, because every
third party properties added to `object' referred from Array.prototype
doesn't haves property {DontEnum}.

Approach like that:
var a = new Array();
a.someAddedFunction = function(){};

Here `a' referred `object' created from Array.[[Construct]] method. In
the next line, will be added property `someAddedFunction' to that
`object' and assigned reference to function. That property doesn't
have {DontEnum}, so will be enumerated from for-in loop. Not only
this. If you use Richard Cornford pattern, on the every `object'
passed for argument, will be added new properties on thy fly and
assigned value to that properties. That is more memory unefficient,
and that properties doesn't haves {DontEnum} attribute.

If you want, you can use `object' referred from property of Global
Object `Array', to defined helper methods and passed reference to
`object' who internal [[Prototype]] refer Array.prototype.
e.g.
Array.someAddedFunction = function(arr){};

Disadvantage of that technique is value of argument `arr'. You might
need to checked value. Because you don't sure that value is reference
to `object' who [[Prototype]] referred Array.prototype.

Regards.
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas said:
But less memory efficient with more than one instance.

Hmmm, true.

So something like this then? (after feature-testing __proto__ behavior,
of course):

var augment = (function(){
var mixin = {
last: function() {
return this[this.length-1];
},
__proto__: Array.prototype
};
return function(object) {
object.__proto__ = mixin;
return object;
};
})();

var arr = augment([1,2,3]);
[...]

Not really, see below.
Only if you use __proto__.

How else can you assign to object's [[Prototype]]?

In most cases, a standards-compliant reference to the object's prototype
object is known; in this case, `Array.prototype'. In fact, I do not think
there is much value in replacing the prototype object of Array instances
with an object that has the original value of `Array.prototype' next in its
prototype chain. The only advantage of this approach that I can think of is
that properties inherited from Array.prototype could be shadowed without
overwriting them.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Asen said:
I got an interesting question and maybe some of you might have idea or
at least want to have some riddle to solve... whatever. I would like
to create own Object that would behave similar to Array Object, but
would have defined some methods that are not in current Array
implementation. It need to not touch .prototype of an Array, so it
should work like this:

var a = [2,4,6];

a.someAddedFunction(); // error - no Array .prototype extending

When i define helper methods for arrays, i never use Array.prototype
to add new properties. I want to keep clear for-in, because every
third party properties added to `object' referred from Array.prototype
doesn't haves property {DontEnum}.

That reasoning is questionable, though. Why are you using a `for-in'
statement to iterate over Array instances to begin with?
Approach like that:
var a = new Array();
a.someAddedFunction = function(){};

That is OK for one instance, but if you have a number of instances you would
waste a lot of memory with that approach. That could be mitigated with
creating the function only once and only assign a reference to it, but it
would be comparably tedious anyway. I am not sure if for-in is worth all
that.
[...]
If you want, you can use `object' referred from property of Global
Object `Array', to defined helper methods and passed reference to
`object' who internal [[Prototype]] refer Array.prototype.
e.g.
Array.someAddedFunction = function(arr){};

Disadvantage of that technique is value of argument `arr'. You might
need to checked value. Because you don't sure that value is reference
to `object' who [[Prototype]] referred Array.prototype.

The argument is unnecessary for operating on one Array instance; the
function can use `this', regardless to which property a reference to it is
assigned at first. Having the object referred to by `Array' as a repository
of possible prototype methods is an interesting idea, theoretically; I am
just not sure of how much practical use that would be.


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas said:
kangax said:
Thomas 'PointedEars' Lahn wrote:
kangax wrote:
I actually don't see why you would want to have method in proto chain
of an object instead of just assigning that method to an object
directly.

Latter one is simpler
[...]
and much more compatible.
Only if you use __proto__.
How else can you assign to object's [[Prototype]]?

In most cases, a standards-compliant reference to the object's prototype
object is known; in this case, `Array.prototype'. In fact, I do not
think there is much value in replacing the prototype object of Array
instances with an object that has the original value of `Array.prototype'
next in its prototype chain. The only advantage of this approach that I
can think of is that properties inherited from Array.prototype could be
shadowed without overwriting them.

Perhaps, you should read this thread again more carefully?

Or perhaps you should?

As I understand it, this thread is about creating an object that works like
an Array instance but has additional features. Because the [[Put]] method
of Array instances cannot be inherited (your push() tests the wrong
property), that can only be accomplished with a true Array instance, and
there are two ways to provide it with new properties: a) augment the object
itself; b) augment its prototype object so that those properties are
inherited. As for b), [].__proto__ === Array.prototype, so using the less
compatible `__proto__' property is unnecessary. (The same goes for user-
defined objects, for which the prototype object can be referred to by either
Object.prototype or usually UserDefinedConstructor.prototype.) There is
also little value in the extended prototype chain that you proposed as the
method can easily be added to that Array prototype directly. (It is all the
same to for-in iteration.)

There is a c) which has not been mentioned in this thread yet (but we've
been over this): Use a wrapper object, and map properties inherited from the
Array prototype accordingly.


PointedEars
 
V

VK

wilq said:
I got an interesting question and maybe some of you might have idea or
at least want to have some riddle to solve... whatever. I would like
to create own Object that would behave similar to Array Object, but
would have defined some methods that are not in current Array
implementation. It need to not touch .prototype of an Array, so it
should work like this:

var a = [2,4,6];

a.someAddedFunction();  // error - no Array .prototype extending

var c = new SuperArray([2,4,6]);
for (var i=0,l=c.length;i<l;i++)
{
   alert(c); // alerts 2, then 4, then 6

}

c.someAddedFunction(); // calls function

Have you got any idea how to do that? Is this possible at all? Thanks
for any answers here.


It was already answered not long time ago:
http://groups.google.com/group/comp.lang.javascript/msg/17ca24e86760231b
 
A

Asen Bozhilov

That reasoning is questionable, though.  Why are you using a `for-in'
statement to iterate over Array instances to begin with?

Where is the wrong here? For-in is build in iterator. Iterate over any
properties of `object' who doesn't have attribute {DontEnum}. Decimal
indexes of array object itself is a properties of that object.

When i open PrototypeJS lib API, to Array object.
<URL:http://api.prototypejs.org/language/array.html />

<quote>
Why you should stop using for...in to iterate?
</quote>

Please anybody, explain why, because in url above, explanation is
related with PrototypeJS implementation of array add-on methods.
 
D

Dmitry A. Soshnikov

[...]
Good. That reduces your options to creating array instances and
assigning functions to their named properties to provide additional
method (and not using for-in loops on those object, or filtering the
for-in loops used so they don't act on the added methods).
[...]

And what's the main goal do not extend Array.prototype if you still
avoid for-in loops for you single in-place extended objects?

for-in loop is only useful for sparse arrays (better - very sparse
arrays, with indexes: 0, 1, 99, 502) and for such structures better to
use Object (without afraid for-in loops as no one in good sense will
extend Object.prototype).
 
D

Dmitry A. Soshnikov

Thomas said:
kangax wrote:
Thomas 'PointedEars' Lahn wrote:
kangax wrote:
Thomas 'PointedEars' Lahn wrote:
kangax wrote:
I actually don't see why you would want to have method in proto chain
of an object instead of just assigning that method to an object
directly.
Latter one is simpler
[...]
and much more compatible.
Only if you use __proto__.
How else can you assign to object's [[Prototype]]?
In most cases, a standards-compliant reference to the object's prototype
object is known; in this case, `Array.prototype'.  In fact, I do not
think there is much value in replacing the prototype object of Array
instances with an object that has the original value of `Array.prototype'
next in its prototype chain.  The only advantage of this approach that I
can think of is that properties inherited from Array.prototype could be
shadowed without overwriting them.
Perhaps, you should read this thread again more carefully?
Or perhaps you should?

Are you sure you haven't missed anything?

"I would like to create own Object that would behave similar to Array
Object, but would have defined some methods that are not in current
                                                      ^^^^^^^^^^^^^^
Array implementation. It need to not touch .prototype of an Array, so it
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
should work like this"

But anyway, the wrapper you created has enumerable [.last] method.
What's the main goal to afraid Array.prototype itself in this case if
still for-in loops will affect on your wrapped object properties?
Why wrong property? Doesn't `push` affect array object's `length`?

[...]

[.push] can affect `length` property and without wrappers:

var o = {
push: [].push
};
o.push(1);
o.length; // 1

But sure in this case `length` will be enumerable and seen in for-in.

P.S.> Who said "do not extend Array.prototype"? As all this stuff with
in-place adding methods to array single objects or with wrappers -
will show that added methods in for-in? So you should still "afraid"
of for-in. Why do not extend Array.prototype then if needed?

If the only reason - "we don't know who will use our library, so we
won't extend Array.prototype that they can use for-in for arrays" -
that's ok, that's another question, I agree in here. But in own
project - I don't see any troubles for do not augment Array.prototype.
Only if you iterate strong sparse arrays, but for that better to use
non-array object.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top