構造関数とnewコマンド
25693 ワード
JavaScript言語はオブジェクト指向プログラミング能力が強いので、本章ではJavaScriptがどのようにオブジェクト指向プログラミングを行うかを紹介します.
相手は何ですか
オブジェクト指向プログラミング(Object Oriented Prograamming)は、現在主流のプログラミングモデルです.それは真実な世界の各種の複雑な関係を抽象的に一つの対象として、そして対象の間の分業と協力によって、真実な世界に対するシミュレーションを完成します.
すべての対象は機能センターであり、明確な分業があり、情報の受け取り、データの処理、情報の発信などの任務を遂行することができます.したがって、オブジェクト指向プログラミングは、柔軟、コード多重、高度モジュール化などの特徴があり、維持と開発が容易であり、一連の関数やコマンドからなる伝統的なプロセスプログラミングよりも、複数の人が協力する大型ソフトウェアプロジェクトに適している.
では、「対象」はいったい何ですか?
私たちは二つのレベルから理解します.
(1)対象は単体の抽象的なものである.
本、自動車、一人が対象となります.データベース、ウェブページ、リモートサーバとの接続も対象となります.実物が抽象的に対象になると、実物の関係は対象との関係になり、現実の状況をシミュレーションし、対象に対してプログラミングすることができます.
(2)オブジェクトは、属性と方法をカプセル化した容器である.
属性は対象の状態です.方法は対象の行為です.例えば、私たちは動物を
構造関数
オブジェクト指向プログラミングの第一歩は、オブジェクトを生成することです.
前に述べましたが、対象は単体の抽象です.通常はテンプレートが必要で、ある種の実物の共通の特徴を表し、対象はこのテンプレートに基づいて生成される.
典型的なオブジェクト指向プログラミング言語(例えばC++とJava)には、クラスという概念があります.クラスとは、オブジェクトのテンプレートのことで、オブジェクトはクラスの例です.しかし、JavaScript言語の対象システムは、「クラス」に基づくものではなく、構造関数とプロトタイプチェーンに基づくものです.
JavaScript言語は、オブジェクトのテンプレートとして構造関数を使用します.「コンストラクタ」とは、オブジェクトを生成するための関数です.オブジェクトの基本構造を記述するテンプレートを提供します.一つのコンストラクタは、複数のオブジェクトを生成できます.これらのオブジェクトは同じ構造を持っています.
構造関数の書き方は普通の関数ですが、自分の特徴と使い方があります.
構造関数の特徴は二つあります.
関数内部に
基本的な使い方
この場合,コンストラクタは普通の関数になり,インスタンスオブジェクトは生成されない.また、後述する原因により、
したがって、
もう一つの解決策は、
new命令の原理
返す対象として空のオブジェクトを作成します.この空のオブジェクトのプロトタイプを、構造関数の
この空のオブジェクトを関数内部の
コンストラクタ内部のコードの実行を開始します.
すなわち、構造関数の内部で、
コンストラクタ内部に
しかし、
一方、一般関数(内部に
関数内部では
Object.creat()を使ってインスタンスオブジェクトを作成します.
テンプレートとしてのコンストラクタは、インスタンスオブジェクトを生成することができます.しかし、場合によっては、インスタンスオブジェクトしか入手できないが、オブジェクトは基本的に構造関数によって生成されていないので、
相手は何ですか
オブジェクト指向プログラミング(Object Oriented Prograamming)は、現在主流のプログラミングモデルです.それは真実な世界の各種の複雑な関係を抽象的に一つの対象として、そして対象の間の分業と協力によって、真実な世界に対するシミュレーションを完成します.
すべての対象は機能センターであり、明確な分業があり、情報の受け取り、データの処理、情報の発信などの任務を遂行することができます.したがって、オブジェクト指向プログラミングは、柔軟、コード多重、高度モジュール化などの特徴があり、維持と開発が容易であり、一連の関数やコマンドからなる伝統的なプロセスプログラミングよりも、複数の人が協力する大型ソフトウェアプロジェクトに適している.
では、「対象」はいったい何ですか?
私たちは二つのレベルから理解します.
(1)対象は単体の抽象的なものである.
本、自動車、一人が対象となります.データベース、ウェブページ、リモートサーバとの接続も対象となります.実物が抽象的に対象になると、実物の関係は対象との関係になり、現実の状況をシミュレーションし、対象に対してプログラミングすることができます.
(2)オブジェクトは、属性と方法をカプセル化した容器である.
属性は対象の状態です.方法は対象の行為です.例えば、私たちは動物を
animal
の対象として抽象してもいいです.「属性」を使って具体的にはその動物です.「方法」を使って動物のある行為(走る、狩り、休憩など)を表します.構造関数
オブジェクト指向プログラミングの第一歩は、オブジェクトを生成することです.
前に述べましたが、対象は単体の抽象です.通常はテンプレートが必要で、ある種の実物の共通の特徴を表し、対象はこのテンプレートに基づいて生成される.
典型的なオブジェクト指向プログラミング言語(例えばC++とJava)には、クラスという概念があります.クラスとは、オブジェクトのテンプレートのことで、オブジェクトはクラスの例です.しかし、JavaScript言語の対象システムは、「クラス」に基づくものではなく、構造関数とプロトタイプチェーンに基づくものです.
JavaScript言語は、オブジェクトのテンプレートとして構造関数を使用します.「コンストラクタ」とは、オブジェクトを生成するための関数です.オブジェクトの基本構造を記述するテンプレートを提供します.一つのコンストラクタは、複数のオブジェクトを生成できます.これらのオブジェクトは同じ構造を持っています.
構造関数の書き方は普通の関数ですが、自分の特徴と使い方があります.
var Vehicle = function () {
this.price = 1000;
};
上記のコードでは、Vehicle
は構成関数であり、テンプレートを提供してインスタンスオブジェクトを生成する.普通の関数と区別するために、コンストラクションの名前の最初の文字は通常大文字です.構造関数の特徴は二つあります.
関数内部に
this
のキーワードが使用され、生成されるオブジェクトの例を表しています.オブジェクトを生成するには、new
コマンドを使用して、Vehicle
関数を呼び出す必要があります.newコマンド基本的な使い方
new
コマンドの役割は、構造関数を実行し、インスタンスオブジェクトを返すことである.var Vehicle = function () {
this.price = 1000;
};
var v = new Vehicle();
v.price // 1000
上記のコードは、new
コマンドにより、構成関数Vehicle
にインスタンスオブジェクトを生成させ、変数v
に保存させる.この新たに生成された例示的なオブジェクトは、構造関数Vehicle
からprice
の属性を継承する.new
コマンドが実行されると、構造関数内部のthis
は、新たに生成されたインスタンスオブジェクトを表し、this.price
は、インスタンスオブジェクトにprice
属性があり、値は1000であることを示している.new
コマンドを使用する場合、必要に応じて、コンストラクタはパラメータを受け取ることができます.var Vehicle = function (p) {
this.price = p;
};
var v = new Vehicle(500);
new
コマンド自体はコンストラクタを実行することができますので、後のコンストラクタは括弧を持ってもいいし、括弧を付けなくてもいいです.次の2行のコードは等価です.var v = new Vehicle();
var v = new Vehicle;
自然な問題ですが、new
コマンドを使用することを忘れたら、直接コンストラクタを呼び出すとどうなりますか?この場合,コンストラクタは普通の関数になり,インスタンスオブジェクトは生成されない.また、後述する原因により、
this
は、グローバルオブジェクトを代表して、いくつかの予期せぬ結果をもたらす.var Vehicle = function (){
this.price = 1000;
};
var v = Vehicle();
v.price
// Uncaught TypeError: Cannot read property 'price' of undefined
price
// 1000
上のコードでVehicle
コンストラクタを呼び出すとnew
コマンドを追加するのを忘れました.その結果、price
属性はグローバル変数になり、v
はundefined
になる.したがって、
new
コマンドを使用しないで、直接的に構造関数を呼び出すことを避けるために、非常に注意すべきである.構造関数がnew
コマンドと一緒に使用される必要があることを保証するために、1つの解決法は、構造関数の内部で厳格なモード、すなわち第1行にuse strict
を加えることである.function Fubar(foo, bar){
'use strict';
this._foo = foo;
this._bar = bar;
}
Fubar()
// TypeError: Cannot set property '_foo' of undefined
上のコードのFubar
は構成関数であり、use strict
コマンドはこの関数が厳密なモードで動作することを保証する.厳密なモードでは、関数内部のthis
は大域オブジェクトに向けられず、デフォルトはundefined
に等しいため、new
を追加しないとエラーメッセージを呼び出すことになる(JavaScriptはundefined
に属性を追加することができない).もう一つの解決策は、
new
コマンドが使用されているかどうかをコンストラクタ内部で判断し、使用されていないことが発見された場合、直接にインスタンスオブジェクトに戻ることである.function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}
this._foo = foo;
this._bar = bar;
}
Fubar(1, 2)._foo // 1
(new Fubar(1, 2))._foo // 1
上のコードの構造関数は、new
コマンドを加えなくても同じ結果が得られます.new命令の原理
new
コマンドを使用すると、その後の関数呼び出しは正常な呼び出しではなく、次のステップを順次実行します.返す対象として空のオブジェクトを作成します.この空のオブジェクトのプロトタイプを、構造関数の
prototype
属性に向ける.この空のオブジェクトを関数内部の
this
キーワードに割り当てます.コンストラクタ内部のコードの実行を開始します.
すなわち、構造関数の内部で、
this
は新たに生成された空のオブジェクトを指し、this
の動作のすべては、この空のオブジェクト上で発生する.コンストラクタを「コンストラクター」というのは、この関数の目的は、空のオブジェクト(すなわちthis
オブジェクト)を操作して、その「構造」を必要とする形です.コンストラクタ内部に
return
文があり、return
の後にオブジェクトが続く場合、new
コマンドはreturn
文で指定されたオブジェクトに戻ります.そうでなければ、return
文にかかわらず、this
オブジェクトに戻る.var Vehicle = function () {
this.price = 1000;
return 1000;
};
(new Vehicle()) === 1000
// false
上記のコードにおいて、構造関数Vehicle
のreturn
文は数値を返します.このとき、new
コマンドは、このreturn
文を無視して、「構造」後のthis
オブジェクトに戻る.しかし、
return
文がthis
とは無関係の新しいオブジェクトを返した場合、new
コマンドはthis
オブジェクトではなく、この新しいオブジェクトを返します.この点には特に注意が必要です.var Vehicle = function (){
this.price = 1000;
return { price: 2000 };
};
(new Vehicle()).price
// 2000
上記のコードにおいて、構造関数Vehicle
のreturn
文は、新しいオブジェクトを返します.new
コマンドは、this
オブジェクトではなく、このオブジェクトに戻ります.一方、一般関数(内部に
this
キーの関数がない)にnew
コマンドを使用すると、空のオブジェクトが返されます.function getMessage() {
return 'this is a message';
}
var msg = new getMessage();
msg // {}
typeof msg // "object"
上記のコードでは、getMessage
は普通関数であり、文字列を返します.new
コマンドを使用すると、空のオブジェクトが得られます.これは、new
コマンドが常にオブジェクトに戻るか、インスタンスオブジェクトか、またはreturn
文で指定されたオブジェクトになるからです.本例では、return
文は文字列を返しているので、new
コマンドはこの文を無視しています.new
コマンドの簡略化された内部フローは、以下のコードで表されてもよい.function _new(/* */ constructor, /* */ param1) {
// arguments
var args = [].slice.call(arguments);
//
var constructor = args.shift();
// , prototype
var context = Object.create(constructor.prototype);
//
var result = constructor.apply(context, args);
// , , context
return (typeof result === 'object' && result != null) ? result : context;
}
//
var actor = _new(Person, ' ', 28);
new.target関数内部では
new.target
属性が使用できます.現在の関数がnew
コマンド呼び出しの場合、new.target
は現在の関数を指し、そうでなければundefined
です.function f() {
console.log(new.target === f);
}
f() // false
new f() // true
この属性を使用して、関数が起動したときにnew
コマンドが使用されているかどうかを判断することができます.function f() {
if (!new.target) {
throw new Error(' new !');
}
// ...
}
f() // Uncaught Error: new !
上のコードでは、構造関数f
が呼び出したときにnew
コマンドを使用していないので、エラーを投げました.Object.creat()を使ってインスタンスオブジェクトを作成します.
テンプレートとしてのコンストラクタは、インスタンスオブジェクトを生成することができます.しかし、場合によっては、インスタンスオブジェクトしか入手できないが、オブジェクトは基本的に構造関数によって生成されていないので、
Object.create()
方法を使用して、あるインスタンスオブジェクトを直接テンプレートとして使用して、新しいインスタンスオブジェクトを生成することができる.var person1 = {
name: ' ',
age: 38,
greeting: function() {
console.log('Hi! I\'m ' + this.name + '.');
}
};
var person2 = Object.create(person1);
person2.name //
person2.greeting() // Hi! I'm .
上記のコードにおいて、オブジェクトperson1
はperson2
のテンプレートであり、後者は前者の属性および方法を継承する.