Sandra Schlichting
Sandra Schlichting

Reputation: 25996

Why do functions seem to be objects?

I am trying to learn JS/ES and am a bit confused about classes and objects.

The two code snippets below do exactly the same, but the last one is clearly using a class. But what is the first using? I thought a class became an object when executed?

I would have expected clock to be a function, but it can apparently be used as an object. Why is that?

And why would anyone ever want to use the class way, when the first is shorter to type?

var clock = {
  start() {
    setInterval(() => console.log(Date()), 1000) 
  }
}
clock.start()

and

class Clock {
  start() {
    setInterval(() => console.log(Date()), 1000)
  }
}
const c = new Clock
c.start()

Upvotes: 1

Views: 131

Answers (1)

Garrett Motzner
Garrett Motzner

Reputation: 3230

Classes are templates for objects. They describe how a bunch of objects behave. In javascript, new Clock() is how you get a new object of the class Clock.

The object literal syntax ({foo: 'bar'}) is a shortcut to create a generic object. So the class of an object created that way is Object. Also, creating an object that way doesn't give you a way to make new objects with the same behavior (which would mean in a sense they would be of the same class).

In some languages like C++ and Java, classes are fundamental building blocks of the language. But in javascript, it's just a concept, and basically means (informally) any object that has the same specified behavior. More formally though it is an object that has the class prototype in its prototype chain. What are prototypes and prototype chains? Well, every* object (even those made with the object literal syntax) has a prototype as a hidden** property. When looking up a property in the object, if it isn't found in the object itself, it looks up the property in the prototype. And if that prototype object doesn't have that property, it goes "up the chain" and checks the prototype's prototype for the property, and keeps doing that as far as it can.

When you use the class syntax, you are setting properties on the prototype for every object that class creates. So in your example, you are assigning the function start to the prototype for every Clock object. But if you assign it to the object itself, then it doesn't use a prototype.

Interestingly, classes are also objects (they can have properties and methods).

For official documentation, see here:

*: You can make an object without a prototype using Object.create(null), but those are rare. The benefit of this is that you can't accidentally access properties of the prototype, because there are none.

**: You can sometimes access the prototype using the deprecated __poto__ property. The preferred new way is Object.getPrototypeOf(object)

Here are some examples you can explore:

const clockByLiteral = { start(){/* do something */} }
function startClock () { /* do something */}
class Clock {
  start() { /* do something */}
}

const clock1 = new Clock()
const clock2 = new Clock()

function getClassName(object){
  return object.constructor.name
}
console.log('"class" of clockByLiteral:', getClassName(clockByLiteral))
console.log('"class" of clockByLiteral.start:', getClassName(clockByLiteral.start))
console.log('"class" of startClock:', getClassName(startClock))
console.log('"class" of Clock class:', getClassName(Clock))
console.log('"class" of clock1:', getClassName(clock1))
console.log('"class" of clock2:', getClassName(clock2))
console.log('is the constructor of clock1 Clock?:', clock1.constructor === Clock, "(meaning that classes and constructors are the same objects, and are functions also")
console.log('is the prototype of clock1 Clock.prototype?:', Object.getPrototypeOf(clock1) === Clock.prototype)


console.log('is clock1 the same object as clock2?:', clock1 === clock2)
console.log('is clock1.start the same as clock2.start?:', clock1.start === clock2.start)
console.log('is clock1.start the same as Clock.prototype.start?:', clock1.start === Clock.prototype.start)

console.log('is clock1 an "instance" of Clock?:', clock1 instanceof Clock)
console.log('is clock1 an "instance" of Object?:', clock1 instanceof Object)
console.log('is clock1 an "instance" of Function?:', clock1 instanceof Function)
console.log('is Clock an "instance" of Function?:', Clock instanceof Function)
console.log('is startClock an "instance" of Function?:', Clock instanceof Function)
console.log('is startClock an "instance" of Object?:', Clock instanceof Object)

Upvotes: 3

Related Questions