今日、括弧なしで関数を呼び出すことができると聞きました。私が思いついた唯一の方法は、次のような関数を使うことでした。apply
またはcall
。
f.apply(this);
f.call(this);
しかし、これらには括弧が必要でapply
、call
最初からやり直すことになります。また、次のようなイベントハンドラに関数を渡すというアイデアも検討しました。setTimeout
:
setTimeout(f, 500);
しかし、ここで疑問になるのは、「setTimeout
括弧なしでどうやって呼び出すのか?」ということです。
では、この謎を解くにはどうすればよいでしょうか? 括弧を使わずに JavaScript で関数を呼び出すにはどうすればよいでしょうか?
ベストアンサー1
括弧なしで関数を呼び出す方法はいくつかあります。
次の関数が定義されていると仮定します。
function greet() {
console.log('hello');
}
次に、括弧なしで呼び出す方法をいくつか示しますgreet
。
1. コンストラクターとして
を使用すると、new
括弧なしで関数を呼び出すことができます。
new greet; // parentheses are optional in this construct.
構文
new constructor[([arguments])]
2.toString
またはvalueOf
実装
toString
そしてvalueOf
特別なメソッドです。変換が必要なときに暗黙的に呼び出されます。
var obj = {
toString: function() {
return 'hello';
}
}
'' + obj; // concatenation forces cast to string and call to toString.
このパターンを(悪用して)括弧なしで呼び出すこともできますgreet
:
'' + { toString: greet };
または、以下を使用しますvalueOf
:
+{ valueOf: greet };
valueOf
そして、toString
実際には@@プリミティブメソッド (ES6 以降) があり、そのメソッドも実装できます。
+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }
valueOf
2.b関数プロトタイプでのオーバーライド
前述のアイデアを利用してvalueOf
、Function
プロトタイプ:
Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
};
それが終わったら、次のように記述できます。
+greet;
行の後半には括弧がありますが、実際のトリガー呼び出しには括弧はありません。詳細についてはブログをご覧ください。「JavaScript でメソッドを呼び出すが、実際には呼び出さない」
3. ジェネレーターとして
定義できるのはジェネレータ関数(*
)は、反復子. を使用して呼び出すことができますスプレッド構文またはfor...of
構文。
まず、元の関数のジェネレーターのバリアントが必要ですgreet
。
function* greet_gen() {
console.log('hello');
}
そして括弧なしで定義して呼び出します@@イテレータ方法:
[...{ [Symbol.iterator]: greet_gen }];
通常、発電機にはyield
キーワードがどこかにありますが、関数が呼び出されるためには必要ありません。
最後の文は関数を呼び出しますが、これは次のようにも実行できます。構造化解除:
[,] = { [Symbol.iterator]: greet_gen };
またはfor ... of
構造ですが、独自の括弧があります。
for ({} of { [Symbol.iterator]: greet_gen });
上記は元の関数でも実行できますが、実行後にgreet
プロセスで例外がトリガーされることに注意してください(FF と Chrome でテスト済み)。ブロックを使用して例外を管理できます。 greet
try...catch
4. ゲッターとして
@jehna1がこの件について完全な回答をしているので、彼に敬意を表しましょう。グローバルスコープで括弧なしで関数を呼び出す方法は次のとおりです。非推奨__defineGetter__
方法。Object.defineProperty
その代わり。
このためには、元の関数のバリエーションを作成する必要がありますgreet
。
Object.defineProperty(globalThis, 'greet_get', { get: greet });
その後:
greet_get;
greet
次のようにすると、グローバル オブジェクトに痕跡を残さずに元の関数を呼び出すことができます。
Object.defineProperty({}, 'greet', { get: greet }).greet;
しかし、ここには括弧があると主張する人もいるかもしれません (実際の呼び出しには関係ありませんが)。
5. タグ機能として
ES6以降では関数に引数を渡して呼び出すことができます。テンプレートリテラル次の構文を使用します:
greet``;
6. プロキシハンドラーとして
ES6以降では、プロキシ:
var proxy = new Proxy({}, { get: greet } );
そして、任意のプロパティ値を読み取ると、次が呼び出されますgreet
。
proxy._; // even if property not defined, it still triggers greet
これには多くのバリエーションがあります。もう 1 つの例:
var proxy = new Proxy({}, { has: greet } );
1 in proxy; // triggers greet
7. インスタンスチェッカーとして
演算子は、定義されている場合、2 番目のオペランドに対してメソッドinstanceof
を実行します。@@hasInstance
1 instanceof { [Symbol.hasInstance]: greet } // triggers greet