How can I refer to a class from a static method without using the class name itself in JavaScript (similar to PHP's self and self::method_name)?
For example, in the class below, how can I refer to the method foo and the method bar inside of the foobar method without the use of FooBar.methodName?
class FooBar {
static foo() {
return 'foo';
}
static bar() {
return 'bar';
}
static foobar() {
return FooBar.foo() + FooBar.bar();
// self::foo() + self::bar() would have been more desirable.
}
}
You can use the this keyword to refer to the object itself.
See example below:
class FooBar {
static foo() {
return 'foo';
}
static bar() {
return 'bar';
}
static foobar() {
return this.foo() + this.bar();
// self::foo() + self::bar() would have been more desirable.
}
}
const res = FooBar.foobar();
console.log(res);
Yes: the syntax you're asking about is "this".
From MDN:
https://medium.com/#yyang0903/static-objects-static-methods-in-es6-1c026dbb8bb1
As MDN describes it, “Static methods are called without instantiating
their class and are also not callable when the class is instantiated.
Static methods are often used to create utility functions for an
application.” In other words, static methods have no access to data
stored in specific objects.
...
Note that for static methods, the this keyword references the class.
You can call a static method from another static method within the
same class with this.
Also note:
There are two ways to call static methods:
Foo.methodName()
// calling it explicitly on the Class name
// this would give you the actual static value.
this.constructor.methodName()
// calling it on the constructor property of the class
// this might change since it refers to the class of the current instance, where the static property could be overridden
If all of those methods are static then you can use this.
class FooBar {
static foo() {
return 'foo';
}
static bar() {
return 'bar';
}
static foobar() {
return this.foo() + this.bar();
}
}
Related
What is the difference between the following two declarations?
Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }
Is it okay to think of the first statement as a declaration of a static method, and the second statement as a declaration of an instance 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();
Yes, the first one is a static method also called class method, while the second one is an instance method.
Consider the following examples, to understand it in more detail.
In ES5
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.isPerson = function(obj) {
return obj.constructor === Person;
}
Person.prototype.sayHi = function() {
return "Hi " + this.firstName;
}
In the above code, isPerson is a static method, while sayHi is an instance method of Person.
Below, is how to create an object from Person constructor.
var aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
In ES6
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static isPerson(obj) {
return obj.constructor === Person;
}
sayHi() {
return `Hi ${this.firstName}`;
}
}
Look at how static keyword was used to declare the static method isPerson.
To create an object of Person class.
const aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
NOTE: Both examples are essentially the same, JavaScript remains a classless language. The class introduced in ES6 is primarily a syntactical sugar over the existing prototype-based inheritance model.
When you create more than one instance of MyClass , you will still only have only one instance of publicMethod in memory but in case of privilegedMethod you will end up creating lots of instances and staticMethod has no relationship with an object instance.
That's why prototypes save memory.
Also, if you change the parent object's properties, is the child's corresponding property hasn't been changed, it'll be updated.
For visual learners, when defining the function without .prototype
ExampleClass = function(){};
ExampleClass.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// >> error! `someInstance.method is not a function`
With same code, if .prototype is added,
ExampleClass.prototype.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method();
// > error! `ExampleClass.method is not a function.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// > output: `Called from instance`
To make it clearer,
ExampleClass = function(){};
ExampleClass.directM = function(){} //M for method
ExampleClass.prototype.protoM = function(){}
var instanceOfExample = new ExampleClass();
ExampleClass.directM(); ✓ works
instanceOfExample.directM(); x Error!
ExampleClass.protoM(); x Error!
instanceOfExample.protoM(); ✓ works
****Note for the example above, someInstance.method() won't be executed as,
ExampleClass.method() causes error & execution cannot continue.
But for the sake of illustration & easy understanding, I've kept this sequence.****
Results generated from chrome developer console & JS Bin
Click on the jsbin link above to step through the code.
Toggle commented section with ctrl+/
A. Static Method:
Class.method = function () { /* code */ }
method() here is a function property added to an another function (here Class).
You can directly access the method() by the class / function name. Class.method();
No need for creating any object/instance (new Class()) for accessing the method(). So you could call it as a static method.
B. Prototype Method (Shared across all the instances):
Class.prototype.method = function () { /* code using this.values */ }
method() here is a function property added to an another function protype (here Class.prototype).
You can either directly access by class name or by an object/instance (new Class()).
Added advantage - this way of method() definition will create only one copy of method() in the memory and will be shared across all the object's/instance's created from the Class
C. Class Method (Each instance has its own copy):
function Class () {
this.method = function () { /* do something with the private members */};
}
method() here is a method defined inside an another function (here Class).
You can't directly access the method() by the class / function name. Class.method();
You need to create an object/instance (new Class()) for the method() access.
This way of method() definition will create a unique copy of the method() for each and every objects created using the constructor function (new Class()).
Added advantage - Bcos of the method() scope it has the full right to access the local members(also called private members) declared inside the constructor function (here Class)
Example:
function Class() {
var str = "Constructor method"; // private variable
this.method = function () { console.log(str); };
}
Class.prototype.method = function() { console.log("Prototype method"); };
Class.method = function() { console.log("Static method"); };
new Class().method(); // Constructor method
// Bcos Constructor method() has more priority over the Prototype method()
// Bcos of the existence of the Constructor method(), the Prototype method
// will not be looked up. But you call it by explicity, if you want.
// Using instance
new Class().constructor.prototype.method(); // Prototype method
// Using class name
Class.prototype.method(); // Prototype method
// Access the static method by class name
Class.method(); // Static method
I have a class declaration which is quite large with lots of methods.
It's something like this:
class Foo {
constructor() {
// ...code
}
config() {
// ...code
}
theme() {
// ...code
}
register() {
// ...code
}
render() {
// ...code
}
write() {
// ...code
}
}
I'd like to split the source code up so it's easy to manage and read.
Is there a way I can assign a method to a class dynamically within the class itself?
I'd like to do something like:
function bar(value) {
if (value) {
this.baz = 'baz'
return this
}
}
class Foo {
constructor() {
// ...code
}
new bar() // Would reference function above ^ (not valid)
}
I know this is not valid or correct but because the class syntax automatically creates and binds a property called bar() to the prototype of Foo I can't think how you would do it, and perhaps the functionality doesn't exist.
The closest I can think to splitting the code up, is to extend a class but this seems a bit verbose.
class Bar {
bar() {
if (value) {
this.baz = 'baz'
return this
}
}
}
class Foo extends Bar {
constructor() {
super()
}
}
bar() is now in Foo's prototype.
This method also offers little control to creating aliases if needed and also is harder to make it clear what methods that are available in the Foo class.
Is there a better way?
I can think of two ways of adding external functions as own properties:
Set it as a property of the this inside the constructor of the class.
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 Class
Foo.prototype.foobar = foobar;
foo.foobar();
Also, I presume there isn't a way to set a prototype of a class inside
the class definition itself?
From your question in the comment, yes you can set the function in the prototype of the class inside the class definition itself:
function bar(){
console.log("bar in prototype");
console.log("accessing data from this: ", this.data);
}
class Foo{
constructor(){
this.data = "data"
Foo.prototype.bar = bar;
Foo.prototype.thisBar = bar.bind(this);
}
//methods are added in prototypes
baz(){
console.log("baz in prototype");
}
}
const foo = new Foo();
foo.bar();
foo.baz();
//accessing from prototype, here this is global context and will print undefined
Object.getPrototypeOf(foo).bar();
//this is bound so always refers to the instance
Object.getPrototypeOf(foo).thisBar();
//same for methods in es6
Object.getPrototypeOf(foo).baz();
For static methods just set it either as a property of the class or set it as public static property.
class Bar{
constructor(){
//some logic
}
static baz = baz;
}
function foo(){
console.log("from foo");
}
function baz(){
console.log("from baz");
}
Bar.foo = foo;
Bar.foo();
Bar.baz();
I wish to be able to extend any object given in an .extend(obj) function. So far I have it working except for when an object literal is being passed in.
example:
class myClass {
static extend(obj) {
Object.assign(Object.getPrototypeOf(obj), myClass.prototype);
myClass.call(obj);
}
sayHello() {
return 'hello!'
}
}
This works fine when the extend function is called from within a constructor like so:
function foo() {
myClass.extend(this);
}
const bar = new foo();
bar.sayHello();
However when I pass in an object literal which is already created the methods from myClass.prototype are not available.
const foo = {};
myClass.extend(foo);
foo.sayHello(); // this is not available.
Is there a way to check the last case and assign the prototype to the object itself instead of it's prototype so that the last scenario will also work?
static extend() {
if (/* obj is an object literal */) {
Object.assign(obj, myClass.prototype);
} else {
// first example
}
This works fine when the extend function is called from within a constructor like so:
It shouldn't work fine, and when I ran it I got the error Class constructor myClass cannot be invoked without 'new'. That error is because of the statement myClass.call(obj);. Only if I change the class to an ES5 constructor function does it work.
However when I pass in an object literal which is already created the methods from myClass.prototype are not available.
They were for me.
function myClass() {}
myClass.extend = function(obj) {
Object.assign(Object.getPrototypeOf(obj), myClass.prototype);
myClass.call(obj);
}
myClass.prototype.sayHello = function() {
return 'hello!'
}
const foo = {};
myClass.extend(foo);
foo.sayHello(); // "hello!"
Babel is doing its magic, which makes me very confused about what's going on.
What's the difference between foo and bar in this react Component? And when should I use which?
class MyComponent extends Component {
foo() {
//...
}
bar = () => {
//...
}
}
(My own guess is foo is in the prototype, and bar is in the constructor? Anyways, I don't know what I'm talking about)
My own guess is foo is in the prototype, and bar is in the constructor?
That's exactly right.
foo() {}
in this context is a method declaration and the value gets assigned to the prototype. It's equivalent to
MyComponent.prototype.foo = function() {};
bar = ... ;
on the other hand is a class field. This is a shorthand notation for assigning properties to the instance in the constructor:
constructor() {
this.bar = ... ;
}
And because of how arrow functions work, using class fields with arrow functions basically lets you create "bound" methods.
More on arrow functions: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?
And when should I use which?
The tl;dr is: Use class field + arrow function when you need a bound function.
When do you need a bound function? Whenever you want this inside the function to refer to a specific value but you don't control how the function is called.
That's mostly the case for event handlers (i.e. functions that are passed to other functions / components) that need to access the component instance (e.g. call this.setState or access this.props/this.state).
You don't have to use it though, you could also bind the method when you need to. However, binding a method only once in the constructor is ideal for React components so that, if the method is passed as event handler, always the same function object is passed.
As noted in another answer, this is not related to React at all. Class fields are likely officially integrated into the language this year.
In the second example bar is an arrow function.
Until arrow functions, every new function defined its own this value.
For instance, this can be a new object in the case of a constructor.
function Person(age){
this.age=age;
console.log(this);
}
let person=new Person(22);
Or this can points to the base object if the function created can be accessed like obj.getAge().
let obj={
getAge:function(){
console.log(this);
return 22;
}
}
console.log(obj.getAge());
An arrow function does not create its own this, it's just used the this value of the enclosing execution context. In the other hand, arrow function uses this of parent scope.
foo is an instance method of class MyComponent. While bar is an instance property of class MyComponent (that happens to be assigned an anonymous function).
It might make more sense to see it used in the traditional sense...
class MyComponent {
// instance property
someProperty = 42;
// instance method
someMethod() {}
}
So why use an instance property instead of an instance method?
Instance methods in javascript must be called within the class scope to inherit the class context (this)...
class MyComponent {
// instance method
someMethod() {
const value = this.state.value // ERROR: `this` is not defined
}
render() {
return <div onClick={this.someMethod} />
}
}
However, since arrow functions inherit their scope, this will be available if you use an arrow function instead
class MyComponent {
// instance method
someProperty = () => {
const value = this.state.value // this works!
}
render() {
return <div onClick={this.someProperty} />
}
}
Essentially, there is no difference.
This has nothing to do with React, though.
Arrow functions
Arrow functions are a relatively new feature added to Javascript that give you a way of defining functions in a more concise way.
function foo (param1, param2, etc) {
// do something
}
// becomes
var foo = (param1, param2, etc) => {
// do something
}
If you have only 1 param, you don't need the parantheses:
function foo (param) {
// do something
}
// becomes
var foo = param => {
// do something
}
If there is only 1 expression, and the result is returned, you can omit the {} too!
function foo (param) {
returns param * 2
}
// becomes
var foo = param1 => param * 2
this is not not updated
In an arrow function, you don't get a new this - it is the same as in the parent block. This can be useful in some cases (when using setTimeout, for example)
JS Classes
In ES6 we can also use the class keyword to define "classes" in Javascript. They still use prototype, though!
function Something (foo) {
this.bar = foo;
}
Something.prototype.baz = function () {
return baz * 2
}
// becomes
class Something {
constructor(foo) {
this.bar = foo;
}
baz () {
return baz * 2
}
}
So the constructor is done in constructor() and all the following methods are added to the Prototype. It's nothing but syntactic sugar (but kinda does some of the Prototype heavy lifting for you)
You can use extends!
class SomethingElse extends Something {
constructor(foo, lol) {
this.lol = lol
// Use super() to call the parent's constructor
super(foo)
}
baz () {
return baz * 2
}
}
Firstly the code that will explain my query
class myClass {
constructor() {
this.myVariable = 10;
}
myFunctionCalledFromStaticFunction() {
console.log (`myFunctionCalledFromStaticFunction myvariable is ${this.myVariable}`);
}
static staticMyFunctionTwo() {
console.log (`staticMyFunctionTwo myvariable is ${this.myVariable}`);
this.myFunctionCalledFromStaticFunction();
}
static staticMyFunction() {
console.log (`staticMyFunction myvariable is ${this.myVariable}`);
myClass.staticMyFunctionTwo();
}
myFunctionTwo() {
console.log (`myFunctionTwo myvariable is ${this.myVariable}`)
myClass.staticMyFunction();
}
myFunction() {
console.log (`myFunction myvariable is ${this.myVariable}`)
this.myFunctionTwo();
}
}
(function ($) {
'use strict';
const helloClass = new myClass();
console.log (`main myvariable is ${helloClass.myVariable}`)
helloClass.myFunction();
myClass.staticMyFunctionTwo();
}(jQuery));
And, now a codePen example https://codepen.io/hellouniverse/pen/jYyYre
Now, a disclaimer I have to give. I have searched through stackoverflow, online and my own experience. I am quite certain it is not possible.
If you take the code and run it or check in the codepen, you will notice that myVariable is undefined in static functions. Also, I can not call a normal function from a static function.
Am I correct in this statements? Or is it possible or has a workaround?
I assume that you want to have accessible primitive data across the static and non-static methods. For such cases you would rather need to use Symbol or const.
How to use symbol you can read here: MDN Symbol
And your modified examples: using Symbol and using the const keyword
Looking at your codepen, the answer is no you cannot use this in a static function call, UNLESS you are calling another static method. As MDN points out, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static, you can have this
class Logger {
static error(message) {
this.log(message, 'error');
}
static log(message, level) {
console[level](message);
}
}
You have to understand what a class method desugars to. Consider
class Foo {
static bar () {
console.log(this.fooProp);
}
};
Is more-or-less the same as this:
function Foo() {};
Foo.bar = function () { console.log(this.fooProp); };
Note that it isn't Foo.prototype.bar. Functions are Objects in JavaScript, and just like any other object when you call a method of an object (e.g. Foo.bar()) then this is set to the object it is called on (e.g. the function 'Foo', not an instance of Foo). I can say
Foo.fooProp = 3;
Foo.bar(); // logs 3
Without ever actually creating an instance of Foo. I can also call it from an instance
let foo = new Foo();
foo.constructor.bar(); // logs 3
But the this value will always refer to the class/constructor rather than the instance
foo.fooProp = 5;
foo.constructor.bar(); // still 3
Unless one does something like this:
Foo.bar.call(foo); // now it's 5