括弧なしで関数を呼び出す


275

今日、括弧なしで関数を呼び出すことができると言われました。私が考えることができる唯一の方法は、applyまたはのような関数を使用することでしたcall

f.apply(this);
f.call(this);

しかし、これらには括弧が必要でapplyありcall、私たちを1に置く必要があります。また、次のようなイベントハンドラーに関数を渡すことも検討しましたsetTimeout

setTimeout(f, 500);

しかし、質問は「setTimeout括弧なしでどのように呼び出すのですか?」になります。

では、このなぞなぞの解決策は何でしょうか?括弧を使用せずにJavaScriptで関数を呼び出すにはどうすればよいですか?


59
失礼なことをしようとはしていませんが、私は尋ねなければなりません。
jehna1 2016年

36
@ jehna1 関数の呼び出し方法に関する質問に答えていて、最後に括弧が必要だと言ったときに修正されたため。
Mike Cluck 2016年

3
すごい!私はこの質問がそれほど大きな牽引力を持つとは思っていませんでした。たぶん私は自分でそれを尋ねるべきだったでしょう:-)
Amit

5
これは私の心を打つような質問です。このような虐待や搾取によって、どんな善がもたらされるのでしょうか?<insert any solution>よりも客観的に優れている、またはより有用な1つの有効な使用例を知りたいf()
ありがとう

5
@Aaron:正当な理由はないと言いますが、Alexander O'Maraの回答が指摘するように、ブラケットの削除がコードの実行を防ぐのに十分であるかどうか、または難読化されたJavascriptの競合についてはどうでしょうか?もちろん、保守可能なコードを書こうとするとき、それはばかげた目的ですが、面白い質問です。
PJTraill 2016年

回答:


429

括弧なしで関数を呼び出す方法はいくつかあります。

この関数が定義されているとしましょう:

function greet() {
    console.log('hello');
}

次に、greet括弧なしで呼び出す方法をいくつか示します。

1.コンストラクタとして

ではnew、あなたは括弧なしの関数を呼び出すことができます。

new greet; // parentheses are optional in this construct.

opratorのMDNnewから:

構文

new constructor[([arguments])]

2. As toStringまたはvalueOf実装

toStringvalueOfは特別なメソッドです:変換が必要なときに暗黙的に呼び出されます:

var obj = {
    toString: function() {
         return 'hello';
    }
}

'' + obj; // concatenation forces cast to string and call to toString.

(ab)このパターンを使用して、greet括弧なしで呼び出すことができます:

'' + { toString: greet };

またはvalueOf

+{ valueOf: greet };

valueOfそしてtoString、実際には@@ toPrimitiveメソッド(ES6以降)から呼び出されるため、そのメソッドを実装することもできます。

+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }

2.b valueOf関数プロトタイプでのオーバーライド

前のアイデアを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');
}

次に、@@ iteratorメソッドを定義して、括弧なしで呼び出します

[...{ [Symbol.iterator]: greet_gen }];

通常、ジェネレーターはyieldどこかにキーワードを持っていますが、関数が呼び出される必要はありません。

最後の文は、関数を呼び出しますが、それはまたして行うことができる非構造

[,] = { [Symbol.iterator]: greet_gen };

またはfor ... ofコンストラクトですが、独自の括弧があります:

for ({} of { [Symbol.iterator]: greet_gen });

元の関数でも上記を実行できますが、実行後にgreet例外がトリガーされます(FFおよびChromeでテスト済み)。例外はブロックで管理できます。 greettry...catch

4.ゲッターとして

@ jehna1はこれについて完全な答えを持っているので、彼に信用を与えます。グローバルスコープで括弧なしの関数を呼び出し、非推奨の__defineGetter__メソッドを回避する方法を次に示します。Object.defineProperty代わりに使用します。

greetこのために、元の関数のバリアントを作成する必要があります。

Object.defineProperty(window, 'greet_get', { get: greet });

その後:

greet_get;

windowグローバルオブジェクトが何であるかに置き換えます。

次の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.インスタンスチェッカーとして

instanceofオペレータが実行する@@hasInstance定義された場合、第二オペランドに方法。

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet

1
これは、を使用した非常に興味深いアプローチvalueOfです。
Mike Cluck 2016年

4
ES6を忘れた:func``;
Ismael Miguel

1
あなたは:)はevalを呼び出すことにより、ブラケットを使用する
trincot

1
その場合、関数は実行されませんが、呼び出し元に渡されます。
トリンコット2017

1
別の方法で可能になりますすぐに@trincot パイプライン演算子'1' |> alert
デ・ニーロ

224

これを行う最も簡単な方法は、new演算子を使用することです。

function f() {
  alert('hello');
}

new f;

それは正統で不自然なことですが、機能し、完全に合法です。

newパラメーターが使用されていない場合、オペレーターは括弧を必要としません。


6
修正、これを行う最も簡単な方法は、関数式を強制的に評価するオペランドを付加することです。!fコンストラクター関数のインスタンスをインスタンス化せずに、同じ結果になります(新しいthisコンテキストを作成する必要があります)。パフォーマンスがはるかに高く、多くの一般的なライブラリ(Bootstrapなど)で使用されています。
THEtheChad 2016年

6
@THEtheChad-式を評価することは関数を実行することと同じではありませんが、関数を呼び出す(実行を引き起こす)異なる(より優れた?より驚くべき?)メソッドがある場合は、回答を投稿してください!
アミット2016年

感嘆符を付加する点、または任意の他の単一の文字単項演算子(+-~このようなライブラリーでは)、戻り値が必要とされていないライブラリの最小化バージョンでバイトを保存することです。(つまり!function(){}())通常の自己呼び出し構文は次のとおりです(function(){})()(残念ながら、構文function(){}()はブラウザーに依存していません)最上位の自己呼び出し関数は通常何も返さないため、通常、このバイト節約手法のターゲットです
Ultimater

1
@THEtheChadこれは本当ですか?Chromeで試したところ、関数が実行されませんでした。function foo() { alert("Foo!") };例:!foo戻り値false
Glenn 'devalias'

95

ゲッターとセッターを使用できます。

var h = {
  get ello () {
    alert("World");
  }
}

このスクリプトを次のように実行します。

h.ello  // Fires up alert "world"

編集:

議論さえできます!

var h = {
  set ello (what) {
    alert("Hello " + what);
  }
}

h.ello = "world" // Fires up alert "Hello world"

編集2:

括弧なしで実行できるグローバル関数を定義することもできます。

window.__defineGetter__("hello", function() { alert("world"); });
hello;  // Fires up alert "world"

そして引数付き:

window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world";  // Fires up alert "Hello world"

免責事項:

@MonkeyZeusが述べたように:意図がどれほど優れていても、このコードを運用で使用することは決してありません。


9
「私はあなたがより大きくより良いものに移ったのであなたのコードを引き継ぐ名誉を持っている斧を操る狂人です。それでも私はあなたがどこに住んでいるか知っています」というPOVから厳密に言えば。私はこれが普通の笑ではないことを本当に望みます。維持しているプラ​​グイン内で使用することは可能です。ビジネスロジックコードを書いて、斧を研いでいる間お待ちください:)
MonkeyZeus 2016年

3
@MonkeyZeusハハ、はい!これをGoogleから見つけることができる将来のコーダーのための免責事項を追加
jehna1 2016年

実際、この免責事項は厳しすぎます。「getter as a function call」の正当な使用例があります。実際、非常に成功したライブラリで広く使用されています。(ヒントをご希望ですか?? :-)
Amit

1
__defineGetter__推奨されていません。Object.defineProperty代わりに使用してください。
Felix Kling 2016年

2
@MonkeyZeus-私がコメントで明示的に書いたように、このスタイルの関数呼び出し、内部的にではなく、API自体として非常に成功したパブリックライブラリで、広範囲にわたって意図的に使用されています。
Amit

23

ここだたとえば特定の状況には:

window.onload = funcRef;

ただし、そのステートメントは実際には呼び出されていませんが、将来の呼び出しにつながります

しかし、私は灰色の領域がこのようななぞなぞには大丈夫かもしれないと思います


6
それはいいですが、それはJavaScriptではなく、DOMです。
アミット2016年

6
@ Amit、DOMはブラウザのJS環境の一部であり、JavaScriptのままです。
zzzzBov 2016年

3
@zzzzBovすべてのECMAScriptがWebブラウザーで実行されるわけではありません。それとも、「JavaScript」を使用して、Nodeとは対照的に、ESとHTML DOMを組み合わせることに特に言及している人ですか?
Damian Yerrick 2016年

9
@DamianYerrick、私はDOMを一部のJS環境で使用可能なライブラリと見なしています。これにより、一部の環境で使用可能なアンダースコアよりもJavaScriptが少なくなることはありません。それはまだJavaScriptであり、ECMAScript仕様で規定されている部分ではありません。
zzzzBov 2016年

3
@zzzzBov、「DOMはJavaScriptを使用してアクセスされることが多いですが、JavaScript言語の一部ではありません。他の言語からもアクセスできます。」。たとえば、FireFoxはDOM実装にXPIDLとXPCOMを使用します。指定したコールバック関数の呼び出しがJavaScriptで実装されていることは自明ではありません。DOMは、他のJavaScriptライブラリのようなAPIではありません。
トリンコット2016年

17

横向きの考え方を受け入れる場合、ブラウザには、実際の括弧文字なしで、関数の呼び出しなど、任意のJavaScriptを実行するために悪用できるいくつかのAPIがあります。

1. locationおよびjavascript:プロトコル:

そのような手法の1つはjavascript:location割り当て時にプロトコルを乱用することです。

実例:

location='javascript:alert\x281\x29'

けれども、技術的 \x28および\x29コードが評価されると、実際まだカッコある()の文字が表示されません。括弧は、割り当て時に評価されるJavaScriptのストリングでエスケープされます。


2. onerrorおよびeval

同様に、ブラウザによってはonerror、をに設定しeval、有効なJavaScriptに文字列化するものをスローすることにより、グローバルを悪用することができます。これはブラウザがこの振る舞いに一貫性がないのでトリッキーですが、これがChromeの例です。

Chromeの動作例(Firefoxではなく、他は未テスト):

window.onerror=eval;Uncaught=0;throw';alert\x281\x29';

は最初の引数としてthrow'test'に渡さ'Uncaught test'れるため、これはChromeで機能しonerrorます。これは、ほぼ有効なJavaScriptです。私たちが代わりにthrow';test'それをすれば合格し'Uncaught ;test'ます。これで有効なJavaScriptができました!を定義しUncaught、テストをペイロードに置き換えます。


結論として:

このようなコードは本当にひどいため、決して使用すべきではありませんが、XSS攻撃で使用されることがあるので、XSSを防ぐために括弧のフィルタリングに頼る必要はありません。このようなコードを防ぐためにCSPを使用することも良い考えです。


これはeval、括弧文字を実際に使用せずに文字列を使用して書き込むことと同じではありません。
Zev Spitz

@ZenSpitzそうですが、eval通常は括弧が必要なので、このフィルター回避ハックです。
Alexander O'Mara

5
間違いなく\x28\x29まだ括弧です。'\x28' === '('
トリンコット2016年

@trincotこれは私が回答で取り上げた種類のポイントですが、これは、これが行われる理由を示す横向きの考え方です。答えをもっと明確にしました。
Alexander O'Mara 2016年

また、これを削除することに投票した人は、回答を削除するためのガイドラインを確認してください
Alexander O'Mara

1

ES6には、タグ付きテンプレートリテラルと呼ばれるものがあります。

例えば:

function foo(val) {
    console.log(val);
}

foo`Tagged Template Literals`;


3
確かに、これは私が1.5年前に投稿した回答に含まれています...なぜ新しい回答に値するのかわかりませんか?
トリンコット

2
同じことを今実装したい他の人々が新しい答えを使うことができるので。
mehta-rohan 2018

3
@ mehta-rohan、私はそのコメントを理解できません。これが理にかなっているとしたら、同じ答えを何度も繰り返し複製してみませんか?それがどのように役立つかわかりません。
トリンコット2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.