HTML: Markup language
CSS: Styling language
JavaScript: Scripting language
Web APIs: Programming interfaces
All web technology
Learn web development
Discover our tools
Get to know MDN better
このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。
View in English Always switch to English
This feature is not Baseline because it does not work in some of the most widely-used browsers.
using 宣言は、同期的に破棄されるブロックスコープのローカル変数を宣言します。 const と同様に、 using で宣言された変数は初期化が必要であり、再代入できません。変数の値は null、undefined、または [Symbol.dispose]() メソッドを持つオブジェクトのいずれかでなければなりません。変数がスコープ外になると、リソースを解放するために、オブジェクトの [Symbol.dispose]() メソッドが呼び出されます。
using
const
null
undefined
[Symbol.dispose]()
using name1 = value1; using name1 = value1, name2 = value2; using name1 = value1, name2 = value2, /* …, */ nameN = valueN;
nameN
宣言する変数の名前。各変数名は、 JavaScript で有効な識別子でなければならず、構造分解バインドパターンであってはなりません。
valueN
変数の初期値。有効な式であれば何でも指定可能ですが、その値は null、undefined、または [Symbol.dispose]() メソッドを持つオブジェクトのいずれかでなければなりません。
この宣言は以下の場所で使用できます。
for
for...of
for await...of
特に、以下では使用できません。
switch
for...in
using は、変数のスコープ(ブロック、関数、モジュールなど)の寿命に紐づく破棄可能リソースを宣言します。スコープが終了すると、リソースは同期的に破棄されます。変数は null または undefined の値を持つことが許されるため、リソースはオプションとすることができます。
変数が最初に宣言され、その値がヌル値でない場合、オブジェクトからディスポーザーが取得されます。 [Symbol.dispose] プロパティに関数が含まれていない場合、 TypeError が発生します。このディスポーザーはスコープに保存されます。
[Symbol.dispose]
TypeError
変数がスコープ外になると、ディスポーザーが呼び出されます。スコープ内に複数の using または await using 宣言が含まれる場合、宣言の順序とは逆の順序で全てのディスポーザーが実行されます(宣言の種類は問いません)。全てのディスポーザーの実行が保証されます(try...catch...finally の finally ブロックと同様です)。破棄処理中に発生したすべてのエラー(スコープ終了の原因となった初期エラーを含む)は、 1 つの SuppressedError 内に集約されます。それぞれの例外は順に suppressed プロパティと error プロパティとして保持され、破棄処理完了後にこの SuppressedError が発生します。
await using
try...catch...finally
finally
SuppressedError
suppressed
error
using はリソース管理を字句スコープに結び付けます。これは便利であると同時に、時に混乱を招くこともあります。変数自体がスコープ外になった際にその値を保持する方法は多数存在するため、既に破棄されたリソースへの参照を保持したままになる可能性があります。期待通りに動作しない可能性がある例を以下に示します。エラー処理の保証を維持しつつリソースの破棄を手動で管理したい場合は、代わりに DisposableStack を使用することができます。
DisposableStack
以下の例では、単純な Resource クラスに getValue メソッドと [Symbol.dispose]() メソッドがある状態を想定します。
Resource
getValue
class Resource { value = Math.random(); #isDisposed = false; getValue() { if (this.#isDisposed) { throw new Error("リソースは破棄されています"); } return this.value; } [Symbol.dispose]() { this.#isDisposed = true; console.log("リソースが破棄されました"); } }
usingで宣言されたリソースは、ブロックを終了する際に破棄されます。
{ using resource = new Resource(); console.log(resource.getValue()); // resource はここで破棄される }
関数本体内で using を使用することができます。この場合、リソースは関数の実行が終了したとき、 関数から戻る直前に破棄されます。
function example() { using resource = new Resource(); return resource.getValue(); }
ここで、 resource[Symbol.dispose]() は getValue() の後、 return 文が実行される前に呼び出されます。
resource[Symbol.dispose]()
getValue()
return
リソースは、クロージャによってキャプチャされていると、宣言よりも長く存続する可能性があります。
function example() { using resource = new Resource(); return () => resource.getValue(); }
この場合、 example()() を呼び出すと、 example から返った時点でリソースが破棄されているため、常に破棄済みのリソースに対して getValue が実行されます。コールバックが一度呼び出された直後にリソースを破棄したい場合は、次のパターンを検討してください。
example()()
example
function example() { const resource = new Resource(); return () => { using resource2 = resource; return resource2.getValue(); }; }
ここでは、 const 宣言されたリソースの別名を using 宣言されたリソースとして作成しています。これにより、コールバックが呼び出された後にのみリソースが破棄されます。コールバックが一度も呼び出されない場合、リソースは解放されないことに注意してください。
モジュールの最上位レベルで using を使用できます。この場合、リソースはモジュールの実行が終了したときに破棄されます。
using resource = new Resource(); export const value = resource.getValue(); // リソースはここで破棄される
export using は不正な構文ですが、using を使用して他の場所で宣言された変数を export することは可能です。
export using
export
using resource = new Resource(); export { resource };
これも推奨されません。インポーターは常に破棄されたリソースを受け取るためです。クロージャの問題と同様に、これによりリソースの値が変数よりも長く存続することになります。
for...of ループの初期化子で using を使用することができます。この場合、リソースはループの反復ごとに破棄されます。
const resources = [new Resource(), new Resource(), new Resource()]; for (using resource of resources) { console.log(resource.getValue()); // リソースはここで破棄される }
以下の 2 つは、複数の使い捨てリソースを宣言する同等の方法です。
using resource1 = new Resource(), resource2 = new Resource(); // または using resource1 = new Resource(); using resource2 = new Resource();
どちらの場合でも、スコープが終了する際に resource2 が resource1 より先に破棄されます。これは resource2 が resource1 に依存している可能性があるためで、resource2 が破棄される時点で resource1 がまだ利用可能であることを保証するために先に破棄されるのです。
resource2
resource1
using を使用すると、変数の値を null または undefined にすることができるため、リソースは任意で存在しても構いません。つまり、例えば、ある種のリソースの利用可否を確認する場合などには、次のようにします。
function acquireResource() { // ここで、このリソースに割り当てる空間が存在するかどうかなど、 // 現実世界に関連する条件を想像してください。 if (Math.random() < 0.5) { return null; } return new Resource(); }
次のようにする必要はありません。
const maybeResource = acquireResource(); if (maybeResource) { using resource = maybeResource; console.log(resource.getValue()); } else { console.log(undefined); }
次のようにすることができます。
using resource = acquireResource(); console.log(resource?.getValue());
using 文を使用すれば、変数を使用せずにリソースの自動破棄が実現できます。これは、ロックの作成など、ブロック内でコンテキストを設定するのにとても便利です。
{ using _ = new Lock(); // ここで並行処理を実行 // ここでロックが解除される }
_ は通常の識別子ですが、使い捨て変数として使用するのが慣例です。変数を使用せずに複数の宣言を行うには、_ を接頭辞とした変数名など、異なる名前を使用する必要があります。
_
using 変数は、 let および const 変数と同様の一時的なデッドゾーンの制約を受けます。これは、変数が初期化される前にアクセスできないことを意味します。つまり、リソースの有効なライフタイムは、厳密に初期化からそのスコープの終了までとなります。これにより、 RAII スタイルのリソース管理が可能になります。
let
let useResource; { useResource = () => resource.getValue(); useResource(); // Error: Cannot access 'resource' before initialization using resource = new Resource(); useResource(); // 有効 } useResource(); // Error: Resource is disposed
using 宣言は、エラー発生時のリソース解放管理において最も有用です。注意を怠ると、エラーによって後続のコードが実行されなくなるため、一部のリソースがリークする可能性があります。
function handleResource(resource) { if (resource.getValue() > 0.5) { throw new Error("リソース値が大きすぎます"); } } try { using resource = new Resource(); handleResource(resource); } catch (e) { console.error(e); }
これにより、 handleResource が発生させたエラーを確実に捕捉してログに記録します。また、 handleResource がエラーを発生させたかどうかにかかわらず、 try ブロックを終了する前にリソースは破棄されます。
handleResource
try
ここで、usingを使用しない場合、次のようなことを行う可能性があります。
try { const resource = new Resource(); handleResource(resource); resource[Symbol.dispose](); } catch (e) { console.error(e); }
しかし、 handleResource() がエラーを発生させた場合、制御は resource[Symbol.dispose]() に到達せず、リソースがリークします。さらに、リソースが 2 つ存在する場合、先行する破棄処理で発生したエラーが後続の破棄処理の実行を妨げ、より多くのリークを引き起こす可能性があります。
handleResource()
より複雑な場合、つまりディスポーザ自身がエラーを発生させる場合を考えてみましょう。
class CantDisposeMe { #name; constructor(name) { this.#name = name; } [Symbol.dispose]() { throw new Error(`${this.#name} を破棄できません`); } } let error; try { using resource1 = new CantDisposeMe("resource1"); using resource2 = new CantDisposeMe("resource2"); throw new Error("メインブロックでのエラー"); } catch (e) { error = e; }
発生したエラーはブラウザーのコンソールで確認できます。その構造は以下の通りです。
SuppressedError: An error was suppressed during disposal suppressed: SuppressedError: An error was suppressed during disposal suppressed: Error: resource1 を破棄できません error: Error: メインブロックでのエラー error: Error: resource2 を破棄できません
ご覧の通り、 error には破棄処理中に発生したすべてのエラーが SuppressedError として格納されています。追加されるそれぞれのエラーは error プロパティとして追加され、元のエラーは suppressed プロパティとして追加されます。
Enable JavaScript to view this browser compatibility table.
Symbol.dispose