5

私は最近これについて多くの研究を行っていますが、本当に良い確かな答えはまだ得られていません。JavaScriptエンジンが関数ステートメントに出くわしたときに新しいFunction()オブジェクトが作成されることをどこかで読みました。これにより、オブジェクトの子である可能性があると思います(したがって、1つになります)。それで私はダグラス・クロックフォードに電子メールを送りました、そして彼の答えは次のとおりでした:

関数ステートメントはコンパイラーを呼び出さないため、正確ではありません。

しかし、同様の結果が得られます。

また、私の知る限り、新しいオブジェクトとしてインスタンス化されていない限り、関数コンストラクターでメンバーを呼び出すことはできません。したがって、これは機能しません。

function myFunction(){
    this.myProperty = "Am I an object!";
}
myFunction.myProperty; // myFunction is not a function
myFunction().myProperty; // myFunction has no properties

ただし、これは機能します。

function myFunction(){
    this.myProperty = "Am I an object!";
}
var myFunctionVar = new myFunction();
myFunctionVar.myProperty;

これは単なるセマンティクスの問題ですか...プログラミングの世界全体で、オブジェクトが実際にオブジェクトになるのはいつですか、それはどのようにJavaScriptにマッピングされますか?

4

8 に答える 8

13

関数とコンストラクターについて魔法のようなものは何もありません。JavaScript のすべてのオブジェクトは…そうですね、オブジェクトです。ただし、一部のオブジェクトは他のオブジェクトよりも特別です。つまり、組み込みオブジェクトです。違いは主に次の点にあります。

  1. オブジェクトの一般的な扱い。例:
    • 数値と文字列は不変です (⇒ 定数)。それらを内部的に変更するメソッドは定義されていません — 新しいオブジェクトは常に結果として生成されます。それらにはいくつかの固有のメソッドがありますが、それらを変更したり、新しいメソッドを追加したりすることはできません。そうしようとしても無視されます。
    • nullundefined特別なオブジェクトです。これらのオブジェクトでメソッドを使用しようとしたり、新しいメソッドを定義しようとすると、例外が発生します。
  2. 該当する事業者。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:

  1. 新しい空のオブジェクト ( {}) が作成されました。
  2. その内部プロトタイプ プロパティは に設定されていましたfun.prototype{}この場合、何も変更していないため、空のオブジェクト ( ) になります。
  3. 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はコンストラクターから返された「裸の」オブジェクトです。

于 2008-12-16T21:03:55.237 に答える
5

特定の質問に答えるために、技術的には関数は常にオブジェクトです。

たとえば、いつでもこれを行うことができます。

function foo(){
  return 0;
}
foo.bar = 1;
alert(foo.bar); // shows "1"

thisJavascript 関数は、ポインタを使用する場合、他の OOP 言語のクラスのように動作します。new キーワードを使用して、オブジェクトとしてインスタンス化できます。

function Foo(){
  this.bar = 1;
}
var foo = new Foo();
alert(foo.bar); // shows "1"

現在、他の OOP 言語から Javascript へのこのマッピングはすぐに失敗します。たとえば、Javascript には実際にはクラスのようなものはありません。オブジェクトは、代わりに継承のためにプロトタイプ チェーンを使用します。

Javascript で何らかの重要なプログラミングを行う場合は、あなたが電子メールを送った Crockfordの Javascript: The Good Partsを強くお勧めします。

于 2008-12-16T18:41:14.573 に答える
4

確かに、関数は「第一級市民」です:それらはオブジェクトです。

すべてのオブジェクトにはプロトタイプがありますが、直接参照できるのは関数のプロトタイプのみです。関数オブジェクトを引数としてがnew呼び出されると、関数オブジェクトのプロトタイプをプロトタイプとして使用して新しいオブジェクトが作成thisされ、関数が入力される前に新しいオブジェクトに設定されます。

したがって、そのままにしておいても、すべての関数をコンストラクターと呼ぶことができthisます。

コンストラクターやプロトタイプなどに関する非常に優れたチュートリアルがあります...個人的には、JavaScriptのオブジェクト指向プログラミングから多くのことを学びました。これは、プロトタイプを「継承」するがthis、新しいオブジェクトのプロパティを入力するために使用する関数と、特定のプロトタイプを使用する関数オブジェクトの同等性を示しています。

function newA() { this.prop1 = "one"; } // constructs a function object called newA
function newA_Too() {} // constructs a function object called newA_Too
newA_Too.prototype.prop1 = "one";

var A1 = new newA();
var A2 = new newA_Too();
// here A1 equals A2.
于 2008-12-16T19:35:38.130 に答える
4

Javascript の「グローバル」スコープ (少なくともブラウザーでは) はwindowオブジェクトです。

これはthis.myProperty = "foo"、関数をプレーンとして呼び出して呼び出すと、myFunction()実際に設定していることを意味しますwindow.myProperty = "foo"

の 2 番目のポイントmyFunction().myPropertyは、ここでは の戻り値を見ているということです。myFunction()したがって、null を返すため、当然プロパティはありません。

あなたが考えているのはこれです:

function myFunction()
{
    myFunction.myProperty = "foo";
}

myFunction();
alert(myFunction.myProperty); // Alerts foo as expected

これは(ほぼ)同じです

var myFunction = new Function('myFunction.myProperty = "foo";');
myFunction();

コンテキストで使用するとnew、「戻り値」が新しいオブジェクトになり、「this」ポインターが新しいオブジェクトに変わるため、期待どおりに機能します。

于 2008-12-16T18:37:46.437 に答える
1

まず、JavaScript はオブジェクトに関して C++/Java と同じように動作しないため、JavaScript がどのように機能するかを理解できるようにするには、この種のアイデアを窓の外に放り出す必要があります。

この行が実行されると:

var myFunctionVar = new myFunction();

this内部は、myFunction()作成しているこの新しいオブジェクトを参照します - myFunctionVar。したがって、このコード行:

 this.myProperty = "Am I an object!";

本質的に次の結果があります

 myFunctionVar.myProperty = "Am I an object!";

newオペレーターに関するドキュメントを参照すると役立つ場合があります。JS では、new基本的に、演算子を使用して関数からオブジェクトを作成できます。これは、単純な古い関数です。newC++ や Java の場合のように、コンストラクターとしてマークする演算子と共に使用する関数について特別なことは何もありません。ドキュメントが言うように:

ユーザー定義のオブジェクト タイプを作成するには、次の 2 つの手順が必要です。

  1. 関数を記述してオブジェクト タイプを定義します。
  2. new でオブジェクトのインスタンスを作成します。

それで、あなたはコードで何をしましたか

function myFunction(){
    this.myProperty = "Am I an object!";
}

コンストラクターとして役立つ関数を作成することです。myFunction.myPropertyコードが失敗する理由は、という名前の参照myFunctionがないためです。

于 2008-12-16T18:41:45.150 に答える
0

new キーワードでインスタンス化した場合にのみ、関数はコンストラクターとして機能します。

結果は、"this" キーワードを使用してメンバー プロパティにアクセスできるオブジェクトです。メソッド内の this キーワードは、関数が他の方法で使用されている場合は意味がありません。

于 2008-12-16T19:00:35.003 に答える
0

JavaScript は ECMA スクリプトに基づいています。その仕様は、OOP であるためにプロトタイピング モデルを使用します。ただし、ECMA スクリプトは厳密なデータ型を強制しません。ECMA スクリプトがプロパティにメモリを割り当てる「新しい」呼び出しを必要とするのと同じ理由で、オブジェクトをインスタンス化する必要があります。それ以外の場合は関数のままで、必要に応じて呼び出すことができます。その場合、プロパティは初期化されます。関数が終了すると破棄されます。

于 2008-12-16T18:40:09.800 に答える