JavaScriptのクラスと静的メソッド


262

私はこれがうまくいくことを知っています:

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

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

でも電話したいなら

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

仕事をするためのいくつかの方法を見つけFoo.talk

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

これを行う他の方法はありますか?それが正しいのかどうかはわかりません。JavaScriptコードでクラスメソッドまたは静的メソッドを使用していますか?


14
Foo.talk = function ...
早期最適化、

1
@downvoterstepintothelight Foo.walk = function() {}はプロトタイプチェーン上にないため、インスタンスには影響しません。関数の[[prototype]]ポイントをその関数に向けさせるクロスブラウザの方法はありprototypeますか?
lostyzd

3
クラスメソッドは定義によってインスタンスに影響を与えないため、おそらく私はあなたが何を望んでいるのかわかりません
時期尚早の最適化

@downvoterstepintothelight私はそれを疑う、この方法は、Pythonのような言語では、インスタンスは、そのクラスのメソッドを呼び出すことができ、違いがあるthisのポインタ。
lostyzd

回答:


410

まず、JavaScriptはクラスベースの言語ではなく、主にプロトタイプ言語であることを覚えておいてください1。はクラスではなく、オブジェクトである関数です。キーワードを使用して、その関数からオブジェクトをインスタンス化できます。これにより、標準のOOP言語のクラスに似たものを作成できます。Foonew

__proto__クロスブラウザーのサポートが不十分であるため、ほとんどの場合は無視することをお勧めしprototypeます。代わりに、動作方法の学習に重点を置きます。

関数2から作成されたオブジェクトのインスタンスがあり、そのメンバー(メソッド、属性、プロパティ、定数など)の1つに何らかの方法でアクセスする場合、アクセスはプロトタイプ階層をたどって(a)が見つかるまで続きますメンバー、または(b)別のプロトタイプが見つからない。

階層は、呼び出されたオブジェクトから始まり、そのプロトタイプオブジェクトを検索します。プロトタイプオブジェクトにプロトタイプがある場合、それが繰り返され、プロトタイプが存在しない場合undefinedは返されます。

例えば:

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

これらの「基本的な」部分はすでにある程度理解しているように見えますが、念のため、それらを明示的にする必要があります。

JavaScriptでは、すべてがオブジェクトです3

すべてがオブジェクトです。

function Foo(){}新しい関数を定義するだけでなく、を使用してアクセスできる新しい関数オブジェクトを定義しFooます。

これがFooのプロトタイプにアクセスできる理由ですFoo.prototype

また、次の機能を設定することもできますFoo

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

この新しい機能には、次の方法でアクセスできます。

Foo.talk();

これで、関数オブジェクトの関数と静的メソッドの類似点に気付くでしょう。

f = new Foo();クラスインスタンスの作成、クラスFoo.prototype.bar = function(){...}の共有メソッドのFoo.baz = function(){...}定義、およびクラスのパブリック静的メソッドの定義と考えてください。


ECMAScript 2015は、これらの種類の宣言にさまざまな構文シュガーを導入し、それらを実装しやすくすると同時に読みやすくしました。したがって、前の例は次のように書くことができます。

class Foo {
  bar() {...}

  static baz() {...}
}

次のbarように呼び出すことができます:

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

次のbazように呼び出されます:

Foo.baz()

1:classECMAScript 5仕様の「Future Reserved Word」でしたが、ES6では、classキーワードを使用してクラスを定義する機能が導入されています。

2:本質的にはコンストラクターによって作成されたクラスインスタンスですが、誤解を招きたくない微妙な違いがあります。

3:プリミティブ値(、、ブール値、数値、文字列を含む)はundefinednull低水準言語の実装であるため、技術的にはオブジェクトではありません。ブール値、数値、および文字列は、オブジェクトのようにプロトタイプチェーンと相互作用するため、この回答の目的のために、完全ではないとしても、それらを「オブジェクト」と見なす方が簡単です。


1
@lostyzd-まあ、彼らはを介してそれにアクセスできますFoo.talk()。必要に応じて、コンストラクタでそれを割り当てることができthis.talk = Foo.talkます。-または、注記のとおり、を割り当てFoo.prototype.talk = Foo.talkます。しかし、これが良いアイデアかどうかはわかりません。原則として、インスタンスメソッドはインスタンスに固有である必要があります。
nrabinowitz、2011年

2
@Doug Avery Foo.talk()は、名前空間関数を呼び出しているだけです。Java / C#などのOOP言語で静的メソッドが呼び出されるのと同様の状況で使用します。ユースケースの良い例は、のような関数ですArray.isArray()
zzzzBov

7
PS nullはオブジェクトのtypeof null == 'object'
mvladk

1
足りない基本的な点は、静的メソッドが継承されることです。Foo.talk = function ()...独自のクラス名のサブクラスでは利用できません。これは、サブクラスを「拡張」することで回避できますが、よりエレガントな方法も探しています。

1
@nus、一部の言語のみが静的メソッドの継承を許可しています。継承が必要な場合は、最初に静的メソッドを使用しないでください。
zzzzBov 2013

67

あなたは以下のようにそれを達成することができます:

function Foo() {};

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

これで、以下のように「トーク」関数を呼び出すことができます。

Foo.talk();

JavaScriptでは関数もオブジェクトなので、これを行うことができます。


37

インスタンスから静的メソッドを呼び出します。

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

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

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

単純なJavascriptクラスプロジェクト:https : //github.com/reduardo7/sjsClass


13
これは静的な呼び出しではありません。var obj = new Clazz(); Clazzの新しいインスタンスを作成します。ただし、Clazz.staticMethod()は他のすべてのものなしで結果を達成します。
mpemburn 2014年

5
@mpemburn:エドゥアルドも彼の答えは正しいです。彼が示しているのは、「外部」から静的メソッドを呼び出すだけでなくClazz.staticMethod、インスタンス化されたオブジェクト内からこれらの静的メソッドにリンクする方法を示しています。これはNode.jsのような環境で特に役立ちます。requireを使用すると、元のコンストラクタに直接アクセスできない場合があります。私が追加する唯一のものはthis.constructor.staticMethod.apply(this, arguments);
Mauvis Ledford 2015

1
絶対に素晴らしい、コーヒースクリプトコンストラクタ内でも動作します。constructor: (a) -> @constructor.add @(まあ、とにかくほとんど)
Orwellophile

31

以下は、JavaScriptが静的/インスタンス変数とメソッドをどのように処理するかを示す良い例です。

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

良い点:これを介して静的関数にアクセスできないのは奇妙なことかもしれませんthis
TrapII、2015年

解決策をありがとうこれは私が探していたもので、thisキーワードのアクセスがあるでしょう
santhosh

30

追加で、今ではで行うことが可能ですclassし、static

'use strict'

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

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

};

あげる

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

例は千の言葉に値するので、これが最良の答えです。ただし、動作しない理由a.talk()は説明されていません。受け入れられた答えは、プロトタイプのチェーンがそれを見つけるべきだということですよね?しかし、そうではありません
Pynchia

11

私は名前空間を使用します:

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

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

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

そしてそれを使うには:

Foo.Talk("Testing");

または

Foo.ChangeElement();

6

ES6は現在サポートclassstatic魔法のようなキーワード:

class Foo {
    constructor() {}

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

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

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

}

こんな答えを探していました。静的メソッドは非静的メソッド/変数を呼び出すことができますか?
Tomasz Mularczyk

1
@Tomasz静的メソッドでは、「this」がクラスのインスタンスに設定されるのではなく、クラス自体に設定されます。したがって、もちろん、静的メソッドインスタンスメソッド呼び出すことができますが、それがインスタンスに何らかの方法でアクセスできる場合に限られます(例:´static staticMethod(){new Foo()。talk();)。} '
JHH

3

ES5で静的メソッドを記述する必要がある場合、そのための優れたチュートリアルを見つけました。

//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 ;)");  
};

@ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/を参照してください


2

ただ追加のメモ。クラスES6を使用して、静的メソッドを作成すると、Javacsriptエンジンが記述子属性を旧式の「静的」メソッドとは少し異なるように設定します。

function Car() {

}

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

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

これは、brand()の内部属性(記述子プロパティ)を

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

}
..

に比べ

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

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

brand()の内部属性を

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

..

ES6の静的メソッドのenumerablefalseに設定されていることを確認してください。

これは、for-inループを使用してオブジェクトをチェックできないことを意味します

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

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

ES6の静的メソッドは、他のクラスのプライベートプロパティ(名前、長さ、コンストラクター)と同様に処理されますが、静的メソッドは引き続き書き込み可能であるため、書き込み可能な記述子はtrueに 設定されます{ writable: true }。また、それをオーバーライドできることも意味します

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

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

1

あなたがコールにしようとするとFoo.talk、JS関数を検索しようとしtalk__proto__や、もちろん、それは見つけることができません。

Foo.__proto__ですFunction.prototype


1

静的メソッドの呼び出しはクラスで直接行われ、クラスのインスタンスでは呼び出すことができません。静的メソッドは、ユーティリティ関数の作成によく使用されます

かなり明確な説明

mozilla.orgから直接取得

Fooをクラスにバインドする必要があります。次に、新しいインスタンスを作成するときにmyNewInstance.foo()を呼び出すことができます。クラスをインポートする場合は、静的メソッドを呼び出すことができます。


0

私がそのような状況に直面したとき、私はこのようなことをしました:

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 () {}
};

だから今私はinfoメソッドを呼び出すことができます Logger.info("my Msg", "Tag");


私はいつもこれを行っていますが、それは基本的に単なる名前空間です。インスタンス変数を使用してインスタンスを作成することはできませんか?
dcsan 2018年

0

あなたの場合、あなたがしたい場合Foo.talk()

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

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

しかし、それprototypeは実装するための非効率的な方法です、使用する方が良いです。


別の方法では、私の方法は静的クラスとして定義されています:

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

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

上記の静的クラスはprototype、静的使用法として一度だけ構築されるため、使用する必要はありません。

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


@jvitorocありがとう!
Nick Tsai

0

JavaScriptには実際のクラスはなく、プロトタイプ継承のシステムを使用します。オブジェクトは、プロトタイプチェーンを介して他のオブジェクトから「継承」します。これはコード自体によって最もよく説明されています:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.