Why is the JSON stringification of a subclassed array an object? - javascript

/* StackOverflow needs a console API */ console.log = function(x) { document.write(x + "<br />"); };
B = function() {}
B.prototype = Array.prototype;
var a = new Array();
var b = new B();
a[0] = 1;
b[0] = 1;
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
JSON stringifies the subclass as an object ( { "0": 1 } ) instead of as an array ( [1] )`
Is there any way to modify this behaviour?
EDIT
I'm using (non-negotiably) ES5. I've simplified the example slightly. In reality, the subclassing is set up through a function inherit() which does this:
var inherit = function(base, derived) {
function F() {}
F.prototype = base.prototype;
derived.prototype = new F();
derived.prototype.constructor = derived;
};

As far as I know, you cannot inherit from array. As soon as you create a constructor function, the instances of it will be objects. When you want the functionality of an array, rather create an array and add the methods on it you want. This can be done with a function:
function createExtendedArray () {
var a = [];
a.method1 = function() {};
return a;
}

Related

Javascript Inheritance: One Subclass gets the Prototype, the other doesn't

I have a SuperClass "class", and this class is to be inherited (via the prototype chain) by SubClassA and SubClassB. However, while the inheritance appears to work for SubClassA, it fails for SubClassB. The code is below:
function SuperClass(childCell){
this.childCell = childCell;
this.children = new Array(9);
for(i=0; i<9; i++) {
this.children[i] = new this.childCell();
}
}
function SubClassA(){
this.num = 1;
}
SubClassA.prototype = new SuperClass(SubClassB);
function SubClassB(){
this.num = 2;
}
SubClassB.prototype = new SuperClass(SubClassC);
function SubClassC(){
this.num = 3;
}
var x = new SubClassA();
In this code, I set x to an object of SubClassA, and this should in turn give me a children property containing 9 SubClassB objects. It does this properly, but in turn, each SubClassB object should contain 9 SubClassC objects. However, after inspecting the console, I found that none of the SubClassB objects actually contain the childCell or the children properties that it was supposed to inherit via prototype.
In other words, x.children[0] returned SubClassB {num: 2}, and had none of the other properties.
Why does the inheritance work for SubClassA but not SubClassB?
try reorder your declaration sample like
function Parent(childCell){
this.childCell = childCell;
this.children = new Array(9);
for(var i=0; i<9; i++) {
this.children[i] = new this.childCell();
}
}
function ChildA(){
this.num = 1;
}
function ChildB(){
this.num = 2;
}
function ChildC(){
this.num = 3;
}
ChildB.prototype = new Parent(ChildC);
ChildA.prototype = new Parent(ChildB);
your problem - you call ChildB constructor before you add prototype to it
UPDATE
#Bagavatu when you create object you use prototype which set for constructor function, then you can change prototype properties and this changes will be apply to all objects with this prototype.
In your case you change reference to prototype so it does not apply to object which was created before. You can test it in simple example
function A() {this.cell = 10}
function B() {this.num =1}
var b1 = new B(); // b1 = {num:1}
B.prototype = new A();
var b2 = new B(); // b1 = {num:1}, b2 = {num:1, cell:10}
I usually dont dig that much deep.But when we use sub classing in javascript,folllow the following pattern.
function Superclass() { }
Superclass.prototype.someFunc = function() { };
function Subclass() { }
Subclass.prototype = new Superclass();
Subclass.prototype.anotherFunc = function() { };
var obj = new Subclass();

How to iterate over an object's prototype's properties

I have some code:
var obj = function() { }; // functional object
obj.foo = 'foo';
obj.prototype.bar = 'bar';
for (var prop in obj) {
console.log(prop);
}
What surprised me is that all that is logged is foo. I expected the for loop to iterate over the properties of the obj's prototype as well (namely bar), because I did not check for hasOwnProperty. What am I missing here? And is there an idiomatic way to iterate over all the properties in the prototype as well?
I tested this in Chrome and IE10.
Thanks in advance.
You're iterating over the constructor's properties, you have to create an instance. The instance is what inherits from the constructor's prototype property:
var Ctor = function() { }; // constructor function
Ctor.prototype.bar = 'bar';
var obj = new Ctor(); // instantiation
// adds own property to instance
obj.foo = 'foo';
// logs foo and bar
for (var prop in obj) {
console.log(prop);
}
If you want to maintain an inheritance hierarchy by defining all the properties even before the object instantiation, you could follow the below approach. This approach prints the prototype hierarchy chain.
Note: In this approach you don't have to create the constructor initially.
function myself() {
this.g = "";
this.h = [];
this.i = {};
myself.prototype = new parent();
myself.prototype.constructor = myself;
}
function parent() {
this.d = "";
this.e = [];
this.f = {};
parent.prototype = new grandParent();
parent.prototype.constructor = parent;
}
function grandParent() {
this.a = "";
this.b = [];
this.c = {};
}
var data = new myself();
var jsonData = {};
do {
for(var key in data) {
if(data.hasOwnProperty(key) && data.propertyIsEnumerable(key)) {
jsonData[key] = data[key];
}
}
data = Object.getPrototypeOf(data).constructor.prototype;
Object.defineProperties(data, {
'constructor': {
enumerable: false
}
});
} while (data.constructor.name !== "grandParent")
console.log(jsonData);

Programmatically create a new object

I am trying to create a function which will take arguments arg1, arg2... then pass them into a constructor for a new object C like so: new C(arg1, arg2...), so to make a new instance of C the user would simply have to call C(arg) instead of new C(arg). Here is my first attempt:
var C = function(a){ this.a = a; }
var Cn = function(){
new C.apply(this, arguments);
}
Cn(0) // Should make a new C with a property a equal to 0
new C(0) // ie the same as this
Edit: Note, I need it to take an arbitrary number of arguments and not use eval. I'm making a library implementing Algebraic Data Types in js.
Edit: The solution was to take Jeremy's Idea and adapt it to take an unbounded number of arguments:
var C = function() {
// A unique object so we can identify when we used the 'newless' constructor
var newlessConstructorObj = {}
// Check to see if C has been called with `new`
if(!(this instanceof C))
// If not pass arguments as single arg back to C
return new C(newlessConstructorObj, arguments);
// Check to see if we got here from the line above, if so the arguments were passed in the second arg
var args = (arguments[0] === newlessConstructorObj) ? arguments[1] : arguments
// Do stuff with args
this.a = args[0];
}
C(0);
new C(0);
If you want to be able to call the function with or without the new keyword, you have to follow this pattern:
C = function(a) {
if (!(this instanceof C)) {
return new C(a);
}
this.a = a;
}
so to create a new instance of "C":
c = new C(a);
and
c = C(a);
will both return a properly formed instance of C
I would have picked Fabrizio Calderan's solution any day, but because you want this specific functionality - here is what predecessors tell us:
you can apply arguments to prototype.constructor (but there are issues when doing it with native types, like Number):
var Cn = function(){
return C.prototype.constructor.apply(C, arguments);
}
Link: Instantiating a JavaScript object by calling prototype.constructor.apply
or.. use eval:
function construct(Constructor)
{
/*
* or Array.prototype.slice.call(arguments, 1).map(function() { ... })
* in JavaScript 1.6+, compatibles, and with augmented Array.prototype
*/
var args = [];
for (var i = 1, len = arguments.length; i < len; i++)
{
args[i - 1] = "arguments[" + i + "]";
}
/* or args.join(", ") if you need it pretty-printed */
return eval("new Constructor(" + args + ")");
}
function Foo()
{
window.alert(Array.prototype.slice.call(arguments, 0).join(", "));
}
var f = construct(Foo, /bar/g, {baz: 42});
Link: http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/ff1a104bdc33d5c8
something like this?
var C = function(obj){
var a;
for (a in obj) {
this[a] = obj[a];
}
}
var Cn = function(obj) { return new C(obj); }
instance = Cn({
a : 1,
b : 2
})
instance.a //1
instance.b //2
instance.c //undefined
Doing it with a fixed number of args is simple:
// assuming one arg
function Cn(arg) {
return new C(arg);
}
// assuming two args
function Cn(arg0, arg1) {
return new C(arg0, arg1);
}
and so on. You can even make a general version for any number of parameters by iterating over arguments to create a string, then eval it. Crass, but effective.
But what's the point? Just to save typing 4 characters?
If you don't care about having a correct instanceof check you can try this:
var C = function(a){ this.a = a; }
var Cn = function(){
return C.apply({}, arguments); // notice an empty object here
}
Cn(0) // Should make a new C with a property a equal to 0
new C(0) // ie the same as this
Updated for ES6, you can use the spread operator:
var C = function(a){ this.a = a; }
var Cn = function(...args){
return new C(...args);
}
assert.deepStrictEqual(Cn(10), new C(10));

cloning an object in Javascript maintaining instanceof

I want to clone an object in Javascript. I have:
iPath = function () { this.heading = 0; this.path = []; };
loop = new iPath();
I know with jQuery I can do something like:
cloneLoop = $.extend(true, {}, loop);
but than
assert(cloneLoop instanceof iPath, "fails because loop is not an iPath");
How can i do a deep clone fulfilling last assert statement?
How about this:
cloneLoop = $.extend(true, new iPath(), loop);
...though I'm not sure if you'd want to do a deep copy. I'd think this would be better:
cloneLoop = $.extend(new iPath(), loop);
Extend simply copies properties from one object to another. So you have to start with a pristine copy of the object you want to copy into. So use new iPath() instead of {}.
var iPath = function () { this.heading = 0; this.path = []; };
loop = new iPath();
cloneLoop = $.extend(true, new iPath(), loop);
alert(cloneLoop instanceof iPath);
If not supporting older browsers is an option, you should be able to use Object.create:
var cloneLoop = Object.create(loop);
Here's a demo
function Foo() {
this.x = 1;
this.y = 1;
this.blah = { f: "a", g: "b" };
}
var f = new Foo();
var clone = Object.create(f);
alert(clone instanceof Foo);
alert(clone.blah.f);
alerts true, then a (at least on Chrome, older browsers will not support Object.create)
You'll need to write your own clone method:
Something along the lines of:
iPath.prototype = {
clone: function () {
var p;
p = new iPath();
$.extend(true, p, this);
return p;
}
}
cloneLoop = loop.clone();

Javascript inherit from array and make it global

The small demo below illustrates my problem:
// 1 - Define a global reference to classA
(function() {
window.classA = new ClassA();
})();
// 2 - ClassA object definition
function ClassA() {
this.test1 = function() {
document.write('test1');
};
}
// 3 - ClassA inherits Array and has a test function
ClassA.prototype = new Array;
ClassA.prototype.test2 = function() {
document.write(this[0]);
}
// 4 - Test our ClassA
var c = new ClassA();
c.test1();
c.push('test2');
c.test2();
// 5 - Test our global ClassA
classA.test1();
classA.push('test2'); // doesn't work
classA.test2(); // doesn't work
Try it here: http://jsfiddle.net/SPSW4/
What is the proper way to define a global variable classA (ClassA instance)?
The correct approach would be to create the pseudo-sub-classed Array constructor within an immediately invoked function expression and then expose the result to an explicit global object.
(function( global ) {
// Declare the ArrayLike constructor
function ArrayLike() {
var args = [].slice.call( arguments ),
length = args.length, i = 0;
this.length = length;
for ( ; i < length; i++ ) {
this[ i ] = args[ i ];
}
return this;
}
// Define ArrayLike's prototype by creating a new Array instance
ArrayLike.prototype = new Array();
// Define your own proto method
ArrayLike.prototype.firstChar = function() {
var ret = [],
length = this.length, i = 0;
for ( ; i < length; i++ ) {
ret[ i ] = this[ i ][ 0 ];
}
return ret;
};
// Expose the ArrayLike constructor.
global.ArrayLike = ArrayLike;
})( this );
var a = new ArrayLike( "alpha", "beta", "gamma" );
console.log( a.push("delta") ) // 4
console.log( a ); // ["alpha", "beta", "gamma", "delta"]
console.log( a.firstChar() ); // ["a", "b", "g", "d"]
See it live: http://jsfiddle.net/rwaldron/gLdkb/
Your code appears to be binding the global classA variable before ClassA is fully defined. I believe you will have more luck if you do it like:
// 1 - define ClassA
window.ClassA = function() {
this.test1 = function() {
document.write('test1');
};
};
ClassA.prototype = new Array;
ClassA.prototype.test2 = function() {
document.write(this[0]);
}
// 2 - Define a global reference to classA
window.classA = new ClassA();
// 3 - Test our ClassA
var c = new ClassA();
c.test1();
c.push('test2');
c.test2();
// 4 - Test our global ClassA
classA.test1();
classA.push('test2'); // doesn't work
classA.test2(); // doesn't work
Here's an example: http://jsfiddle.net/SPSW4/2/
swap
// 2 - ClassA object definition
function ClassA() {
this.test1 = function() {
document.write('test1');
};
}
// 1 - Define a global reference to classA
(function() {
window.classA = new ClassA();
})();
declaration before calling functions in JavaScript, it is a scripting language.
Try this:
// 2 - ClassA object definition
function ClassA() {
this.test1 = function() {
document.write('test1');
};
}
// 3 - ClassA inherits Array and has a test function
ClassA.prototype = new Array;
ClassA.prototype.test2 = function() {
document.write(this[0]);
}
// 4 - Test our ClassA
var c = new ClassA();
c.test1();
c.push('test2');
c.test2();
// 1 - Define a global reference to classA
window.classA = new ClassA();
// 5 - Test our global ClassA
classA.test1();
classA.push('test2');
classA.test2();
Actually there were two problems:
1. Creating object before declaring class
2. Creating object before extending class
Define your class
ClassA = function()
{
this.test1 = function()
{
document.write('test1');
};
};
Then apply array prototype
ClassA.prototype = Array.prototype;
Then you can extend your class
ClassA.prototype.test2 = function()
{
document.write(this[0]);
};
About the “global reference” part. In the first part of your code your are not making a reference, you are instancing a class which has not been defined yet at that point. There is no need to do that also. What is your point with that part?
Move the class definition before the call.
Note: The only thing that really needs to come first is the prototyping, since your code first assigns the class, but never sees the effect of the prototyping, which occurs later. Your class assignment should come after the prototypes.
You're missing a ; after the test2() definition.
// Class Definition
function ClassA() {
this.test1 = function() { document.write('foo'); };
}
ClassA.prototype = new Array();
ClassA.prototype.test2 = function() { document.write(this[0]); };
// Init
(function() {
window.classA = new ClassA();
})();
// Method Calls
var c = new ClassA();
c.test1();
c.push('bar');
c.test2();
classA.test1();
classA.push('bar');
classA.test2();

Categories

Resources