1

Writing a library and eventually end goal is to determine if an object is an observable. However, I don't know for sure if the user will pass a non-Observable value.

I am surprised that even primitives in JS have a constructor property, e.g.:

const bool = true;
console.log(bool.constructor.name);

const string = 'foo';
console.log(string.constructor.name);

const num = 5;
console.log(num.constructor.name);

All defined, which surprises me, I would have guessed primitive values in JS would not have a constructor (for performance reasons, such as in Java).

so my question is: is there any value in JS for which the constructor would be undefined?

Ultimately, I am looking to create a decent test that a value is an Observable, here is my preliminary test:

module.exports = function isObservable(val) {

    return (val && typeof val.subscribe === 'function'
    && val.constructor && /Observable/.test(val.constructor.name));

};
8
  • Perhaps if you explain what you're trying to do with the .constructor property, then we can more fully advise if that's an appropriate use of the property. Commented Jan 9, 2017 at 0:51
  • "Writing a library and eventually end goal is to determine if an object is an observable." :) Commented Jan 9, 2017 at 0:54
  • I assume best thing to do would be something like /Observable/.test(value.constructor.name), as well as test that subscribe is an method Commented Jan 9, 2017 at 0:55
  • Wouldn't use either test instanceof to see if it inherits from some known base object or test for the existence of certain properties that indicates it has the appropriate methods to be an observable? A subclassed object won't have a known .constructor as the constructor will point to the subclassed object's constructor. Commented Jan 9, 2017 at 0:56
  • instanceof is not good because then I need to import the RxJS or other observable lib, I don't want to import the libraries, because this is for a library and need to be concerned with saving diskspace/install time etc. Commented Jan 9, 2017 at 0:57

4 Answers 4

3

undefined and null do not have a constructor property.

Values like numbers appear to have a constructor, but indeed are somewhat "special" because

x = 3
x.y = "z"         // no problem
console.log(x.y)  // undefined!

"wrapped" numbers (x = new Number(42)) are instead normal objects and you can store additional properties in them. So while 3 declares Number as its constructor, calling it doesn't generate the same type of value as the number 3.

Sign up to request clarification or add additional context in comments.

Comments

3

Except for 'null' & 'undefined' primitives, all other primitives have constructor property.

5 Comments

well, they are wrapped by default ?
does not really answer the question - I am specifically wondering if there is any circumstance for which a constructor might be undefined.
Except for 'null' & 'undefined' primitives, all other primitives have constructor property.
please add that comment to your answer
Have added it to the answer
1

Does every value/object in JS have a constructor?

The simple answer is "NO" because we know that null and undefined do not "have" a constructor. So there are values which "do not have a constructor"

The more qualified answer is "We DON'T KNOW whether null has a constructor because if you try to execute: "let a = null.constructor" your program crashes.

But then in your comment you say "I am specifically wondering if there is any circumstance for which a constructor might be undefined". That was my question too.

In other words, is there a case in which we can execute a.constructor === undefined and get true as result?

The answer to that question is "YES", as shown by this code-snippet:

let a = {};    
a.constructor = undefined;
console.log (a.constructor); 
// -> console shows: undefined

Comments

0

It's a bad idea to use the .constructor property for what you're trying to do because that eliminates any essence of subclassing and eliminates the ability for the caller to use their own flavor of observable that supports the required contract, but isn't named what you are looking for. It also removes the ability to use mixins or composite objects where the original constructor isn't the observable itself. These are likely not things you want to prohibit.

Now that you've modified the question to show what you're really trying to do (detect if you're being passed an observable object), you have a couple choices:

  1. Test instanceof to see if it derives from a known base object.
  2. Test the object to see if it has the known and expected properties of an object that behaves the way you want. This is called "duck" typing. If it quacks like a duck, it must be a duck and is enough of a duck for what you're trying to do even though you haven't checked its DNA.

I'd suggest you extend what you had with this:

return (val && 
        typeof val.subscribe === 'function' && 
        typeof val.unsubscribe === 'function')

to add a couple more properties/methods that you expect to be on a legitimate observable object. You can make the test as stringent as you want (test for 10 separate methods if you want or even call a couple methods with no side effects to see if they return the desired value), but in reality, you probably just want to make sure that it somewhat smells like it's an observable and then if the caller is giving you a half baked observable, that's kind of their issue. Chance are your real goal here is to just advise the developer when they've made a programming mistake so you can "early on" throw an exception during their development that shows they've passed in the wrong kind of object. The simplest type of smell test should suffice for that. You generally aren't interested in testing for subleties.

There's really no reason to test for more properties than you're actually going to use. If someone wants to make their own version of an observable that supports the properties you use, then they should be allowed to do that so you shouldn't have to worry beyond that. If they try to use something that looks like an observable, but doesn't quite implement the right interfaces on the object, then that's kind of on them for not giving you a legit observable, not really on your API.

8 Comments

@AlexanderMills - I added a few more comments to my answer about the goals of your test here. I think you just need a quick test to see if it generally smells like you were passed the right type of object so, if not, you can immediately throw and tell the developer they aren't using your API properly. This probably isn't a case where you're trying to detect with fine grain whether this is a perfect observable or not. If the caller gives you a half-baked observable that happens to pass your smell test, but is a poor implementation, that's on the caller, not on you.
thanks, to add one more wrench into the mix, very much need to test if a value is an Observable (RxJS, or native ES7, etc), but I also need to distinguish between a Subscriber and an Observable. Imagine for a second that both a Subscriber and Observable have subscribe and unsubscribe as methods - is there a better way than duck typing to determine which is which without importing the library?
The above suggests that maybe a duck test or smoke test is perhaps problematic
Is therre not a duck test that would distinguish between an observable or subscriber? Or a fast method with no side effects that would give you an answer? I will have to get up to speed on RxJS in order to offer help to you more there.
@AlexanderMills - OK, it sounds like you're good to go with duck typing then. Cool.
|

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.