Instance vs class methods?

https://www.codecademy.com/paths/web-development/tracks/web-dev-js-arrays-loops-objects/modules/learn-javascript-objects/lessons/advanced-objects/exercises/object-methods

I’m doing this exercise and it mentions instance and class methods. Looked on MDN and still confused it says:
There are object instance methods:
a) .hasOwnProperty(propertyName) //boolean indicating whether the object has the specified property as its own property rather than inheriting it.
b) .valueOf // will return the primitive value of an object - i.e. its initial value before it was altered.
There are object class/static methods:
a) Object.assign()
b) Object.entries()
c) Object.keys()

What actually is the difference between them?

Secondly, I don’t quite get what object.prototype refers to on MDN. Because if prototype is what enables object methods to be inherited shouldn’t it be prototype.object (since prototype is the overarching one)?

Thanks so much for any help!

1 Like

Object.assign() is a function copies one object’s properties to another object.

Object.entries() returns object properties in key value pairs.

Object.keys() returns an array of strings representing an inputted object’s property names. (just the names not the key:value pairs)

Is that what you meant by difference? Or are you thinking in what contexts would they be used?

Here’s a free and very useful book on JS (https://eloquentjavascript.net/)
You can read more on the theory there :slight_smile:

1 Like

That would be, Object.prototype. Object is the root class of everything in JS. It is the top of the prototype chain, save for null which is the very top.

Other objects do not inherit from Object, but from its prototype, the object that contains all methods That’s why we see,

Array.prototype.push()

When we create a class, instances do not inherit from the class, directly, but from its constructor prototype.

class Foo {
    constructor(foo, bar) {
        this.foo = foo;
        this.bar = bar;
    }
    foo_speak () {
        console.log(`Aw, ${this.foo}!`)
    }
    bar_speak () {
        console.log(`Aw, ${this.bar}!`)
    }
}
foo = new Foo('foo', 'bar')
foo.foo_speak()
foo.bar_speak()
console.log(foo.constructor.prototype.hasOwnProperty('foo_speak'))
console.log(foo.constructor.prototype.hasOwnProperty('bar_speak'))

Output

Aw, foo!
Aw, bar!
true
true
2 Likes

Now consider how we can extend a class with more methods, even when there are already existing instances…

Foo.prototype.combine = function () {
    return `${this.foo}${this.bar}`
}
console.log(foo.combine())
//  foobar
2 Likes

Thanks for all your help today btw! It does clarify things but I was actually wondering why
a) Object.assign()
b) Object.entries()
c) Object.keys()
are static methods but the above are under a different category. I’ll have a look at the book thanks!! Yes more the context, but i think someone answered it below. Thanks so much anyways…

1 Like

Thanks for taking the time to explain this, I think I get the gist of it.
In the example, however, why did you put this.foo = foo; underneath the constructor? Like does it mean foo can just be accessed anywhere? If so why wasn’t it foo = this.foo;?

And you said "instances do not inherit from the class, directly, but from its constructor prototype". However, in the example, foo (the instance of the Foo class) inherits .foo_speak() from the class Foo, rather than the constructor, hence why you call foo.foo_speak() rather than foo.constructor.foo_speak(), surely?

I think overall what you are trying to say is that there is a class called Object and underneath there is another nested object called prototype, which houses the constructors and methods. But this is built in javascript so we don’t need to declare an instance of each object we create using ourObjectName = new Object.prototype ();?

Blockquote Now consider how we can extend a class with more methods, even when there are already existing instances…

Foo.prototype.combine = function () {
    return `${this.foo}${this.bar}`
}
console.log(foo.combine())
//  foobar

So foo.combine is now a static method rather than an instance method?
Does that mean any method accessed using the Object.prototype methods are instance methods, whereas any new methods created such as combine that are based only the specific instance of that class are static methods?

So to go back to my initial question, since both Object.assign() and .hasOwnProperty are built in, why is one an instance method and another a static method?

foo is the formal parameter and has no value outside of the constructor. We need to assign it to the instance so it becomes a property value. this.foo is the new instance property.

If you haven’t studied class or custom constructors yet then this won’t make a lot of sense.

The methods are bound to the constructor prototype but JS knows where to find them in the prototype chain. The same way in the following we don’t write .prototype when invoking the method…

Array.prototype.push()  =>  array.push()

The constructor is not part of the prototype. It has a prototype.

The Object class built in to JavaScript has it’s own way to identity data types so that when we define an object of any type, it knows what object wrapper to give it.

s = "string"

The value above is primitive, but in the assignment is given a String object wrapper. s has no type since it’s just a variable. The object it is assigned does have a type, the one given it by Object. The type of an object is what binds it to its class constructor.

s = "string"

is very similar to,

s = new String('string')

I’m not sure we can call it static since it cannot be invoked by the class itself, only the instances.

console.log(Foo.combine())
                ^

TypeError: Foo.combine is not a function
1 Like

Blockquote
foo is the formal parameter and has no value outside of the constructor. We need to assign it to the instance so it becomes a property value. this.foo is the new instance property.
If you haven’t studied class or custom constructors yet then this won’t make a lot of sense.

Thanks a lot for taking the time to explain this all! I haven’t studied class or custom constructors, I’m around 33% of the web development careers path, so not sure if I will. I will try to understand it anyways.

I think I do understand what you’re saying but I’m not quite sure. To recap:

  1. You’re saying that when we assign variables say x = 1, this is essentially the same as:
    x = new Number(number).
    What is happening is that javascript identifies the data type as being number and then automatically gives it the number object wrapper. This then binds it to the numbers related constructor.

  2. Inside this constructor, there is a prototype object, which has various methods (which represent the built in methods).

  3. When we call these methods, they are automatically bound to prototype so although MDN may write x.prototype.methodName(), we can simply call x.methodName() and can omit .prototype. We also don’t have to include .constructor before .prototype because the prototype and constructors are bound together since constructors specific to the data type/object wrapper have prototypes. And javascript knows how to find methods inside the constructor.prototype?

Is that correct would you say? My only question then is what happens to the ‘Object’ class,

Block Quote
class Foo {
constructor(foo, bar) {
this.foo = foo;
this.bar = bar;
}
foo_speak () {
console.log(Aw, ${this.foo}!)
}
bar_speak () {
console.log(Aw, ${this.bar}!)
}
}
foo = new Foo(‘foo’, ‘bar’)
foo.foo_speak()
foo.bar_speak()
console.log(foo.constructor.prototype.hasOwnProperty(‘foo_speak’))
console.log(foo.constructor.prototype.hasOwnProperty(‘bar_speak’))

In this example, when declaring foo, the class name is still included through foo = new Foo(‘foo, bar’), but for object this isn’t seen? I think i’m almost there just a bit lost on some bits.

Lastly, I gather that:
a) instance methods = can only be called by instances of a class.
(Instances of a class also happen to be objects now when defined using a constructor and a new operator?)

This means if i have x = 1, I can apply an instance method on x such as .valueOf.

b) static methods = can be called on classes. So If i had a variable that represents a group of objects such as an array, I would be able to apply a static method such as Object.assign?

I cannot thank you enough, but if you have read this thank you once again. I know its incredibly time consuming/ frustrating to keep responding to me but it is incredibly satisfying to understand something a bit better and I truly appreciate all the help you have given me.

1 Like

In no particular order, I’ll try to go through (some) of your questions.

Prototype

A prototype is an object containing all the methods of a class or constructor function. Since all of JS exists in the same namespace, we can extend even the built in classes (though it is not a good idea in production code). Our class definitions and constructor functions just get piled on to the same code base. In other words, our custom objects are added to the prototype chain and get the same treatment as built-ins.

All classes have a prototype. We don’t have to create it. JavaScript does that when we define them. The thing to focus on is what our constructor is doing. It is creating a new object instance. We assign (or consume immediately) that instance and then are able to invoke its methods with that object as the execution context.

Segue to class methods…

Conveniently this topic has come up. A class definition is a different execution context than its instances. That’s why it didn’t have access to our extended method, above.

Static methods are executable by/on the class itself, or by instances, but which generally take arguments and do not draw on context data.

Correct. That’s why they are called instance methods. They are owned by the instance, not the class, or the global object. Only an instance can call a method.

In the case of built in objects such as booleans, numbers, strings, objects and arrays we can see clearly demonstrated to the access to methods on each. Some have more methods than others. How many methods would we expect the Boolean class to have in its prototype? Yet numbers, strings, objects and arrays have a heap of them.

The top of the prototype chain is null. It has no attributes. How could it? Yet it is there, and it is an identifiable object.

null is what gets returned from prompt() when the user clicks Cancel or presses the Esc key on their keyboard. That means it is a returnable value, and therefore an object.

console.log(`${null || undefined}, ${typeof (null || undefined)}`)
console.log(`${null && undefined}, ${typeof (null && undefined)}`)
undefined undefined
null object
1 Like

Thanks a lot for taking the time to type this all out, mtf! Really really helpful stuff.

I think I get it - so basically class/static methods can be invoked by classes or instances of that class. They’re non-specific, whereas instance methods are specific to instances only. Which is why .hasOwnProperty is an instance method, since you could never apply such a method to an entire class but only an instance. However, you could apply Object.entries to see all the properties inside a class, since classes have methods in them and methods are simply properties with functions.

So lastly, built-in functions are associated with .prototype so we never need to call .prototype when invoking any method, right?

Your image:

And in your last image, you’re basically saying, undefined’s type is undefined whereas null is an object as shown by its typeof?

Thanks a lot btw!

Exactly. undefined is not an object even while we consider it as the default return value of a function. It has no definition so cannot be prototyped. The Number equivalent would be zero which in maths is undefined. It’s one of the most common limits in calculus.

We rarely need to test for undefined, literally. It is falsy, which is enough to know.

const foo = function(x) {
    if (x > 0) return 'positive'
    if (x < 0) return 'negative'
}
console.log(foo(5))     // positive
console.log(foo(-5))    // negative
console.log(foo(0))     // undefined

if (foo(0)) {
    console.log('Not gonna happen.')
}
1 Like

Thanks so much!!! :slight_smile:

1 Like

Consider the following over-exaggeration:

class Foo {
    constructor(foo, bar) {
        this.foo = foo;
        this.bar = bar;
        this.foo_speak = Foo.foo_speak;
        this.bar_speak = Foo.bar_speak;
    }
    foo_speak () {
        console.log(`Aw, ${this.foo}!`)
    }
    bar_speak () {
        console.log(`Aw, ${this.bar}!`)
    }
}
console.log(Object.entries(Foo))
console.log(Object.entries(new Foo('a','b')))

Purely for illustration. Here’s the output…

[]
[ [ 'foo', 'a' ],
  [ 'bar', 'b' ],
  [ 'foo_speak', undefined ],
  [ 'bar_speak', undefined ] ]

Object.entries() does not list the methods of a class. The class definition has no entries that we can inspect with that method.

Ahh I see, haha, is that because Object.entries doesn’t list functions/constructors and thus, won’t list anything below or because my understanding is flawed?

Secondly, I was still just wondering (again unfortunately) why is it this.foo = foo;?

Above you mentioned:
foo is the formal parameter and has no value outside of the constructor. We need to assign it to the instance so it becomes a property value. this.foo is the new instance property.

But, surely we want to reassign the formal parameter to this.foo? Not this.foo to the formal parameter? or are they the same?

Thanks as always.

Say we’re a scientist studying Neutronium. Tell me our understanding is not flawed. It’s all baby-steps. The more we adapt that approach, and keep cycling back over prerequisite concepts and test ourselves against those paradigms the more progress we make going forward. Never think you don’t understand; but never also think that you do. That’s the seedling of curiosity.

1 Like