11

I'm trying to make the constructor abort the object construction if something fails, for example it can't get a hold of a canvas.

But as I'm using new I see that klass() always returns this regardless of any return null or any other value, can I work around this to return null?

Now that I think of, a solution may be to create the new instance inside klass() and return that instance or null, and not use new, is there a better solution?

function klass( canvas_id ) {
    var canvas = document.getElementById( canvas_id );

    if( ! ( canvas && canvas.getContext ) ) {
        return null;
    }   
}
var instance = new klass( 'wrong_id' );
console.log( instance, typeof instance );
1
  • 1
    You can throw an exception or you can set some state in your object that can be tested. Commented Dec 23, 2011 at 17:07

6 Answers 6

15

You could make a "factory function" or "static factory method" instead:

Foo.CreateFoo = function() {
  // not to confuse with Foo.prototype. ...
  if (something) {
    return null;
  }
  return new Foo();
};

// then instead of new Foo():
var obj = Foo.CreateFoo();

Same thing using the newer class syntax:

class Foo {
  static CreateFoo() {
    if (something) {
      return null;
    }
    return new Foo();
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I think some variation on this would generally be a better approach. Try to grab the element in a function, and if one is found, pass it on to the constructor. If not, return null.
do you have solution for class instead of using function?
12

The better solution would be to throw an error:

function klass(canvas_id) {
    var canvas = document.getElementById( canvas_id );

    if( ! ( canvas && canvas.getContext ) ) {
        throw new Error('Not a canvas');
    }  
}

// later...

try {
    var c = new klass("canvas_id");
} catch(E) {
    // error caught
}

EDIT: Constructors can be "forced" to not return an instance:

function Foo() {
   var canvas = ...;

   if ('undefined' == '' + Foo.CANVAS_CHECK)
      Foo.CANVAS_CHECK = ( canvas && canvas.getContext );

   if (!Foo.CANVAS_CHECK)
      return []; // the constructor will actually return an empty array

   // passed; initialize instance here
}


// later on...

var foo;

if (!((foo = new Foo()) instanceof Foo)) {
   // Failed. Canvas is unsupported.
}

// You happy now, am not i am?  ;-)

The odd thing is, however, that if a "constructor" returns a number, string, true, false, etc., it actually does return an instance. The second solution only works when the constructor returns an empty array [] or an empty object {}.

7 Comments

Would you really want to clutter your code with try/catch statements just to test if an element was found?
Not to check if an element was found. But if you're trying to validate if a canvas is actually supported, I'd rather raise an exception.
Yeah, it's good. Although as @amnotiam says, I'd like better a single liner like if( !( a = new klass('id') )
@LinusKleen - er, yes I was mistaken in saying "to test if an element is found". But my main point is that you'd need the try/catch every place where you're doing new klass("canvas_id");. If it's for feature detection, you should really do it once, store the result, then have the constructor act accordingly.
@amnotiam Although I'm an exception-guy, I added something where the code isn't cluttered. Thanks for hinting on checking the result once and storing it. I implemented this into my second variant.
|
3

You can use combine a factory with the constructor in one function using the technique described in John Resig's article. Example:

function Person(name) {
    var me = arguments.callee;

    if (!(this instanceof me)) {
        // factory code

        // validate parameters....
        if(!name.match(/^[a-z]+$/))
            return null;

        // ...and call the constructor
        return new me(arguments);

    } else {

        // constructor code
        this.name = name;       

    }
}


a = Person("joe")   // ok 
b = Person("bob")   // ok
c = Person("R2D2")  // null

1 Comment

+1 for not just copying but giving credit where it's due. (And probably also the best resulting one-liner, yet.)
0

The answer is unfortunately, you can't : ( . The reasons are in this answer:

What values can a constructor return to avoid returning this?

Comments

0

As I posted in my comment above...

function klass( canvas_id ) {
    var canvas = document.getElementById( canvas_id );

    if( ! ( canvas && canvas.getContext ) ) {
        return new Boolean;
    }   
}

var instance1 = new klass( 'wrong_id' );

if(!(instance1 instanceof klass))
    console.log( 'Canvas is not suppored' );

var instance2 = new klass( 'wrong_id' ).valueOf();

console.log( instance2, typeof instance2 );

if(instance2 !== false)
    console.log( 'Canvas is supported, yeah' );

Comments

0

I got around the same problem with this trick:

  ...
  function MyConstructor () 
  { ...
    let Null = { valueOf: ()=>null }
    return Null;
  } 

A constructor can not return null. But it can return any Object so it can return the object 'Null' defined above. The caller of the constructor then simply needs to call valueOf() before using the result:

let maybeNull = (new MyConstructor (maybeArg)) . valueOf();

This works because valueOf() is a built-in JavaScript method which by default for everything except boxed Numbers boxed Strings and boxed Booleans returns themselves.

You can override valueOf() for any object, like I did above for my Null(), to return null.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.