HappyCoding HappyCoding - 3 months ago 10
Javascript Question

How to classify class, function and function property in javascript?

I've 3 objects like these:

class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
}

let Add = function (a, b) {
return a + b
};

let Math = {
multiply: function (x, y) {
return x * y
}
};


As you can
see
,
Person
is a class,
Add
and
Math.multiply
are functions.

My question: How to classify
class
and
functions
in this case?

My question comes from:
Person
looks like a function, like this:

let Person = function (name, age) {
this._name = name;
this._age = age;
};


Also, there are no problems if I declare an object to get new instance of
Add
function:

let result = new Add(2, 3); // this is allowed although it just needn't


new operator says:


The new operator creates an instance of a user-defined object type or
of one of the built-in object types that has a constructor function.


All of 3 cases,
Person
,
Add
and
Math.multiply
are constructors. So:

let person = new Person('Hermione', 18); // allowed
let result = new Add(2, 3); // allowed
result = new Math.multiply(2, 2); // allowed


Then, because all of them are constructors, it's so difficult to classify
class
and
function
(for me).

Is there a way to achieve that?

Answer

I think your question stems from quite understandable confusion: Despite the class syntax, JavaScript doesn't have classes as distinct from functions like some class-based OOP languages do (Java, C#, C++). class syntax creates a function and associated properties on the object referenced by that function's prototype property. It's not a separate thing. See below the bar for more.

Further, JavaScript didn't originally make any distinction between normal functions and constructor functions; it was purely a matter of how you used used them (e.g., via new or calling them directly). As of ES2015 which introduced both class syntax and arrow functions, the language is moving toward distinguishing different kinds of functions (the ones created via class will throw an error if you call them without new; arrow functions will throw an error if you do call them via new).

If your goal is to look at Person in code and know whether it was created with class or function without calling it, the only way to do that is to convert it to a string and look at the result. The specification requires that Function.prototype.toString return a string that...

...must have the syntax of a FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending upon the actual characteristics of the object.

So for instance, if I've used class Foo { }, then Foo.toString() has to return a class declaration or expression, whereas if I've used function Foo { } instead, it must return a function declaration or expression. So you could work it out with a regular expression.

In a comment you've asked zerkms

But in javascript I can use Add and Math.multiply as classes. Is it better if I can use a function like a class? No, it isn't.

It completely depends on how Add and X.multiply (I really wouldn't shadow the built-in Math object) are defined: It may well make sense to call them via new. It's not uncommon for an object property to be a reference to a constructor function:

let Nifty = {
    Stuff: class {
        constructor(name) {
            this.name = name;
        }
    }
};
let x = new Nifty.Stuff("Coolness");
console.log(x.name); // "Coolness"

or

// Better not to do this, but it's allowed
let Nifty = {
    Stuff: function(name) {
        this.name = name;
    }
};
let x = new Nifty.Stuff("Coolness");
console.log(x.name); // "Coolness"

Re class creating functions: That's literally what it does:

class Foo {
}
console.log(typeof Foo); // "function"

In fact, this:

class Foo {
}

roughly translates to

let Foo = function() {
    if (!(this instanceof Foo)) { // This check isn't quite right, but it's close
        throw new TypeError("Class constructor Foo cannot be invoked without 'new'");
    }
};

and

class Foo {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
        console.log(`Hi, my name is ${name}!`);
    }
}

roughly translates to

let Foo = function(name) {
    if (!(this instanceof Foo)) { // This check isn't quite right, but it's close
        throw new TypeError("Class constructor Foo cannot be invoked without 'new'");
    }
    this.name = name;
};

Foo.prototype.sayHello = function sayHello() {
        console.log("Hi, my name is " + name + "!");
    }
};