関数とコンストラクターについて魔法のようなものは何もありません。JavaScript のすべてのオブジェクトは…そうですね、オブジェクトです。ただし、一部のオブジェクトは他のオブジェクトよりも特別です。つまり、組み込みオブジェクトです。違いは主に次の点にあります。
- オブジェクトの一般的な扱い。例:
- 数値と文字列は不変です (⇒ 定数)。それらを内部的に変更するメソッドは定義されていません — 新しいオブジェクトは常に結果として生成されます。それらにはいくつかの固有のメソッドがありますが、それらを変更したり、新しいメソッドを追加したりすることはできません。そうしようとしても無視されます。
null
とundefined
特別なオブジェクトです。これらのオブジェクトでメソッドを使用しようとしたり、新しいメソッドを定義しようとすると、例外が発生します。
- 該当する事業者。JavaScript は演算子を (再) 定義することを許可していないため、利用可能なものに固執しました。
- 数値には、算術演算子を使用した特別な方法があります:
+
, -
, *
, /
.
- 文字列には、連結演算子を処理する特別な方法があります:
+
.
- 関数には、「呼び出し」演算子を処理する特別な方法があります:
()
、およびnew
演算子。後者は、コンストラクターのプロパティを使用する方法、プロトタイプへの適切な内部リンクを使用してオブジェクトを構築する方法、および正しくprototype
セットアップされたコンストラクター関数を呼び出す方法についての生来の知識を持っています。this
ECMAScript 標準 ( PDF ) を見ると、これらの「追加」機能はすべてメソッドとプロパティとして定義されていることがわかりますが、それらの多くはプログラマーが直接利用することはできません。それらのいくつかは、標準 ES3.1 の新しいリビジョンで公開されます (2008 年 12 月 15 日時点のドラフト: PDF )。1 つのプロパティ ( __proto__
) がFirefox で既に公開されています。
これで、あなたの質問に直接答えることができます。はい、関数オブジェクトにはプロパティがあり、自由に追加/削除できます。
var fun = function(){/* ... */};
fun.foo = 2;
console.log(fun.foo); // 2
fun.bar = "Ha!";
console.log(fun.bar); // Ha!
関数が実際に何をするかは問題ではありません — 私たちが関数を呼び出さないので、関数が機能することは決してありません! 定義してみましょう:
fun = function(){ this.life = 42; };
それ自体はコンストラクターではなく、そのコンテキストで動作する関数です。そして、それを簡単に提供できます:
var context = {ford: "perfect"};
// now let's call our function on our context
fun.call(context);
// it didn't create new object, it modified the context:
console.log(context.ford); // perfect
console.log(context.life); // 42
console.log(context instanceof fun); // false
ご覧のとおり、既存のオブジェクトにもう 1 つのプロパティが追加されています。
関数をコンストラクターとして使用するには、new
演算子を使用する必要があります。
var baz = new fun();
// new empty object was created, and fun() was executed on it:
console.log(baz.life); // 42
console.log(baz instanceof fun); // true
ご覧のとおりnew
、関数をコンストラクターにしました。次の操作は によって行われましたnew
:
- 新しい空のオブジェクト (
{}
) が作成されました。
- その内部プロトタイプ プロパティは に設定されていました
fun.prototype
。{}
この場合、何も変更していないため、空のオブジェクト ( ) になります。
fun()
この新しいオブジェクトをコンテキストとして呼び出されました。
新しいオブジェクトを変更するのは関数次第です。通常、オブジェクトのプロパティを設定しますが、何でも好きなことを行うことができます。
楽しいトリビア:
コンストラクターは単なるオブジェクトなので、計算できます。
var A = function(val){ this.a = val; };
var B = function(val){ this.b = val; };
var C = function(flag){ return flag ? A : B; };
// now let's create an object:
var x = new (C(true))(42);
// what kind of object is that?
console.log(x instanceof C); // false
console.log(x instanceof B); // false
console.log(x instanceof A); // true
// it is of A
// let's inspect it
console.log(x.a); // 42
console.log(x.b); // undefined
// now let's create another object:
var y = new (C(false))(33);
// what kind of object is that?
console.log(y instanceof C); // false
console.log(y instanceof B); // true
console.log(y instanceof A); // false
// it is of B
// let's inspect it
console.log(y.a); // undefined
console.log(y.b); // 33
// cool, heh?
コンストラクターは、新しく作成されたオブジェクトをオーバーライドする値を返すことができます。
var A = function(flag){
if(flag){
// let's return something completely different
return {ford: "perfect"};
}
// let's modify the object
this.life = 42;
};
// now let's create two objects:
var x = new A(false);
var y = new A(true);
// let's inspect x
console.log(x instanceof A); // true
console.log(x.ford); // undefined
console.log(x.life); // 42
// let's inspect y
console.log(y instanceof A); // false
console.log(y.ford); // perfect
console.log(y.life); // undefined
ご覧x
のとおり、 はA
プロトタイプとすべてのものであり、一方y
はコンストラクターから返された「裸の」オブジェクトです。