My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Friday, March 23, 2007

JavaScript constructor (did you know?)

Update! [2007/03/27]
to know if a generic object is cracked we don't need to create a new instance of its constructor. The fastest, safest and correct way is just to know if this object is an instance of its constructor.
return o instanceof o.constructor ? o.constructor : undefined;

Just fixed the Constructor function ;)


I'm absolutely a JavaScript noob!
Just today I "discovered" that a generic object constructor is not a read-only parameter and I based a lot of conversions with constructor parameter!

What a news for me, I can't believe that every kind of object, except for primitive vairables such String, Boolean and Number, could be modified in this way:

function MyObj(){};
var obj = new MyObj,
str = new String("test"),
sts = "test";
obj.constructor = Array;
str.constructor = Array;
sts.constructor = Array;
alert([
obj.constructor === Array,
str.constructor === Array,
sts.constructor === Array
]); // true, true, false


What does it mean?
This means that my PHP_Serializer object, used from a lot of developers, and my byteson library, with its JSON parser, are not so safe to use as I believed.

Malicius code should not be a problem but these objects should fail conversion in a really simple way because they rely on variables constructor.

What about a solution?
The first point is that to change a variable constructor is absolutely the worst JavaScript practice I can think on but we write code and we want a secure one so this is my proposal:

function Constructor(o){return o === null ? o : Constructor.$[typeof o](o)};
Constructor.$ = {
"boolean": function(){return Boolean},
"function": function(){return Function},
"number": function(){return Number},
"string": function(){return String},
"object": function(o){
var constructor = o.constructor;
return o instanceof o.constructor ? o.constructor : undefined;
},
"undefined": function(){return undefined}
};

This function returns a generic variable constructor, null or undefined if variable is null, is exactely undefined or if it has an unknown constructor.

That's true, if You change a constructor of a generic object is really difficult to know wich one was changed so this function guarantees, at least, that if returned value is undefined but value is defined, it was "cracked" changing constructor value and in this case, it should be deleted or discarded (IMHO).
This is a simple test case:

function Type(){};

var arr = [1,2,3],
obj = {one:"two"},
bot = new Boolean(1),
bom = false,
fun = new Type,
fum = function(){},
num = 1,
nun = new Number(123),
str = "test",
sts = new String("taste");

arr.constructor = String;
obj.constructor = String;
bot.constructor = String;
bom.constructor = String;
fun.constructor = String;
fum.constructor = String;
num.constructor = String;
nun.constructor = String;
str.constructor = Array;
sts.constructor = Array;

alert([
Constructor(arr),
Constructor(obj),
Constructor(bot),
Constructor(bom),
Constructor(fun),
Constructor(fum),
Constructor(num),
Constructor(nun),
Constructor(str),
Constructor(sts)
].join("\n---\n"));


Well, now you have a function to check constructors status so right now you can forget a totally nonsense practice as changing constructor is.

That's all folks :)

5 comments:

Anonymous said...

I'm more of a noob at JS than you. Can you tell me what a constructor is and does? Why is it used...is it important in programming.

Yeah...re: you're first post of above about changing values with the same object, a loosely typed language is very handy in JS.

Anonymous said...

I am wondering where you executed your code. I just checked in IE7, it seems to me constructor is read-only.

Andrea Giammarchi said...

constructor is *not* read only EOD

Anonymous said...

// Firefox 7.0.1

function Car() {};

var car = new Car;

console.log(car.constructor); // Car()

car.constructor = Array;

console.log(car.constructor); // [ undefined ]

Andrea Giammarchi said...

in 2007 things were slightly different I guess ...