简体   繁体   中英

ES6 static method vs non-static and ES5 OOP

I'm a bit confused.

If, for example...:

function Person(firstName, lastName){
    this.firstName = firstName;
    this.lastName = lastName;
}
// so, the following will be shared, and this 
// is the best practice, right?:
Person.prototype.whatEver = function(){
    return this.firstName + " " + this.lastName;
}
// the following will also be shared, but it's 
// bad practice, because it will be included in 
// each instances and take space(memory) for nothing. correct?
Person.whatEver = function(){
    return this.firstName + " " + this.lastName;
}

if the above correct and I got this right, then why the hell would I ever need a static functions? because this:

Person.whatEver = function(){
    return this.firstName + " " + this.lastName;
}

is exactly as this, correct?:

// it would need to be inside of the class, of course
static whatEver(){
    return this.firstName + " " + this.lastName;
}

While this is equivalent to an ES6 static method, it's not doing what you think:

Person.whatEver = function(){
  return this.firstName + " " + this.lastName;
}

That puts the whatEver function on the Person constructor , not on an instantiated person - and the Person constructor probably does not have a firstName or lastName . You were probably thinking of

const person = new Person();
person.whatEver = function(){
  return this.firstName + " " + this.lastName;
}

in which case, yes, running code like that every time a Person is instantiated will result in lots of functions (one for each person) rather than just a single function on the prototype.

The use for static methods is generally outside of interactions with instantiated objects. Think of when you might want information associated with a class even if nothing has necessarily been instantiated yet. For example, you could have a static method on the Person class:

class Person {
  static canEat() {
    return ['apples', 'bananas', 'carrots'];
  }
  // ...

The following will also be shared, but it's bad practice, because it will be included in each instances and take space(memory) for nothing. correct?

No it won't. You see methods in JS are assigned as references to them. This method will be created once and not be repeated again and again for each object. They'll point to the same method. This is a static method only. So both definitions of the whatEver function are statically defining it on the Person class.

So if you have code like:

let firstPerson = new Person();
let secondPerson = new Person();

Both of these objects will have reference to the same instance method(the prototype one) whatEver . Now the Person.whatEver static method will also have a single copy which can be called directly on the Person class only and no instance of this class.

//contstructor 
function Person () { … } 

//member method
Person.prototype.foo = function () { 
  console.log(this); 
}

//static method
Person.foo = function () { 
   console.log(this);
}

All function definitions are created only once in Memory. The diffrence is the »this context«:

new Person().foo(); // [Object Person] or Person {}

Person.foo(); // Person () {}

So that is why you cannot access member properties in a static function. this refers to the constructor function not to the instance. Under the hood, using the new operator will do something like this:

var obj = Person.call({}) // a new Object is created and passed as the
                          // »this context« and automatically returned.

If you would have:

function Person () {
  this.bar = function () { … }
} 

a new function object would created in Memory for each instance ( new Person() ), but that is the particular property of the prototype chain. Calling a method on an instance, will cause the JS interpreter to first search all »own« properties and than go up the prototype chain to find the requested property. The prototype is shared by all instances, so its properties are only created once.

In ES6 it would look like this:

class Person(){

  static foo () {}

  constructor () {}

  bar () {}

}

the following will also be shared, but it's bad practice, because it will be included in each instances and take space(memory) for nothing. correct?

This comes from the misunderstanding of how functions work in Javascript.

In class -based languages, when an instance of a class is created, all the method definitions, including the inherited ones, are copied .

Javascript is not class -based.

ES6 added class support, but it's only a sugar, and javascript class doesn't exactly act like classes in Java or C++ (or most other class ic languages)

static methods in Java or C++ doesn't require instantiation, which could be quite useful.

In Javascript, since there is no such thing as a class, or instantiation of a class, the methods do not get copied. Only the reference to the function gets copied.

In your code

Person.whatEver = function(){
    return this.firstName + " " + this.lastName;
}

For easier understanding, you can think of Person as an object with a function whatEver . So your 'static' method above is not too different from

let Person = {
  whatEver() {
    return this.firstName + " " + this.lastName;
  },
  talk() {
    return 'I can talk';
  },
};

For example, you can call Person.talk() without instantiation.

However, for your whatEver() function, if you need to access this in it, it's not a good idea to declare it as a static function, because the assumption is that this points to the instantiated object.

Class models don't fit into javascript quite well, and the confusion often originates from that.


I want to add one more thing:

// so, the following will be shared, and this 
// is the best practice, right?:
Person.prototype.whatEver = function(){
    return this.firstName + " " + this.lastName;
}

This is not true. All functions in Javascript is shared, and Person.prototype.whatEver and Person.whatEver are just two different ways of declaring functions.

Person.prototype.whatEver can be called on an object you create with new Person(..) , which is a constructor call . By this I mean when a function is called with new , an empty object gets created, this gets bound to this new object, and the object gets prototype -linked to Person .

So when you instantiate it like var p = new Person(..) , and call p.whatEver() , it will try to find whatEver(..) function reference in the prototype chain, and will find one in Person.prototype , which gets called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM