ウェブサイト検索

JavaScriptにおける静的変数


Javascriptで静的変数をどのように作成できますか?

回答 #1

もしあなたがクラスベースの静的型付けオブジェクト指向言語(Java、C++、C#のような)出身であれば、あなたはおそらくインスタンスではなく「型」に関連付けられた変数やメソッドを作ろうとしていると考えられます。

コンストラクタ関数を使った「古典的」アプローチの例は、基本的なOO JavaScriptの概念を理解するのに役立つかもしれません。

function MyClass () { // constructor function
  var privateVariable = "foo";  // Private variable 

  this.publicVariable = "bar";  // Public variable 

  this.privilegedMethod = function () {  // Public Method
    alert(privateVariable);
  };
}

// Instance method will be available to all instances but only load once in memory 
MyClass.prototype.publicMethod = function () {    
  alert(this.publicVariable);
};

// Static variable shared by all instances
MyClass.staticProperty = "baz";

var myInstance = new MyClass();

`staticProperty` は MyClass オブジェクト(これは関数です)に定義されており、作成されたインスタンスとは関係ありません。JavaScript は関数を第一級オブジェクトとして扱うので、オブジェクトであるため、関数にプロパティを割り当てることができます。

更新: ES6は、classキーワードを使用してクラスを宣言する機能を導入しました。これは既存のプロトタイプベースの継承に対するシンタックスシュガーです。

`static` キーワードを使うと、クラス内で静的なプロパティやメソッドを簡単に定義することができます。

上記の例をES6クラスで実装してみましょう:

class MyClass {
  // class constructor, equivalent to
  // the function body of a constructor
  constructor() {
    const privateVariable = 'private value'; // Private variable at the constructor scope
    this.publicVariable = 'public value'; // Public property

    this.privilegedMethod = function() {
      // Public Method with access to the constructor scope variables
      console.log(privateVariable);
    };
  }

  // Prototype methods:
  publicMethod() {
    console.log(this.publicVariable);
  }

  // Static properties shared by all instances
  static staticProperty = 'static value';

  static staticMethod() {
    console.log(this.staticProperty);
  }
}

// We can add properties to the class prototype
MyClass.prototype.additionalMethod = function() {
  console.log(this.publicVariable);
};

var myInstance = new MyClass();
myInstance.publicMethod();       // "public value"
myInstance.additionalMethod(); // "public value"
myInstance.privilegedMethod(); // "private value"
MyClass.staticMethod();             // "static value"

回答 #2

JSの関数はオブジェクトでもあるという事実を利用できるかもしれません。つまり、関数にプロパティを持たせることができるということです。

例えば、(現在は消滅した)記事『Javascriptの静的変数』で示された例を引用すると:

function countMyself() {
    // Check to see if the counter has been initialized
    if ( typeof countMyself.counter == 'undefined' ) {
        // It has not... perform the initialization
        countMyself.counter = 0;
    }

    // Do something stupid to indicate the value
    alert(++countMyself.counter);
}

その関数を何度も呼び出すと、カウンターが増加しているのがわかります。

そして、これはグローバル変数でグローバル名前空間を汚染するよりも、おそらくはるかに良い解決策です。

こちらはクロージャに基づく別の可能な解決策です:JavaScriptで静的変数を使うトリック:

var uniqueID = (function() {
   var id = 0; // This is the private persistent value
   // The outer function returns a nested function that has access
   // to the persistent value.  It is this nested function we're storing
   // in the variable uniqueID above.
   return function() { return id++; };  // Return and increment
})(); // Invoke the outer function after defining it.

これにより同じ種類の結果が得られます――ただし、今回は表示されるのではなく、増分された値が返されます。

回答 #3

これはIIFE(即時実行関数式)を通して行います:

var incr = (function () {
    var i = 1;

    return function () {
        return i++;
    }
})();

incr(); // returns 1
incr(); // returns 2

回答 #4

いくつか似たような回答を見たことがありますが、この投稿が最もよく説明していると思うので、共有したいと思います。

以下はそれから取ったコードの一部で、私はそれを修正して、クラスのデザインテンプレートとして使用できるため、コミュニティに役立つことを願って完全な例を作りました。

それはまた、あなたの質問に答えます:

function Podcast() {

    // private variables
    var _somePrivateVariable = 123;

    // object properties (read/write)
    this.title = 'Astronomy Cast';
    this.description = 'A fact-based journey through the galaxy.';
    this.link = 'http://www.astronomycast.com';

    // for read access to _somePrivateVariable via immutableProp 
    this.immutableProp = function() {
        return _somePrivateVariable;
    }

    // object function
    this.toString = function() {
       return 'Title: ' + this.title;
    }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
    console.log('Downloading ' + podcast + ' ...');
};

その例を踏まえると、次のようにして静的プロパティ/関数にアクセスできます:

// access static properties/functions
console.log(Podcast.FILE_EXTENSION);   // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'

そして オブジェクトのプロパティ/関数 は単に次のように:

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString());       // Title: The Simpsons
console.log(podcast.immutableProp());  // 123

注意 podcast.immutableProp() では、クロージャ: _somePrivateVariable への参照が関数内に保持されています。

あなたは ゲッターとセッター さえ定義できます。次のコードスニペットを見てみましょう(ここで d はプロパティを宣言したいオブジェクトのプロトタイプで、y はコンストラクタの外から見えないプライベート変数です):

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

これはプロパティ d.year を get と set 関数を使って定義します。set を指定しない場合、そのプロパティは読み取り専用となり変更できません(設定しようとしてもエラーは発生しませんが、効果はありません)。各プロパティには writable、configurable(宣言後に変更可能)、enumerable(列挙可能)という属性があり、デフォルトではすべて false です。これらは 3 番目のパラメータで defineProperty を使って設定できます。例: enumerable: true。

これも有効な構文です:

// getters and setters - alternative syntax
var obj = { a: 7, 
            get b() {return this.a + 1;}, 
            set c(x) {this.a = x / 2}
        };

これにより、読み書き可能なプロパティ a、読み取り専用プロパティ b、および書き込み専用プロパティ c が定義され、プロパティ a にアクセスできます。

使用方法:

console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

ノート:

もし new キーワードを忘れた場合の予期しない動作を避けるために、関数 Podcast に次の内容を追加することをお勧めします:

// instantiation helper
function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
// [... same as above ...]
};

これで、次の両方のインスタンス化が期待どおりに動作するようになります:

var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast();     // you can omit the new keyword because of the helper

「new」文は新しいオブジェクトを作成し、すべてのプロパティとメソッドをコピーします。すなわち、

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

注意: 場合によっては、コンストラクタ関数 Podcast で return 文を使用して、クラス内部で依存するものの外部に公開する必要がある関数を保護するカスタムオブジェクトを返すことが有用な場合があります。これは記事シリーズの第2章(オブジェクト)でさらに詳しく説明されています。

あなたは、a と b が Podcast を継承していると言うことができます。では、a と b がインスタンス化された後に、それらすべてに適用されるメソッドを Podcast に追加したい場合はどうでしょうか?この場合、次のように .prototype を使用します。

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};

では、a と b をもう一度呼び出してください:

console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"

プロトタイプの詳細についてはここで確認できます。もし継承についてもっと学びたい場合は、これを調べることをお勧めします。

上で言及した記事シリーズは、読むことを強くお勧めします。以下のトピックも含まれています:

  1. 関数
  2. オブジェクト
  3. プロトタイプ
  4. コンストラクタ関数で new を強制する
  5. 巻き上げ
  6. 自動セミコロン挿入
  7. 静的プロパティとメソッド

注意 JavaScript の 自動セミコロン挿入 の「機能」(6.で述べたように)は、しばしばコードに奇妙な問題を引き起こす原因となります。したがって、私はむしろそれを機能ではなくバグと見なすべきだと考えます。

もしもっと読みたい場合は、これらのトピックについての非常に興味深いMSDN記事があります。その中で説明されているいくつかはさらに詳細を提供しています。

読むのに興味深いのは(上で述べたトピックもカバーしている)MDN JavaScriptガイドのこれらの記事です:

  • JavaScript の再入門
  • オブジェクトを扱う

JavaScriptでc#のoutパラメータをエミュレートする方法(DateTime.TryParse(str, out result)のように)を知りたい場合は、ここにサンプルコードがあります。

Internet Explorer(IE)で作業している方(開発者ツールをF12で開き、コンソールタブを開かない限り、JavaScript用のコンソールはありません)にとって、次のスニペットは役立つかもしれません。これは、上記の例で使用されているように console.log(msg); を使用できるようにします。Podcast 関数の前に挿入してください。

ご便宜のため、上記のコードを1つの完全なコードスニペットとしてまとめました:

let console = { log: function(msg) {  
  let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
  canvas.innerHTML += (br + (msg || "").toString());
}};

console.log('For details, see the explaining text');

function Podcast() {

  // with this, you can instantiate without new (see description in text)
  if (false === (this instanceof Podcast)) {
    return new Podcast();
  }

  // private variables
  var _somePrivateVariable = 123;

  // object properties
  this.title = 'Astronomy Cast';
  this.description = 'A fact-based journey through the galaxy.';
  this.link = 'http://www.astronomycast.com';

  this.immutableProp = function() {
    return _somePrivateVariable;
  }

  // object function
  this.toString = function() {
    return 'Title: ' + this.title;
  }
};

// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
  console.log('Downloading ' + podcast + ' ...');
};


// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
  get: function() {
    return this.getFullYear()
  },
  set: function(y) {
    this.setFullYear(y)
  }
});

// getters and setters - alternative syntax
var obj = {
  a: 7,
  get b() {
    return this.a + 1;
  },
  set c(x) {
    this.a = x / 2
  }
};

// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21

var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};
    
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
<div id="log"></div>

ノート:

  • JavaScriptプログラミング全般に関する良いヒント、アドバイス、推奨事項は、こちら(JavaScriptのベストプラクティス)やあちら('var'と'let'の比較)で見つけることができます。また、暗黙的な型変換(コーション)に関するこの記事もおすすめです。

  • クラスを使ってそれをJavaScriptにコンパイルする便利な方法はTypeScriptです。こちらはプレイグラウンドで、TypeScriptがどのように動作するかを示すいくつかの例を見ることができます。現在TypeScriptを使っていなくても、TypeScriptとJavaScriptの結果を並べて比較できるので見てみる価値があります。ほとんどの例は簡単ですが、すぐに試せるレイトレーサーの例もあります。特に、コンボボックスで「Using Classes」「Using Inheritance」「Using Generics」の例を選んで見ることをおすすめします。これらはJavaScriptですぐに使える便利なテンプレートです。TypeScriptはAngularと一緒に使われています。

  • JavaScriptでローカル変数や関数などのカプセル化を実現するために、次のようなパターンを使うことを提案します(JQueryも同じ手法を使用しています):

<html>
<head></head>
<body><script>
    'use strict';
    // module pattern (self invoked function)
    const myModule = (function(context) { 
    // to allow replacement of the function, use 'var' otherwise keep 'const'

      // put variables and function with local module scope here:
      var print = function(str) {
        if (str !== undefined) context.document.write(str);
        context.document.write("<br/><br/>");
        return;
      }
      // ... more variables ...

      // main method
      var _main = function(title) {

        if (title !== undefined) print(title);
        print("<b>last modified:&nbsp;</b>" + context.document.lastModified + "<br/>");        
        // ... more code ...
      }

      // public methods
      return {
        Main: _main
        // ... more public methods, properties ...
      };

    })(this);

    // use module
    myModule.Main("<b>Module demo</b>");
</script></body>
</html>

もちろん、スクリプトコードを別の *.js ファイルに置くことは可能ですし、置くべきです;これは例を短くするためにインラインで書かれています。

自己呼び出し関数(IIFE=Immediately Invoked Function Expression とも呼ばれる)は、こちらでより詳細に説明されています。

回答 #5

arguments.callee を使って「静的」変数を保存することができます(これは匿名関数でも役立ちます):

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}

回答 #6

更新された回答:

ECMAScript 6では、staticキーワードを使用して静的関数を作成できます:

class Foo {

  static bar() {return 'I am static.'}

}

//`bar` is a property of the class
Foo.bar() // returns 'I am static.'

//`bar` is not a property of instances of the class
var foo = new Foo()
foo.bar() //-> throws TypeError

ES6のクラスは、staticに関して新しい意味論を導入するわけではありません。同じことはES5でも次のように行うことができます:

//constructor
var Foo = function() {}

Foo.bar = function() {
    return 'I am static.'
}

Foo.bar() // returns 'I am static.'

var foo = new Foo()
foo.bar() // throws TypeError

JavaScriptでは関数もオブジェクトであるため、Fooのプロパティに代入することができます。

回答 #7

function Person(){
  if(Person.count == undefined){
    Person.count = 1;
  }
  else{
    Person.count ++;
  }
  console.log(Person.count);
}

var p1 = new Person();
var p2 = new Person();
var p3 = new Person();

回答 #8

以下の例と説明は、ニコラス・ザカスの『Professional JavaScript for Web Developers 第2版』からのものです。これは私が探していた答えだったので、ここに追加すると役立つと思いました。

(function () {
    var name = '';
    Person = function (value) {
        name = value;
    };
    Person.prototype.getName = function () {
        return name;
    };
    Person.prototype.setName = function (value) {
        name = value;
    };
}());
var person1 = new Person('Nate');
console.log(person1.getName()); // Nate
person1.setName('James');
console.log(person1.getName()); // James
person1.name = 'Mark';
console.log(person1.name); // Mark
console.log(person1.getName()); // James
var person2 = new Person('Danielle');
console.log(person1.getName()); // Danielle
console.log(person2.getName()); // Danielle

この例の Person コンストラクタは、getName() および setName() メソッドと同様に、プライベート変数 name にアクセスできます。このパターンを使用すると、name 変数は静的になり、すべてのインスタンスで共有されます。つまり、あるインスタンスで setName() を呼び出すと、他のすべてのインスタンスにも影響します。setName() を呼び出すか、新しい Person インスタンスを作成すると、name 変数は新しい値に設定されます。これにより、すべてのインスタンスが同じ値を返すようになります。

回答 #9

もし新しいクラス構文を使用している場合、次のことを行うことができます:

    class MyClass {
      static get myStaticVariable() {
        return "some static variable";
      }
    }

    console.log(MyClass.myStaticVariable);

    aMyClass = new MyClass();
    console.log(aMyClass.myStaticVariable, "is undefined");

これは実質的にJavaScriptで静的変数を作成します。

回答 #10

JavaScriptで関数ローカルの静的変数をエミュレートする方法は5つあります。

方法1:関数オブジェクトのプロパティを使用する(古いブラウザでサポート)

function someFunc1(){
    if( !('staticVar' in someFunc1) )
        someFunc1.staticVar = 0 ;
    alert(++someFunc1.staticVar) ;
}

someFunc1() ; //prints 1
someFunc1() ; //prints 2
someFunc1() ; //prints 3

方法2: クロージャを使用する、バリアント1(古いブラウザでサポートされています)

var someFunc2 = (function(){
    var staticVar = 0 ;
    return function(){
        alert(++staticVar) ;
    }
})()

someFunc2() ; //prints 1
someFunc2() ; //prints 2
someFunc2() ; //prints 3

方法3:クロージャを使う、バリアント2(古いブラウザでもサポートされています)

var someFunc3 ;
with({staticVar:0})
    var someFunc3 = function(){
        alert(++staticVar) ;
    }

someFunc3() ; //prints 1
someFunc3() ; //prints 2
someFunc3() ; //prints 3

方法4:クロージャを使用、バリアント3(EcmaScript 2015のサポートが必要)

{
    let staticVar = 0 ;
    function someFunc4(){
        alert(++staticVar) ;
    }
}

someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3

厳格モードのための方法5

'use strict'
{
    let staticVar = 0 ;
    var someFunc5 = function(){
        alert(++staticVar) ;
    } ;
}

someFunc5() ; //prints 1
someFunc5() ; //prints 2
someFunc5() ; //prints 3

関連記事