How can I set a dynamically generated pseudoword name in JavaScript to work with the instanceof operator?

I would like to specify the name of a JavaScript pseudo-class stored in an array with a specific name, for example the non-array version works flawlessly:

var Working = new Array();
Working = new Function('arg', 'this.Arg = arg;');
Working.prototype.constructor = Working;
var instw = new Working('test');
document.write(instw.Arg);
document.write('<BR />');
document.write((instw instanceof Working).toString());

      

Output:

test
true

      

However, this format doesn't work:

// desired name of pseudoclass
var oname = 'Obj';

var objs = new Array();
objs.push(new Function('arg', 'this.Arg = arg;'));

// set objs[0] name - DOESN'T WORK
objs[0].prototype.constructor = oname;

// create new instance of objs[0] - works
var inst = new objs[0]('test');
document.write(inst.Arg);

document.write('<BR />Redundant: ');
// check inst name - this one doesn't need to work
try { document.write((inst instanceof objs[0]).toString()); } catch (ex) { document.write('false'); }
document.write('<BR />Required: ');
// check inst name - this is the desired use of instanceof
try { document.write((inst instanceof Obj).toString()); } catch (ex) { document.write('false'); }

      

Output:

test
Redundant: true
Required: false

      

Link to JSFiddle .

+2


source to share


1 answer


You have a few things here that are slightly different from the JS level (which is good, my C # is pretty hackneyed as soon as I leave the core language features 4.0).

First, may I suggest avoiding document.write

at all costs?
There are technical reasons for this and browsers are trying to work around them these days, but it's still as bad as putting it alert()

everywhere (including iteration).
And we all know how annoying Windows system message pop-ups are.

If you're in Chrome, click CTRL+Shift+J

and you get a handy console where you can get results console.log()

(even objects / arrays / functions) that will return workarounds for dataset / DOM objects and strings for other types (like functions). One of the best things about JS these days is that your IDE sits in your browser.
Writing from scratch and saving .js files isn't very easy from the console, but testing / debugging couldn't be easier.

Now, for real trouble.

See what you do with example # 1. Rewriting .prototype.constructor

should be completely unnecessary unless some browsers / engines exist as a last resort.

Inside any function used as a constructor (i.e. called with new

), the function basically creates a new object {}

by assigning it this

, setting this.__proto__ = arguments.callee.prototype

and setting this.__proto__.constructor = arguments.callee

where arguments.callee === function

.

var Working = function () {};
var working = new Working();
console.log(working instanceof Working); // [log:] true

      

Working

is not a string: you made it a function.
In fact, in JS it is also a property window

(in the browser, that is).

window.Working    === Working;  // true
window["Working"] === Working; // true

      

This latter is indeed the key to solving the dilemma in example # 2.

Just before looking at # 2, a caveat:
If you're doing heavy pseudo-grading,

var Shape = function () {
    this.get_area = function () { };
},

Square = function (w) {
    this.w = w;
    Shape.call(this);
};

      

If you want yours to instanceof

work with both Square and Shape, you need to start playing with prototypes and / or constructors, depending on what you would like to inherit and how.



var Shape = function () {};
Shape.prototype.getArea = function () { return this.length * this.width; };

var Square = function (l) { this.length = l; this.width = l; };
Square.prototype = new Shape();

var Circle = function (r) { this.radius = r; };

Circle.prototype = new Shape();
Circle.prototype.getArea = function () { return 2 * Math.PI * this.radius; };

var circle = new Circle(4),
    square = new Square(4);

circle instanceof Shape; // true
square instanceof Shape; // true

      

This is simply because we are setting the prototype object (reusable by each instance) to a completely new instance of the parent class. We could even share this single instance among all the child classes.

var shape = new Shape();
Circle.prototype = shape;
Square.prototype = shape;

      

... just don't override .getArea

, because prototypal inheritance is like inheriting public static methods.

the shape, of course, has shape.__proto__.constructor === Shape

, which is significantly less than square.__proto__.constructor === Square

. Execution instanceof

simply iterates through the links __proto__

, checking if the functions are as specified.

And if you build functions in the above way ( Circle.prototype = new Shape(); Circle.prototype.getArea = function () { /* overriding Shape().getArea() */};

then circle instanceof Circle && circle instanceof Shape

takes care of itself.

Overlay inheritance, or pseudo-constructors (returning objects that are not this

, etc.) require constructor

mangling for these checks to work.

... anyway ... On # 2:

With all of the above, this should be a fairly quick fix.

You are creating a string for the desired function name, not creating the function yourself, and there is no variable Obj

, so you get a "reference error": instead, make your "desired name" an object property.

var classes = {},
    class_name = "Circle",

    constructors = [];


classes[class_name] = function (r) { this.radius = r; };

constructors.push(classes.Circle);

var circle = new constructors[0](8);
circle instanceof classes.Circle;

      

Everything is well defined now, you don't overwrite anything you don't need to overwrite, you can still subclass and override members .prototype

and you can still do in a instanceof

procedural way (assigning data.name

as a property of an object and setting its value to new Function(data.args, data.constructor)

and using that object property to look up ).

Hope this all helps.

+2


source







All Articles