web-dev-qa-db-fra.com

Méthode vs méthode statique en JavaScript

Je sais que ça va marcher:

function Foo() {};
Foo.prototype.talk = function () {
    alert('hello~\n');
};

var a = new Foo;
a.talk(); // 'hello~\n'

Mais si je veux appeler

Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly

Je trouve des méthodes pour faire fonctionner Foo.talk,

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

Y a-t-il d'autres moyens de le faire? Je ne sais pas s’il est juste de le faire. Utilisez-vous des méthodes de classe ou des méthodes statiques dans votre code JavaScript?

251
lostyzd

Tout d’abord, rappelez-vous que JavaScript est avant tout un langage prototype , et non un langage basé sur les classes1. Foo n'est pas une classe, c'est une fonction, qui est un objet. Vous pouvez instancier un objet à partir de qui fonctionne à l'aide du mot clé new, qui vous permettra de créer quelque chose de similaire à une classe dans un langage standard OOP. .

Je suggérerais d'ignorer __proto__ la plupart du temps, car il ne prend pas en charge plusieurs navigateurs et se concentre plutôt sur l'apprentissage du fonctionnement de prototype.

Si vous avez une instance d'un objet créée à partir d'une fonction2 et que vous accédiez à l’un de ses membres (méthodes, attributs, propriétés, constantes, etc.) de quelque manière que ce soit, l’accès descendra dans la hiérarchie du prototype jusqu’à ce que (a) trouve le membre ou (b) ne trouve pas un autre prototype.

La hiérarchie commence sur l'objet appelé, puis recherche son objet prototype. Si l'objet prototype a un prototype, il répète; si aucun prototype n'existe, undefined est renvoyé.

Par exemple:

foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"

foo = {};
console.log(foo.bar); // logs undefined

function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set

Il me semble que vous avez déjà au moins un peu compris ces parties "de base", mais je dois les expliciter pour être sûr.

En JavaScript, tout est un objet3.

tout est un objet.

function Foo(){} ne définit pas seulement une nouvelle fonction, il définit un nouvel objet de fonction accessible à l'aide de Foo.

C'est pourquoi vous pouvez accéder au prototype de Foo avec Foo.prototype.

Ce que vous pouvez également faire est de définir plus de fonctions sur Foo:

Foo.talk = function () {
  alert('hello world!');
};

Cette nouvelle fonction est accessible via:

Foo.talk();

J'espère que maintenant vous remarquerez une similitude entre les fonctions sur un objet fonction et une méthode statique.

Pensez à f = new Foo(); comme à la création d'une instance de classe, Foo.prototype.bar = function(){...} à la définition d'une méthode partagée pour la classe et à Foo.baz = function(){...} à la définition d'une méthode statique publique pour la classe.


ECMAScript 2015 a introduit une variété de sucres syntaxiques pour ce type de déclarations afin de les rendre plus simples à mettre en œuvre tout en facilitant la lecture. L'exemple précédent peut donc être écrit comme suit:

class Foo {
  bar() {...}

  static baz() {...}
}

qui permet à bar d'être appelé comme:

const f = new Foo()
f.bar()

et baz être appelé comme:

Foo.baz()

1: class était un "mot réservé futur" dans la spécification ECMAScript 5 , mais ES6 introduit la possibilité de définir des classes à l'aide du mot clé class.

2: essentiellement une instance de classe créée par un constructeur, mais il y a beaucoup de différences nuancées que je ne veux pas vous induire en erreur

3: valeurs primitives - qui inclut undefined, null, booléens, nombres et chaînes de caractères - ne sont techniquement pas des objets, car ils implémentent un langage de bas niveau. Les booléens, les nombres et les chaînes interagissent toujours avec la chaîne de prototypes comme s'il s'agissait d'objets, si bien que pour les besoins de cette réponse, il est plus facile de les considérer comme des "objets" même s'ils ne sont pas tout à fait.

384
zzzzBov

Vous pouvez y arriver comme ci-dessous:

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

Vous pouvez maintenant appeler la fonction "talk" comme ci-dessous:

Foo.talk();

Vous pouvez le faire car, en JavaScript, les fonctions sont également des objets. "zzzzBov" y a également répondu, mais la lecture est longue.

67
Bipul

Appelez une méthode statique à partir d'une instance:

function Clazz() {};
Clazz.staticMethod = function() {
    alert('STATIC!!!');
};

Clazz.prototype.func = function() {
    this.constructor.staticMethod();
}

var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"

Projet de classe Javascript simple: https://github.com/reduardo7/sjsClass

36
Eduardo Cuomo

Voici un bon exemple pour montrer comment Javascript fonctionne avec des variables et méthodes statiques/d'instance.

function Animal(name) {
    Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
    this.name = name; //instance variable, using "this"
}

Animal.showCount = function () {//static method
    alert(Animal.count)
}

Animal.prototype.showName=function(){//instance method
    alert(this.name);
}

var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");

Animal.showCount();  // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from  Java
31
Jaskey

En plus, il est maintenant possible de faire avec class et static

'use strict'

class Foo {
 static talk() {
     console.log('talk')
 };

 speak() {
     console.log('speak')
 };

};

va donner

var a = new Foo();
Foo.talk();  // 'talk'
a.talk();    // err 'is not a function'
a.speak();   // 'speak'
Foo.speak(); // err 'is not a function'
27
Dima Fomin

J'utilise des espaces de noms:

var Foo = {
     element: document.getElementById("id-here"),

     Talk: function(message) {
            alert("talking..." + message);
     },

     ChangeElement: function() {
            this.element.style.color = "red";
     }
};

Et pour l'utiliser:

Foo.Talk("Testing");

Ou

Foo.ChangeElement();
11
A-Sharabiani

ES6 prend désormais en charge class & static comme un charme:

class Foo {
    constructor() {}

    talk() {
        console.log("i am not static");
    }

    static saying() {
        console.log(this.speech);
    }

    static get speech() {
        return "i am static method";
    }

}
6
Abdennour TOUMI

Si vous devez écrire des méthodes statiques dans ES5, j'ai trouvé un excellent tutoriel pour cela:

//Constructor
var Person = function (name, age){
//private properties
var priv = {};

//Public properties
this.name = name;
this.age = age;

//Public methods
this.sayHi = function(){
    alert('hello');
}
}


// A static method; this method only 
// exists on the class and doesn't exist  
// on child objects
Person.sayName = function() {
   alert("I am a Person object ;)");  
};

voir @ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/

3
Combine

Juste des notes supplémentaires. En utilisant la classe ES6, lorsque nous créons des méthodes statiques..le moteur Javacsript définit l'attribut descripteur un peu différent de la méthode "statique" de la vieille école.

function Car() {

}

Car.brand = function() {
  console.log('Honda');
}

console.log(
  Object.getOwnPropertyDescriptors(Car)
);

il définit l'attribut interne (propriété du descripteur) pour brand () sur

..
brand: [object Object] {
    configurable: true,
    enumerable: true,
    value: ..
    writable: true

}
..

par rapport à

class Car2 {
   static brand() {
     console.log('Honda');
   }
}

console.log(
  Object.getOwnPropertyDescriptors(Car2)
);

qui définit l'attribut interne pour brand () sur

..
brand: [object Object] {
    configurable: true,
    enumerable: false,
    value:..
    writable: true
  }

..

voyez que enumerable est défini sur false pour la méthode statique dans ES6.

cela signifie que vous ne pouvez pas utiliser la boucle for-in pour vérifier l'objet

for (let prop in Car) {
  console.log(prop); // brand
}

for (let prop in Car2) {
  console.log(prop); // nothing here
}

la méthode statique dans ES6 est traitée comme la propriété privée de la classe de l'autre (nom, longueur, constructeur), sauf que la méthode statique est toujours accessible en écriture. Le descripteur accessible en écriture est défini sur true{ writable: true }. cela signifie également que nous pouvons le remplacer

Car2.brand = function() {
   console.log('Toyota');
};

console.log(
  Car2.brand() // is now changed to toyota
);
2
ngakak

Lorsque vous essayez d'appeler Foo.talk, le JS tente de rechercher une fonction talk à __proto__ et, bien entendu, est introuvable.

Foo.__proto__ est Function.prototype.

1
Issac Young

Les appels de méthodes statiques sont effectués directement sur la classe et ne sont pas appelables sur les instances de la classe. Les méthodes statiques sont souvent utilisées pour créer une fonction utilitaire

Description assez claire

tiré directement de mozilla.org

Foo doit être lié à votre classe. Lorsque vous créez une nouvelle instance, vous pouvez appeler myNewInstance.foo (). Si vous importez votre classe, vous pouvez appeler une méthode statique.

1
Dave Keane

Quand j'ai fait face à une telle situation, j'ai fait quelque chose comme ça:

Logger = {
    info: function (message, tag) {
        var fullMessage = '';        
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.log(fullMessage);
        }
    },
    warning: function (message, tag) {
        var fullMessage = '';
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.warn(fullMessage);`enter code here`
        }
    },
    _getFormatedMessage: function () {}
};

alors maintenant je peux appeler la méthode info en tant que Logger.info("my Msg", "Tag");

0
Vishnu

Javascript n'a pas de classe réelle mais utilise un système d'héritage prototypique dans lequel les objets "héritent" d'autres objets via leur chaîne de prototypes. Ceci est mieux expliqué via le code lui-même:

function Foo() {};
// creates a new function object

Foo.prototype.talk = function () {
    console.log('hello~\n');
};
// put a new function (object) on the prototype (object) of the Foo function object

var a = new Foo;
// When foo is created using the new keyword it automatically has a reference 
// to the prototype property of the Foo function

// We can show this with the following code
console.log(Object.getPrototypeOf(a) === Foo.prototype); 

a.talk(); // 'hello~\n'
// When the talk method is invoked it will first look on the object a for the talk method,
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)

// When you want to call
// Foo.talk();
// this will not work because you haven't put the talk() property on the Foo
// function object. Rather it is located on the prototype property of Foo.

// We could make it work like this:
Foo.sayhi = function () {
    console.log('hello there');
};

Foo.sayhi();
// This works now. However it will not be present on the prototype chain 
// of objects we create out of Foo
0

Dans votre cas, si vous voulez Foo.talk():

function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
    alert('hello~\n');
};

Foo.talk(); // 'hello~\n'

Mais c’est un moyen inefficace de mettre en œuvre, utiliser prototype est préférable.


Une autre façon, ma manière est définie comme classe statique:

var Foo = new function() {
  this.talk = function () {
    alert('hello~\n');
    };
};

Foo.talk(); // 'hello~\n'

La classe statique ci-dessus n'a pas besoin d'utiliser prototype car elle ne sera construite qu'une seule fois en tant qu'utilisation statique.

https://github.com/yidas/js-design-patterns/tree/master/class

0
Nick Tsai