Javascript: Class.Method VS. Class.Prototype.Method

JavaScript: Class.method vs. Class.prototype.method

Yes, the first function has no relationship with an object instance of that constructor function, you can consider it like a 'static method'.

In JavaScript functions are first-class objects, that means you can treat them just like any object, in this case, you are only adding a property to the function object.

The second function, as you are extending the constructor function prototype, it will be available to all the object instances created with the new keyword, and the context within that function (the this keyword) will refer to the actual object instance where you call it.

Consider this example:

// constructor function
function MyClass () {
var privateVariable; // private member only available within the constructor fn

this.privilegedMethod = function () { // it can access private members
//..
};
}

// A 'static method', it's just like a normal function
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
// the 'this' keyword refers to the object instance
// you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();

Difference between class method vs className.prototype method in JS

The most obvious difference is:

You can mutate the prototype of every class with the second method (including native ones), while the first syntax only works for declaring your own classes (but on the other hand it keeps things clean & structured).

There are other differences, that you can ignore in most cases:

1) Class methods are not enumerable, while setting a property directly makes it enumerable.

This would be more equivalent to the class syntax:

  Object.defineProperty(Cat.protoype, "method2", {
value() {
console.log("m2");
},
enumerable: false, // it's the default value, this is just for clarity
writable: true,
configurable: true,
});

2) super is only accessible in methods added during declaration (both in objects and classes) of the object / class itself.

3) The .name of the function is "test" in the first, and "anonymous" in the second case. That can be changed by making the function non anonymous, e.g. function method2() { ... }

JavaScript: When to use a Class vs. a prototype?

So, what is the difference between using a class vs. a prototype like below and when do you use either approach?

To answer your question simply, there is no real difference.

Straight from the MDN web docs definition:

JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance.

Should we include methods in prototypes for ES6 Classes for performance?

As you've already discovered, class definitions put methods on the prototype. You asked for a reference so here's a little code you can run to see for yourself:

class Person {    constructor(first, last, age, gender, interests) {        this.name = {            first: first,            last: last        };        this.age = age;        this.gender = gender;        this.interests = interests;    }
greeting () { console.log("Hi! I am", this.name.first); }
farewell () { console.log(this.name.first, "has left the building. Bye for now!"); }}
let p = new Person("Jack", "Beanstalk", 201, "giant", ["crushing things", "stomping things"]);
console.log("Person", Object.getOwnPropertyNames(Person));
console.log("p", Object.getOwnPropertyNames(p));
let p_prototype = Object.getPrototypeOf(p);
console.log("p_prototype === Person.prototype is ", p_prototype === Person.prototype);
console.log("Person.prototype", Object.getOwnPropertyNames(Person.prototype));

How to define prototype method inside class definition (externally) in Javascript?

I can think of two ways of adding external functions as own properties:

  1. Set it as a property of the this inside the constructor of the class.
  2. Set it as a public property of the class, this is the proposal.

The downside is that, all the instances will not share the same function object as it becomes an own property. Own properties are used for holding state not functions.

To add in prototype, which is where methods in ES6 class are added, so that all instances share the same function object. You have to add it to the prototype property of the class. This works as class is a syntactic sugar of the function constructor and every function has a prototype property which is inherited by all instances of that class.

class Foo {  constructor() {    // ...code    this.bar = bar; //becomes own property  }    //As a public field, becomes own property  baz = baz;  }function bar(){  console.log("from bar");}function baz(){  console.log("from baz");}function foobar(){ console.log("from foobar");}
const foo = new Foo();foo.bar();foo.baz();//Set it directly on the prototype prop of the ClassFoo.prototype.foobar = foobar; foo.foobar();

Javascript classes prototype vs internal function declaration vs etc

Summery:

function MyClass(){
//You can access everything from in here (and from all sub functions) including prototypes and statics that are defined afterwards.
var privateVariable = "PriTest"; //Pair cannot by seen outside of MyClass
function privateFunction(){
}

this.publicVariable = "pubTest"; //Pair can be seen by everything except static functions
function publiFunction(){
}this.publiFunction = publiFunction;

this.prototypeFunction(); //This group could of been called like this from anywhere in this object
alert(this.prototypeVariable);
MyClass.staticFunction();
alert(MyClass.staticVariable);
}

MyClass.prototype.prototypeFunction = function(){
//Can access other prototypes, statics, and public functions and variables
this.publicFunction();
this.prototypeVariable;
MyClass.staticFunction();
}
MyClass.prototype.prototypeVariable = "proTest"

MyClass.staticFunction = function(){
//Can access other statics only
alert(MyClass.staticVariable);
}
MyClass.staticVariable = "staTest"

Please tell me if I have anything wrong in the following.

Private (accessible internally): [Same as java]
var variableName || function functionName inside of the object.
Can only be accessed by other private or privileged functions.

Public and Privileged (accessible externally {can still work with all internal objects}): [Same as java's public]
this.variableName || this.functionName = function(){ ... } inside of the object.

Prototype (accessed by other prototypes): [almost outside of the class and can only access publicly available objects]
Class.prototype.variableName || Class.prototype.functionName
Functions declared this way will have access to any public or prototype variables. Attempts to change variable created this way will instead create a new public variable on the object and the prototype variable will be unavailable.

Static: [Same as java?]
Class.variableName || Class.functionName
Can be changed by any function or method.

function MyClass(){
//public, privileged, and private
//Everything in here can see each other
//Everything in here can see everything outside
}
//Prototype and Static
//Prototype, Basically a template that is used on an instance of a class (And therefore does not have access to any of the non public fields)

Prototype Example:

MyClass.prototype.proExample = function(){
this.doSomething;
}
//Is basically equivalent to
function proExample(instanceOfMyClass){
instanceOfMyClass.doSoemthing;
}

Will add to this after I do some tests.

Advantages of using prototype, vs defining methods straight in the constructor?

Methods that inherit via the prototype chain can be changed universally for all instances, for example:

function Class () {}
Class.prototype.calc = function (a, b) {
return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
var args = Array.prototype.slice.apply(arguments),
res = 0, c;

while (c = args.shift())
res += c;

return res;
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

Notice how changing the method applied to both instances? This is because ins1 and ins2 share the same calc() function. In order to do this with public methods created during construction, you'd have to assign the new method to each instance that has been created, which is an awkward task. This is because ins1 and ins2 would have their own, individually created calc() functions.

Another side effect of creating methods inside the constructor is poorer performance. Each method has to be created every time the constructor function runs. Methods on the prototype chain are created once and then "inherited" by each instance. On the flip side of the coin, public methods have access to "private" variables, which isn't possible with inherited methods.

As for your function Class() {} vs var Class = function () {} question, the former is "hoisted" to the top of the current scope before execution. For the latter, the variable declaration is hoisted, but not the assignment. For example:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); }

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }

Proper way to require exported methods into a class (prototype?)

This method works, but arrow functions don't bind this and arguments so it cannot access the this.a or this.b

AND, You have a typo in your MyClass it's constructor.

Simple Proof of concept:
Simple Code example:

const methodOne = function () {
console.log(this.a);
}

class MyClass {
constructor(a, b) {
this.a = a;
this.b = b;
}
m2() {
console.log(this.b);
}
}
MyClass.prototype.methodOne = methodOne;

const tryMe = new MyClass('I work', 'I work too');
tryMe.methodOne()
tryMe.m2();

Output:

I work
I work too

ES6 - declare a prototype method on a class with an import statement

You can still attach a method on a class' prototype; after-all, classes are just syntactic sugar over a "functional object", which is the old way of using a function to construct objects.

Since you want to use ES6, I'll use an ES6 import.

Minimal effort, using the prototype:

import getColor from 'path/to/module';

class Car {
...
}

Car.prototype.getColor = getColor;

As you can see, you still use the prototype property to attach a method, should you choose to.


Calling the module within a class' method:

Alternatively, if you don't want to use the prototype property, you can always have your method return the function from the module:

import getColor from 'path/to/module';

class Car {
getColor () {
return getColor.call(this);
}
}

Using a Getter

You could also be a bit tricky and use a "getter" to achieve this in a different manner.

import getColor from 'path/to/module';

class Car {
get getColor () { return getColor.bind(this) }
}

You could then use it simply by calling, myInstanceOfCar.getColor()

Or in a more semantic usage of a getter:

class Car {
get color () { return getColor.call(this) }
}

// ...

const color = myInstanceOfCar.color;

Keep in mind that getters/setters cannot have the same name as properties that you set in the constructor. You will end up exceeding the maximum call-stack with infinite recursion when you try to use the setter to set that same property. Example: set foo (value) { this.foo = value }


ES2016 Class Properties

If you're using Babel to transpile (and are using experimental proposals), and want to use some ES2016, you can use the following syntax (but keep in mind that this applies the method to the object directly, and does not set it on the prototype):

import getColor from 'path/to/module';

class Car {
getColor = getColor;
}

Optional binding w/ class properties

If you use the shorthand syntax for setting a property, you won't have to bind the method (setting is as a property changes what "this" refers to, essentially automatically binding it), but you certainly can, should you choose to (like if you'd like to bind something else):

getColor = getColor.bind(this);


Related Topics



Leave a reply



Submit