主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

JavaScript: JavaScriptにクラスはあるのか

                
 JavaScriptにはクラスがない。だけど多くの人が便宜を図ってクラスと呼びながら使っているものがある。割と似ているのでクラスと呼んでなにか悪いのかという声もある。JavaScriptの特徴を捉えておきたかったので考えてみる。


たぶん誤解:クラスベースは静的、プロトタイプベースは動的
 少し前の時期に書かれたものを見ると、継承元となるJavaのクラスを検討して、クラスは静的だから動的変更ができず、JavaScriptのはインスタンスで動的変更が自在と説明されていたりする。Pythonはクラスベースだがクラスの動的変更ができるし、そのインスタンスも動的変更ができる。そうするとクラスが静的だというのは静的なJavaによって提供されている特徴で、クラス自体の特徴とはできないだろう。
from __future__ import print_function

class Foo:
def print(self):
print("default")

foo = Foo()
foo.print() # "default"

Foo.print = lambda self: print("reloaded")
foo.print() # "reloaded"



 では動作としてクラスとプロトタイプで異なるところはないのだろうか。あるある。

 インスタンスが自己を更新するようなメソッドを実行しようとすると、クラスに置いてあるメソッドを参照する。このメソッドをクラス自身が実行できるかといえばできない。
class Foo:
x=0

def update(self):
self.x += 1

foo = Foo()
foo.update()

#Foo.update() # fails


 JavaScriptでインスタンスのメソッドを実行しようとすると、プロトタイプに置いたインスタンスのメソッド(関数)を参照する。メソッドを持っているのもまたインスタンスなので、クラスのように制限がかからずメソッドを実行できる。
var foo0 = {
x:0,
update:function(){this.x += 1}
};

//var foo = Object.create(foo0);

function Foo()
{
}

Foo.prototype = foo0;

var foo = new Foo();
foo.update();

foo0.update();

 JavaScriptでもっとも簡単にインスタンスを作ろうとするとクラス(みたいなもの)はいらない。”{}”を使うことでそのままインスタンス自体を書ける。そしてこのインスタンスのプロパティ(メソッドと呼ばれるものを含む)をプロトタイプとして使いまわすのが、JavaScriptでのオブジェクト作成のさらなる方法として実装されている。クラスなしに、でもクラスみたいに合理的にオブジェクトを使う方法が整っているということ。


 プロトタイプはそりゃクラスと別物だ。でも下記のようなコンストラクタをクラスと説明してる記事が他所にある。
function Foo()
{
this.f = function()
{
return "default";
};
}

var foo = new Foo();

 上記をインスタンスを作った先まで少し進めてみる。
console.log(foo.f()); // "default"

Foo.f = function()
{
return "reloaded";
};
console.log(foo.f()); // "default"

 途中でコンストラクタの中で定義されているメソッド(もどき)fを書き換えているが、その影響はインスタンスに現れず、インスタンスからのfの実行は最初に定義されたとおりに実行される。これもクラスと違う。この記事の最初のPythonコードでは、途中でクラスのメソッドを書き換えたところ、インスタンスにそれが反映された。
 クラスの話。あるインスタンスのメソッドは、その設計図となったクラスが持っている。インスタンスからメソッドが実行されると、元のクラスを参照してメソッドを呼び出す。そうすることでインスタンスごとに同じ処理をするメソッドを作ってメモリを浪費するなんてことを防いでいる。インスタンスが作られたあとでも、そのインスタンスの振る舞いを実は管理している、それがクラス。
 真上に書いたJavaScriptコードは、コンストラクタFooでインスタンスを作るたびに、それに付随するメソッドfも新たに作られる。クラスの持つメモリ浪費抑制の機構を持たない。それをJavaScriptで持たせたければ、コンストラクタへの理解をprototypeプロパティまで掘り下げてからメソッドを書くようにしなければならない。それをせずにコンストラクタ内でメソッドを定義してしまうこの書き方を他言語と同じ意味でのクラスとして扱うのはちょっと危険。

 プロトタイプベースとクラスベースでインスタンスの作り方を比べてみた。プロトタイプベースはインスタンスのパーツを共有することでまた同じ働きをするインスタンスを作れるようになっていた。作られるインスタンスの参考にされているのもまたインスタンスだった。クラスベースではインスタンスの参考にされるのはクラスで、それはインスタンスと同様に振る舞えるものではなかった。
 JavaScriptでプロトタイプを使わないコンストラクタをクラスと呼んでいるものも他所にあったので、それとPythonのクラスを比べた。インスタンスのメソッドをどうするかが違った。JavaScriptのプロトタイプを使わないコンストラクタはインスタンスごとにメソッドを作った。Pythonのクラスでは、インスタンスのメソッドはクラスが参照されるようになっていた。これは実際のところPythonに限ったことではない。
 JavaScriptで一部にクラスと呼ばれているものはパッと見たところ他言語のクラスによく似ている。だたじっくり見てみるとなにかしら差異が出てくる。そのあたりは差異として踏まえておきたい。

There's a lady who's sure all that glitters is gold
- Led Zeppelin: Stairway to Heaven


参考:http://blog.livedoor.jp/dankogai/archives/50689356.html
            

コメントの投稿

非公開コメント

No title

プロトタイプベースにおいて、クラス(システム)とは、必要に応じて作るものなのです。
「予め用意されたクラスシステムに縛られない」というのがプロトタイプベースの醍醐味なのです。
ですから、プロトタイプベース言語でも、クラスはもちろん言語構造上元からは無いですが、表現できます。

また、JSにおいてはちょっとややこしい問題が生じます。
何故ならJSはプロトタイプベースの性質をかなり隠蔽して、「クラスシステムを好きに作る」と言うことを極めて難しくし、コンストラクタやnew演算子など、言語が簡素な「クラスシステム」を提供しているからです。
ES3までのJSにおいては、この用意された微妙なクラスシステムを使うしかありませんでした。

本当に純粋なプロトタイプベース言語なら、これらは別に必要ないはずなのです。
つまり、JSはES3まではクラスベースでは無いものの、プロトタイプベースとしても中途半端な言語でした。

しかし、ES6で考えるとまた話は変わります。
ES6ではプロトタイプベース的プログラミングが十分に行えるようになり、また、class構文による上等な「クラスシステム」が提供されます。
ここまでくれば、クラスはある(縛られるのではなく使える)と言ってもいいでしょう。

No title

あとコンストラクタとクラスを比較するのは変だと思います。
一般的に、コンストラクタ+prototype定義がクラスに似てると言われているので。

Re: No title

私はクラスをベースにしたインスタンスとプロトタイプをベースにしたインスタンスの振る舞いに着目して、どういう差異があるかを示しました。便宜のためにJavaScriptにクラスがあるとするのもいいと思いますが、他言語のクラスとの間に実際にある差異を捉えておくのも意義のあることだと思います。

No title

後半のコンストラクタのみとクラスを比較している部分は、クラスベースとインスタンスベースの比較になっていません。
JSでのコンストラクタは、new演算子でのみ呼びだされるものであって、そのとき同時にfunc.prototypeがインスタンスのプロトタイプに設定することで機能しています。

したがって.prototypeの利用のない状況だと、this.f = function()~はreturn {f:function()~}などのように、関数で連想配列を作って返しているのと全く変わらず、プロトタイプベースの性質は全く現れていません。

コンストラクタと、プロトタイプ定義の両方があって、始めてクラスと比較できるのです。
というか、コンストラクタはインスタンスにプロトタイプを設定して、初期化するだけであって、実際に継承され、プロパティアクセスで参照されるのはプロトタイプオブジェクトの方なのですから、
どうせ比較するのならプロトタイプと「クラス」にすべきです。コンストラクタに惑わされてはいけません。コンストラクタはnew演算が特別なはからいをしてくれることでコンストラクタになれるわけで、そうでない時はただの関数です。

そのことに気をつけ、中途半端なものに頼らず、素直にObject.createを使えば勘違いすることはありません。

var Foo = {
f: function () {
return "default"
}
}

var foo = Object.create(Foo)

console.log(foo.f()); // "default"

Foo.f = function() {
return "reloaded"
}

console.log(foo.f()); // "reloaded"

Re: No title

 私の前のコメントが間違えています。本記事で私が書いているのは、巷でJavaScriptのクラスと呼ばれているものを持ってきて、それを他言語のクラスと比較することです。その中で前半がクラスベースとプロトタイプベースの比較です。
 後半はプロトタイプを意図的に排しています。プロトタイプを使わずにコンストラクタ内でメソッドもどきを定義するのも、JavaScriptでのクラス定義の一つの方法だと紹介している説明記事がいくらかあります。そんなやり方をすると他言語のクラスのようにスマートな振る舞いをしてくれないことに触れておきたかったので書きました。JavaScriptのプロトタイプを併用しないコンストラクタと他言語のクラスを比べるのはまっとうな比較ではないと思いますが、プロトタイプを併用しないコンストラクタをクラスと言い換えているところを見かけたので、それと他言語のクラスとの差異を炙り出したくての比較です。端的に言えばそちらの言うとおりプロトタイプへの参照を持たないというのが結論です。
 前半はちょっとまずい点があるので結論を含めて書きかえるかも知れませんが、後半の比較はプロトタイプを使わないコンストラクタをクラスと呼ぶ記事がWeb上にあるなら置いといてもいいんじゃないかと考えています。

No title

後半の例をクラスと呼ぶべきではないと言いたいのではありません。
コンストラクタのみの、何も継承されないクラスだと見立てるはおかしなことではありません。
しかし、それを、他言語のクラスの性質を十分に出しているものと比較するのが変だと言いたいのです。
他言語と比較するのなら、コンストラクタだけで辞書を返すようなものと比較するのが真っ当です。
それか、クラスの性質が出ないと言いたいのならば、JSの他の記述と比較すべきです。

この書き方だと、「JSの良く機能している真っ当なクラスの書き方はこうなんだ」、とか、「JSのクラスには罠がある」のように受け止められます。

Re: No title

 後半の方法でメソッドもどきを実装しているコンストラクタをクラスと呼んでいる例が他所にあったので、それを真似してクラスのように使うとどう違うかを書きました。クラスと呼ばれていたので、素直にクラスと呼べるものとして他言語で、メソッドを一つ持つクラスと比較しました。
 すでに述べているように、まっとうな比較でないのは承知しています。この結果一つでJavaScriptにクラスがないというつもりはありません。しかしこの書き方をしたコンストラクタ自体をクラスと呼ぶような他所の例に対しては、他言語のクラスと違うところがあるということは言えると思います。

 後半の書き方をJavaScriptでのクラスのまっとうな書き方と受け止められるのはなんだかなと思います。クラスの有無はさておき、メモリ浪費をするやり方だと説明しているので。
プロフィール

hMatoba

Author:hMatoba
Github

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。