10

I want to call an inner function from a static function that was called without an instance, like so:

Foo.Bar = function (options) {

    Autodesk.Viewing.Extension.call(this, options);

    ...

    this.innerFunc = function innerFunc(){
      ...
    }
};

Foo.Bar.prototype.constructor =
    Foo.Bar;

Foo.Bar.SomeStaticFunc = function () {
    innerFunc();
}

Use: Foo.Bar.SomeStaticFunc();.

But I get SomeStaticFunc is not a function.

The example here uses a variable for the class, like var Foo.Bar = function (options) {... but isn't that the same as making an instance of the class like so and calling an inner function?

let x= new Foo.Bar(options);
x.innerFunc();

Is there another way to do this?

PS: I know about ES6 classes but I prefer not to migrate this class to ES6 for now because it isn't totally straight forward.

shinzou
  • 5,850
  • 10
  • 60
  • 124
  • use an object literal, otherwise you need to use `new` – Sterling Archer Apr 18 '17 at 14:29
  • @SterlingArcher by object literal you mean like in the example I linked? – shinzou Apr 18 '17 at 14:32
  • That doesn't make any sense. Your `innerFunc` is only created for instances - it's absolutely impossible to call it without an instance. – Bergi Apr 18 '17 at 16:19
  • So `innerFunc` and all of the functions it calls must be static too for this to work? How would I call them then without an instance of the class? @Bergi – shinzou Apr 18 '17 at 16:54
  • Either make all of them static, all of them instance-specific, or let instance methods call static ones. – Bergi Apr 18 '17 at 17:19

1 Answers1

39

Well... It seems that you do not know how JavaScript works internally, so here is a quick recap. :)

  • JavaScript IS an object-oriented language. Object literals are objects, arrays are objects, functions are objects, etc.

  • JavaScript IS NOT a class-based language. You could say: "Hey, I've seen class and extends keywords in ES6!". Yes, but this is just syntactic sugar. JavaScript is not based on classes like Java, it is based on prototypes.

With ES5 syntax, there are two ways to create what you call a "class":

  • An object literal
  • A constructor function

When you use an object literal, your class looks like this:

var myClass = {
  myAttribute: "foo",
  myMethod: function () {
    return "bar";
  }
};

When you use a constructor function, your class looks like this:

function MyClass() {
  this.myAttribute = "foo";
  this.myMethod = function () {
    return "bar";
  };
}

There are of course differences between these two approaches. With the object literal, you have a sort of Singleton where properties are all static to some extent. With the constructor function, you can produce instances whose properties will be introduced by the this keyword. Example:

var myInstance = new MyClass();
console.log(myInstance);

This instance will have "myAttribute" and "myMethod" in its own properties. This means that these properties are tied to the instance. If you want to call this method, you must do this:

myInstance.myMethod();

So far so good... But there is something wrong with what we did previously. this.myMethod will be created again and again for each instance of MyClass and it is always the same. A better way to handle this is to put it in the prototype so that it can be shared by all instances:

function MyClass() {
  this.myAttribute = "foo";
}

MyClass.prototype.myMethod = function () {
  return "bar";
};

This is much better, but myMethod is still tied to MyClass instances...

Now I want to create a static method. By definition, our static method will be tied to the class, not to its instances:

MyClass.myStaticMethod = function () {
  return "baz";
};

Nice. Here, for the sake of experimentation, I want to do something like this:

MyClass.myStaticMethod = function () {
  myMethod();
};

This does not work. Why? In fact, myMethod does not exist in the given scope nor in the outer scope. myMethod has been declared inside another function (the constructor function) and it is not returned, so it is invisible from the outside. Moreover, this function has been put in the prototype of MyClass. This means that it is not available globally, but only on instances of MyClass. When you think about it, it is pretty logical. For instance, when you want to call array methods (methods in Array.prototype), it does not make sense to do that:

push('test');
reverse();
includes('a');

You must call these methods on an array (instance of Array).

[].push('test');
['foo', 'bar', 'baz'].reverse();
['a', 'b'].includes('a');
Badacadabra
  • 8,043
  • 7
  • 28
  • 49
  • 10
    "JavaScript is not based on classes like Java, it is based on prototypes" --- this is a controversial statement. From modelling perspective it does not matter how exactly it is implemented under the hood. Everything is a "syntactic sugar" for CPU instructions and registers. – zerkms Apr 19 '17 at 00:08
  • 1
    @zerkms You are right, but Java has been thought with classes in mind. When you create a program in Java, you must create a class. JavaScript is very different and has been built around prototypes. I agree, you do not need to know how everything works under the hood, but this is really useful to work with JavaScript (ES6 is still not fully supported). Since JavaScript is becoming a language for everything, it is more and more used by developers who did not have time to learn it and who think they can strictly apply what they know about class-based languages. This leads to many errors... – Badacadabra Apr 19 '17 at 00:22
  • "but this is really useful to work with JavaScript" --- I am doing a lot of JS every day, and I have not been dealing with `prototypes` explicitly by myself in any form for more than 2 years. I use "classes" as they are defined in the current standard and not knowing the under-the-hood does not make me less or more efficient: I simply use classes. "This leads to many errors" --- well, I tend to disagree. If you don't deal with prototypes - it makes your life much easier. – zerkms Apr 19 '17 at 00:24
  • 2
    I assume you use lots of libraries, which is great. Indeed, in this case, this is not necessary to struggle with prototypes directly (e.g. jQuery). In fact, you only need to struggle with prototypes when you want to create something in Vanilla JS. This question was about ES5, so I answered about ES5. Of course, if you build modern web apps, you can use React or Angular... ^^ – Badacadabra Apr 19 '17 at 00:31
  • @Badacadabra I also don't think there is any need to know about prototypes. JS is an OO language and all principles apply. In my opinion, the only difference between JS and Java is the "syntactic sugar". If you know OOP you can handle very well in both languages. – Jordan Silva Nov 22 '18 at 08:15
  • Nice clarification. Vanilla js is something most developers try to get away from... because firstly it sounds weird and difficult to grasp and also libraries make life much easier by calling its methods and get the job done. In my view, Vanilla js is more suitable for those who create libraries and features rather than using them. Let me know your thought – Jora Nov 02 '20 at 15:50
  • @zerkms Everything is "not" syntactic sugar. Javascript "Is" prototype based. Read the documentation. It is logical concept. Using just semantics to try to say otherwise, is in a way trying to kid yourself. – Shahzad Rahim Apr 11 '22 at 11:17
  • @ShahzadRahim I read the ES spec on daily basis as part of my job, not sure what your point is. – zerkms Apr 11 '22 at 21:34
  • Reading this almost 6 years later, I realize there is, beyond the controversy, at least one word in my answer that should be avoided because it is awkward: "just". It may give the impression that ES6 brought nothing new with this magical `class` keyword, which is a bit unfair. I want to be clear: from my perspective, "syntactic sugar" is not an insult. It makes life easier, so it is great. – Badacadabra Jan 04 '23 at 19:31