Custom array object - javascript

I'm new to prototyping and instantiations and therefore had a question :
How can I create a function that constructs a new array that also has some properties added with prototype but without modifying the default Array function ?
For example :
function Cool_Object() {
this = new Array() // Construct new array.
//This is only for the example. I know you can't do that.
}
Cool_Object.prototype.my_method = function() {
// Some method added
};
So, if you call :
var myObject = new Cool_Object();
myObject would be an array and have a method called "my_method" (which actually calls a function).
But the default Array object would be intact.
Thanks in advance !

You've got it a bit backwards. Just use Array.prototype as your custom object's prototype.
function Cool_Object() {
this.my_method = function () {
return 42;
}
}
Cool_Object.prototype = Array.prototype;
var foo = new Cool_Object();
foo.my_method(); // 42
foo.push(13);
foo[0]; // 13
You can get both Array.prototype and my_method on Cool_Object's prototype, without modifying Array.prototype, by introducing an intermediate type:
function Even_Cooler() {}
Even_Cooler.prototype = Array.prototype;
function Cool_Object() {}
Cool_Object.prototype = new Even_Cooler();
Cool_Object.prototype.my_method = function () {
return 42;
}

You can't just assign to this, it doesn't work and throws a ReferenceError. Just make Cool_Object extend Array.
One way to do that:
var Cool_Object = Object.create(Array.prototype);
Cool_Object.my_method = function() {
// Some method added
};
Then create further objects with
var obj = Object.create(Cool_Object);

Use an array as the function's prototype, so that your new type "inherits" from Array, and then introduce new methods in the prototype:
function CustomArray() {}
CustomArray.prototype = [];
// introduce a new method to your custom array type
CustomArray.prototype.total = function() {
return this.reduce(function(ret, el) {
return ret+el;
}, 0);
};
// introduce another new method to your custom array type
CustomArray.prototype.arithmetiMean = function() {
return this.total()/this.length;
};
Alternately you could introduce those methods in new instances:
function CustomArray() {
// introduce a new method to your custom array object
this.total = function() {
return this.reduce(function(ret, el) {
return ret+el;
}, 0);
};
// introduce another new method to your custom array object
this.arithmetiMean = function() {
return this.total()/this.length;
};
}
CustomArray.prototype = [];
var arr = new CustomArray();
arr.push(1); // push is an array-standard method
arr.push(2);
arr.push(3);
arr.push(4);
arr.push(5);
arr.push(6);
arr.push(7);
arr.push(8);
arr.push(9);
arr.push(10);
console.log(arr.arithmetiMean());

function PseudoArray() {
};
PseudoArray.prototype = Object.defineProperties(Object.create(Array.prototype), {
constructor: {value:PseudoArray}
})

Adding this for reference, since Object.create is supported in most browsers these days, a good way to make your own array object would be like this:
function MyCustomArray(){
}
MyCustomArray.prototype = $.extend(Object.create(Array.prototype), {
/* example of creating own method */
evenonly : function(){
return this.filter(function(value){return (value % 2 == 0);});
},
/* example for overwriting existing method */
push : function(value){
console.log('Quit pushing me around!');
return Array.prototype.push.call(this, value);
}
});
var myca = new MyCustomArray();
myca instanceof MyCustomArray /*true*/
myca instanceof Array /*true*/
myca instanceof Object /*true*/
myca.push(1); /*Quit pushing me around!*/
myca.push(2); /*Quit pushing me around!*/
myca.push(3); /*Quit pushing me around!*/
myca.push(4); /*Quit pushing me around!*/
myca.push(5); /*Quit pushing me around!*/
myca.push(6); /*Quit pushing me around!*/
myca.length; /*6*/
myca.evenonly() /*[2, 4, 6]*/
Using jQuery's $.extend, because it's convenient to keep code structured, but there's no need for it, you could do this instead:
MyCustomArray.prototype = Object.create(Array.prototype);
MyCustomArray.prototype.push = function(){...}
I much prefer defining the methods on the prototype rather than putting them inside the constructor. It's cleaner and saves your custom array object from being cluttered with unnecessary functions.

Related

Extends ES5 like in ES6 [duplicate]

I realize that, strictly speaking, this is not subclassing the array type, but will this work in the way one might expect, or am I still going to run into some issues with .length and the like? Are there any drawbacks that I would not have if normal subclassing were an option?
function Vector()
{
var vector = [];
vector.sum = function()
{
sum = 0.0;
for(i = 0; i < this.length; i++)
{
sum += this[i];
}
return sum;
}
return vector;
}
v = Vector();
v.push(1); v.push(2);
console.log(v.sum());
I'd wrap an array inside a proper vector type like this:
window.Vector = function Vector() {
this.data = [];
}
Vector.prototype.push = function push() {
Array.prototype.push.apply(this.data, arguments);
}
Vector.prototype.sum = function sum() {
for(var i = 0, s=0.0, len=this.data.length; i < len; s += this.data[i++]);
return s;
}
var vector1 = new Vector();
vector1.push(1); vector1.push(2);
console.log(vector1.sum());
Alternatively you can build new prototype functions on arrays and then just use normal arrays.
If you are consistent with naming the arrays so they all start with a lowercase v for example or something similar that clearly mark them aw vector and not normal arrays, and you do the same on the vector specific prototype functions, then it should be fairly easy to keep track of.
Array.prototype.vSum = function vSum() {
for(var i = 0, s=0.0, len=this.length; i < len; s += this[i++]);
return s;
}
var vector1 = [];
vector1.push(1); vector1.push(2);
console.log(vector1.vSum());
EDIT -- I originally wrote that you could subclass an Array just like any other object, which was wrong. Learn something new every day. Here is a good discussion
http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
In this case, would composition work better? i.e. just create a Vector object, and have it backed by an array. This seems to be the path you are on, you just need to add the push and any other methods to the prototype.
Nowadays you could use subclassing with ES6 classes:
class Vector extends Array {
sum(){
return this.reduce((total, value) => total + value)
}
}
let v2 = new Vector();
v2.push(1);
v2.push(2);
console.log(v2.sum());
console.log(v2.length);
v2.length = 0;
console.log(v2.length);
console.log(v2);
Just another example of the wrapper. Having some fun with .bind.
var _Array = function _Array() {
if ( !( this instanceof _Array ) ) {
return new _Array();
};
};
_Array.prototype.push = function() {
var apContextBound = Array.prototype.push,
pushItAgainst = Function.prototype.apply.bind( apContextBound );
pushItAgainst( this, arguments );
};
_Array.prototype.pushPushItRealGood = function() {
var apContextBound = Array.prototype.push,
pushItAgainst = Function.prototype.apply.bind( apContextBound );
pushItAgainst( this, arguments );
};
_Array.prototype.typeof = (function() { return ( Object.prototype.toString.call( [] ) ); }());
#hvgotcodes answer has an awesome link. I just wanted to summerize the conclusion here.
Wrappers. Prototype chain injection
This seems to be the best method to extend array from the article.
wrappers can be used ... in which object’s prototype chain is augmented, rather than object itself.
function SubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
arr.__proto__ = SubArray.prototype;
return arr;
}
SubArray.prototype = new Array;
// Add custom functions here to SubArray.prototype.
SubArray.prototype.last = function() {
return this[this.length - 1];
};
var sub = new SubArray(1, 2, 3);
sub instanceof SubArray; // true
sub instanceof Array; // true
Unfortunally for me, this method uses arr.__proto__, unsupported in IE 8-, a browser I have to support.
Wrappers. Direct property injection.
This method is a little slower than the above, but works in IE 8-.
Wrapper approach avoids setting up inheritance or emulating length/indices relation. Instead, a factory-like function can create a plain Array object, and then augment it directly with any custom methods. Since returned object is an Array one, it maintains proper length/indices relation, as well as [[Class]] of “Array”. It also inherits from Array.prototype, naturally.
function makeSubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
// Add custom functions here to arr.
arr.last = function() {
return this[this.length - 1];
};
return arr;
}
var sub = makeSubArray(1, 2, 3);
sub instanceof Array; // true
sub.length; // 3
sub.last(); // 3
There is a way that looks and feels like prototypical inheritance, but it's different in only one way.
First lets take a look at one of the standard ways of implementing prototypical inheritance in javascript:
var MyClass = function(bar){
this.foo = bar;
};
MyClass.prototype.awesomeMethod = function(){
alert("I'm awesome")
};
// extends MyClass
var MySubClass = function(bar){
MyClass.call(this, bar); // <- call super constructor
}
// which happens here
MySubClass.prototype = Object.create(MyClass.prototype); // prototype object with MyClass as its prototype
// allows us to still walk up the prototype chain as expected
Object.defineProperty(MySubClass.prototype, "constructor", {
enumerable: false, // this is merely a preference, but worth considering, it won't affect the inheritance aspect
value: MySubClass
});
// place extended/overridden methods here
MySubClass.prototype.superAwesomeMethod = function(){
alert("I'm super awesome!");
};
var testInstance = new MySubClass("hello");
alert(testInstance instanceof MyClass); // true
alert(testInstance instanceof MySubClass); // true
The next example just wraps up the above structure to keep everything clean. And there is a slight tweak that seems at first glance to perform a miracle. However, all that is really happening is each instance of the subclass is using not the Array prototype as a template for construction, but rather an instance of an Array - so the prototype of the subclass comes hooked onto the end of a fully loaded object which passes the ducktype of an array - which it then copies. If you still see something strange here and it bothers you, I'm not sure that I can explain it better - so maybe how it works is a good topic for another question. :)
var extend = function(child, parent, optionalArgs){ //...
if(parent.toString() === "function "+parent.name+"() { [native code] }"){
optionalArgs = [parent].concat(Array.prototype.slice.call(arguments, 2));
child.prototype = Object.create(new parent.bind.apply(null, optionalArgs));
}else{
child.prototype = Object.create(parent.prototype);
}
Object.defineProperties(child.prototype, {
constructor: {enumerable: false, value: child},
_super_: {enumerable: false, value: parent} // merely for convenience (for future use), its not used here because our prototype is already constructed!
});
};
var Vector = (function(){
// we can extend Vector prototype here because functions are hoisted
// so it keeps the extend declaration close to the class declaration
// where we would expect to see it
extend(Vector, Array);
function Vector(){
// from here on out we are an instance of Array as well as an instance of Vector
// not needed here
// this._super_.call(this, arguments); // applies parent constructor (in this case Array, but we already did it during prototyping, so use this when extending your own classes)
// construct a Vector as needed from arguments
this.push.apply(this, arguments);
}
// just in case the prototype description warrants a closure
(function(){
var _Vector = this;
_Vector.sum = function sum(){
var i=0, s=0.0, l=this.length;
while(i<l){
s = s + this[i++];
}
return s;
};
}).call(Vector.prototype);
return Vector;
})();
var a = new Vector(1,2,3); // 1,2,3
var b = new Vector(4,5,6,7); // 4,5,6,7
alert(a instanceof Array && a instanceof Vector); // true
alert(a === b); // false
alert(a.length); // 3
alert(b.length); // 4
alert(a.sum()); // 6
alert(b.sum()); // 22
Soon we'll have class and the ability to extend native classes in ES6 but that may be a another year yet. In the mean time I hope this helps someone.
function SubArray(arrayToInitWith){
Array.call(this);
var subArrayInstance = this;
subArrayInstance.length = arrayToInitWith.length;
arrayToInitWith.forEach(function(e, i){
subArrayInstance[i] = e;
});
}
SubArray.prototype = Object.create(Array.prototype);
SubArray.prototype.specialMethod = function(){alert("baz");};
var subclassedArray = new SubArray(["Some", "old", "values"]);

Create a collection of js objects by deserialising a collection of json objects in node.js

I have an prototype class:
function temp(){
this.a=77;
}
temp.prototype.getValue = function(){
console.log(this.a);
}
and an array of json objects:
var x=[{a:21},{a:22},{a:23}];
Is there any way to instantiate an array of class temp directly using the array of json objects in a similar way to what generics helps us to achieve in Java using Jackson TypeReference.
var y= new Array(new temp());
//something similar to what Object.assign achieves for a single object
Thus can it be extended to other collection of objects like Map<obj1,obj2>,etc.
There is no built-in way to do that in Javascript. With a few assumptions about your data or about a constructor, you could fairly simply create your own function to create such an array:
// pass the constructor for the object you want to create
// pass an array of data objects where each property/value will be copied
// to the newly constructed object
// returns an array of constructed objects with properties initialized
function createArrayOfObjects(constructorFn, arrayOfData) {
return arrayOfData.map(function(data) {
let obj = new constructorFn();
Object.keys(data).forEach(function(prop) {
obj[prop] = data[prop];
});
return obj;
});
}
Or, you can create a constructor that takes an object of data and then initializes itself from that object:
// pass the constructor for the object you want to create
// pass an array of data objects that will each be passed to the constructor
// returns an array of constructed objects
function createArrayOfObjects(constructorFn, arrayOfData) {
return arrayOfData.map(function(data) {
return new constructorFn(data);
});
}
// constructor that initializes itself from an object of data passed in
function Temp(data) {
if (data && data.a) {
this.a = data.a;
}
}
You could directly fill an array with the instances of Temp with Array.from.
function Temp(){
this.a=77;
}
Temp.prototype.getValue = function(){
console.log(this.a);
}
var array = Array.from({ length: 5 }, _ => new Temp);
array[0].a = 42;
console.log(array);
In case you are using NPM, I strongly recommend linq-collections package for this kind of things.
https://www.npmjs.com/package/linq-collections

Can you use custom objects as properties of an object in javascript?

Suppose I create a custom object/javascript "class" (airquotes) as follows:
// Constructor
function CustomObject(stringParam) {
var privateProperty = stringParam;
// Accessor
this.privilegedGetMethod = function() {
return privateProperty;
}
// Mutator
this.privilegedSetMethod = function(newStringParam) {
privateProperty = newStringParam;
}
}
Then I want to make a list of those custom objects where I can easily add or remove things from that list. I decide to use objects as a way to store the list of custom objects, so I can add custom objects to the list with
var customObjectInstance1 = new CustomObject('someString');
var customObjectInstance2 = new CustomObject('someOtherString');
var customObjectInstance3 = new CustomObject('yetAnotherString');
myListOfCustomObjects[customObjectInstance1] = true;
myListOfCustomObjects[customObjectInstance2] = true;
myListOfCustomObjects[customObjectInstance3] = true;
and remove custom objects from the list with
delete myListOfCustomObjects[customObjectInstance1];
but if i try to iterate through the list with
for (i in myListOfCustomObjects) {
alert(i.privilegedGetMethod());
}
I would get an error in the FireBug console that says "i.privilegedGetMethod() is not a function". Is there a way to fix this problem or an idiom in javascript to do what I want? Sorry if this is a dumb question, but I'm new to javascript and have scoured the internet for solutions to my problem with no avail. Any help would be appreciated!
P.S. I realize that my example is super simplified, and I can just make the privateProperty public using this.property or something, but then i would still get undefined in the alert, and I would like to keep it encapsulated.
i won't be the original object as you were expecting:
for (i in myListOfCustomObjects) {
alert(typeof i); // "string"
}
This is because all keys in JavaScript are Strings. Any attempt to use another type as a key will first be serialized by toString().
If the result of toString() isn't somehow unique for each instance, they will all be the same key:
function MyClass() { }
var obj = {};
var k1 = new MyClass();
var k2 = new MyClass();
obj[k1] = {};
obj[k2] = {};
// only 1 "[object Object]" key was created, not 2 object keys
for (var key in obj) {
alert(key);
}
To make them unique, define a custom toString:
function CustomObject(stringParam) {
/* snip */
this.toString = function () {
return 'CustomObject ' + stringParam;
};
}
var obj = {};
var k1 = new CustomObject('key1');
var k2 = new CustomObject('key2');
obj[k1] = {};
obj[k2] = {};
// "CustomObject key1" then "CustomObject key2"
for (var key in obj) {
alert(key);
}
[Edit]
With a custom toString, you can set the object as the serialized key and the value to keep them organized and still continue to access them:
var customObjectInstance1 = new CustomObject('someString');
var customObjectInstance2 = new CustomObject('someOtherString');
var customObjectInstance3 = new CustomObject('yetAnotherString');
myListOfCustomObjects[customObjectInstance1] = customObjectInstance1;
myListOfCustomObjects[customObjectInstance2] = customObjectInstance2;
myListOfCustomObjects[customObjectInstance3] = customObjectInstance3;
for (i in myListOfCustomObjects) {
alert(myListOfCustomObjects[i].privilegedGetMethod());
}
The for iteration variable is just the index, not the object itself. So use:
for (i in myListOfCustomObjects) {
alert(myListOfCustomObjects[i].privilegedGetMethod());
}
and, in my opinion, if you use an Object as an array index / hash, it just would be converted to the string "Object", which ends up in a list with a single entry, because all the keys are the same ("Object").
myListOfCustomObjects =[
new CustomObject('someString'),
new CustomObject('someOtherString'),
new CustomObject('yetAnotherString')
]
you will get access to any element by index of array.

Trivial Inheritance with JavaScript

function StringStream() {}
StringStream.prototype = new Array();
StringStream.prototype.toString = function(){ return this.join(''); };
Calling new StringStream(1,2,3) gives an empty array
x = new StringStream(1,2,3)
gives
StringStream[0]
__proto__: Array[0]
Can someone please explain why the superclass' (Array) constructor is not called?
Just because StringStream.prototype is an array, the StringStream constructor is not replaced with Array as well.
You should implement that yourself: http://jsfiddle.net/gBrtf/.
function StringStream() {
// push arguments as elements to this instance
Array.prototype.push.apply(this, arguments);
}
StringStream.prototype = new Array;
StringStream.prototype.toString = function(){
return this.join('');
};

How to write a javascript clone function that can copy added object methods?

I have a javascript object cloning question. I'd like to be able to clone object methods that have been altered from those defined by the object prototype, or added to the object after instantiation. Is this possible?
The setting here is a javascript 'class' defined by me, so I'm fine with writing a clone method specific to my object class. I just can't figure out how to copy methods.
Example:
function myObject( name, att, dif ) {
/* 'privileged' methods */
this.attribute = function(newAtt) { // just a getter-setter for the 'private' att member
if(newAtt) { att = newAtt; }
return att;
}
// 'public' members
this.printName = name;
}
myObject.prototype.genericMethod = function() {
// does what is usually needed for myObjects
}
/* Create an instance of myObject */
var object153 = new myObject( '153rd Object', 'ABC', 2 );
// object153 needs to vary from most instances of myObject:
object153.genericMethod = function() {
// new code here specific to myObject instance object153
}
/* These instances become a collection of objects which I will use subsets of later. */
/* Now I need to clone a subset of myObjects, including object153 */
var copyOfObject153 = object153.clone();
// I want copyOfObject153 to have a genericMethod method, and I want it to be the one
// defined to be specific to object153 above. How do I do that in my clone() method?
// The method really needs to still be called 'genericMethod', too.
In your clone function, test each method on the object to see if it is equal to the same method on the object's constructor's prototype.
if (obj[method] != obj.constructor.prototype[method])
clone[method] = obj[method];
It sounds like you just want a shallow copy. However beware of that objects are shared among instances since we're not deep copying.
function clone(obj) {
var newObj = new obj.constructor();
for (var prop in obj) {
newObj[prop] = obj[prop];
}
return newObj;
}
var cloned = clone(object153);
A different syntax would be
myObj.prototype.clone = function() {
var newObj = new this.constructor();
for (var prop in this) {
newObj[prop] = this[prop];
}
return newObj;
}
var cloned = object153.clone();
Try it out and see if it works for you, it's still hard to tell what you're doing. If it doesn't, explain why, then I can better understand the problem.

Categories

Resources