皆さんこんにちは。今回はJavaScriptの👻globalThis👻について解説しようと思います。globalThisはJavaScript (ECMAScript) の新機能です。現在TC39プロセスのStage 3にあり1、このまま何事もなければ近いうちにECMAScript2に正式採用されることになります。
この記事はJavaScript2 Advent Calendar 2018の5日目の記事です。
さて、皆さんはこのglobalThisを使ったことがあるでしょうか。私はありません。というか、現在のところglobalThisが利用可能なのは、つい今日安定版がリリースされたばかりのGoogle Chrome 71のみです。この記事にはglobalThisを使ったコード例が出てきますが、実際に動かしたい場合はちゃんとGoogle Chromeが最新版になっているか確認してくださいね。
(Chrome 71がリリースされるのは日本時間で今日(12月5日)のはずですが、あなたがこの記事を読むタイミングによってはまだ出ていないかもしれません。その場合は出るまで待つかベータ版をインストールしてください。)
※ 追記(2019-02-06):現在のところ、Google ChromeとFirefoxの最新版でglobalThisがサポートされています。
※ 追記(2019-04-26):現在のところ、Google Chrome・Firefox・Safariの最新版、そしてnode.js v12でglobalThisがサポートされています。
globalThisとは何か
一言でいうと、globalThisはグローバルオブジェクトです。グローバルオブジェクトというのは、グローバル変数をプロパティに持つようなオブジェクトです。つまり、グローバル変数を作ると、それがグローバルオブジェクトのプロパティとなって現れるのです。
// グローバル変数fooを作成(
foo = 123;
// globalThis.fooが123になっている
console.log(globalThis.foo); // 123
// 逆も可能
globalThis.bar = 456;
console.log(bar); // 456
余談ですが、何も付けないでいきなり作った変数とvar
を使って宣言した変数はグローバル変数になりますが、let
やconst
で作った変数はグローバル変数になりません。やっぱりlet
やconst
は偉いですね。
foo = 123;
console.log(globalThis.foo); // 123
var bar = 456;
console.log(globalThis.bar); // 456
let hoge = 7;
console.log(globalThis.hoge); // undefined
const fuga = 8;
console.log(globalThis.fuga); // undefined
ブラウザにおけるグローバルオブジェクト
ところで、ここまで読んだ一部の方は「あれ?」と思ったのではないでしょうか。というのも、皆さんがお使いのブラウザには既にwindow
が備わっていますよね。JavaScriptでグローバルオブジェクトといえばwindow
のはずです。実際、グローバル変数を作るとwindow
のプロパティになることは古くから知られています。
foo = 123;
console.log(window.foo); // 123
種を明かしてしまうと、globalThis
はwindow
と同じです。
console.log(globalThis === window); // true
とはいえ、「じゃあglobalThisとかいらないじゃん」などとは今さら思いませんよね。window
はブラウザ(DOMが実装されている環境)にしかありませんが、今やJavaScriptが動作する環境はそれだけではありません。
node.jsにおけるグローバルオブジェクト
ブラウザ以外のJavaScript環境として代表的なのがnode.jsです。このnode.jsにはwindow
は存在しません。
その一方で、node.jsにはもともとglobal
があり、これがグローバルオブジェクトの役割を果たしていました。
foo = 123;
console.log(global.foo); // 123
まだnodeにglobalThis
は実装されていませんが、globalThisを実装するPRを見るに、やはり、というか当然ながら、global === globalThis
となるようです。
this
からグローバルオブジェクトを取得する方法
さて、ここまでで記事タイトルのうちglobalThis
とglobal
が出てきましたが、this
は何の関係があるのでしょうか。
これもJavaScriptに関してよく知られた事実ですが、トップレベルの(関数の中でない)コードや、関数を普通に(オブジェクトのメソッドとしてではなく)呼んだ場合はthis
がグローバルオブジェクトになることが知られています。
foo = 123;
console.log(this.foo); // thisがグローバルオブジェクトなのでthis.fooは123
showFoo();
function showFoo() {
// 関数の中でもthisはグローバルオブジェクト
console.log(this.foo);
}
ただし、strictモードの場合は関数の中ではthis
はグローバルオブジェクトではなくundefined
になるのでこの方法は使えません。
foo = 123;
showFoo();
function showFoo() {
'use strict';
// thisがundefinedなのでこれはエラー
console.log(this.foo);
}
その一方、グローバルの(関数の中ではない)this
はstrictモードでもグローバルオブジェクトとして利用可能です。
<script>
'use strict';
var foo = 123;
// トップレベルのthisはstrictモードでもグローバルオブジェクト
// として利用可能なので123が表示される
console.log(this.foo);
showFoo();
function showFoo() {
// 関数の中ではthisはundefinedなのでエラー
console.log(this.foo);
}
</script>
ただ裏技があって、Function
コンストラクタを使うことでstrictモードのコンテキスト内でもstrictモードでない関数を作ることができます。
<script>
'use strict';
var globalObj = (new Function('return this;'))();
console.log(globalObj === globalThis); // true
</script>
しかしそうはいっても、この裏技がうまく動かない環境が少数存在します。具体的にはChrome Appsで使えないそうです3。あと動的に関数を作るのが気持ち悪いですね。
まとめると、グローバルオブジェクトにアクセスする既存の方法は一長一短で、完璧なものはありませんでした。
-
this
はstrictモードの関数内では使用不可。また、実はnode.jsではthis
はグローバルオブジェクトではない。 -
(new Function('return this'))()
はChrome Appsで使用不可。 -
window
はブラウザ環境のみ。 -
global
はnode.jsのみ。
そしてglobalThis
上のリストの上2つは標準で定められた方法ですが使える状況が限られますし、下の2つはECMAScript標準に含まれていないためは環境によって使えるものが異なり統一されていないというのが現状でした。その一方で、今の時代、全ての環境で共通のJavaScriptコードを動かしたい場面はよくあります。(この概念は時代によって呼ばれ方がいろいろと分かれているような気がしますが、自分はIsomorphic JavaScriptとか呼ぶのがしっくり来ます。)
そこで、ECMAScript標準として統一された方法を用意しようということで登場したのがglobalThis
なのです。
globalThis
の概念自体は要するにグローバルオブジェクトなので簡単ですね。ただ、これを読んでいるあなたは、👻globalThis
👻という名前はどうなのと思ったのではないでしょうか。私は思いました。JavaScriptが嫌いな人たちによるネガティブキャンペーンの対象になっているのをよく見るthis
を全面に押し出してくるのはなかなか攻めたネーミングです。そもそも、globalThis
という名前を見ても何が言いたいのかよく分かりません。
まあ、とはいえ、気持ちは分からないでもありません。this
はトップレベルの(関数内でない)環境ではグローバルオブジェクトですから、「そのような(グローバルな)環境でのthis
を参照できるもの」ということでglobalThis
という命名なのでしょう。
ただ、ここでひとつ言えることは、globalThis
は実は次善策だということです。もともとは、グローバルオブジェクトを表す変数の名前はglobal
にする予定でした。つまり、既に存在していたnode.jsの仕様に合わせようとしたのです。
ところが、実際にglobal
を実装してみると、動かなくなるウェブサイトがいくつか発生してしまいました。例えばこのスレッドでは、global
が存在すればnode.js環境であると判断してnode.js向けの処理を行うようなコードが存在することが明らかとなり、それが原因でウェブサイトが動作しなくなった事例が報告されています。
ちょっと本題からずれますが、まったくふざけたコードを書く人もいたものですね。これはglobal
を「グローバルオブジェクト」として使うのではなく「node.jsかどうか判定するためのフラグ」として使用するコードを書いたために起こった問題です。global
ではなくprocess
を使用するコードなども目にしますが将来が危ぶまれますね。
話を戻すと、JavaScriptは後方互換性が重要な言語です。ですから、実際にウェブサイトが壊れてしまう事例がいくつも発生したことでglobal
の採用は頓挫し、別の名前を探さなければいけなくなりました。新しい名前に関するわちゃわちゃした議論をGitHubで見ることができます。
これのおかげでglobal
の標準化は約2年も遅れたわけですが、何だかんだでglobalThis
という名前に決まり、Chrome 71でついに日の目を見ることになったわけです。globalThis
という名前についてはプロポーザルに少し説明があります。
After some data-gathering to determine web compatibility of a short list of choices, we settled on globalThis, as this name is both highly likely to be web compatible, and also maps to the concept of being the “global this value” (and not the “global object”, per above).
まあ要するに先ほど述べたような、グローバルなthis
の値だからglobalThis
なんだよということが書いてありますね。
まとめ
ここまで述べたことはだいたいglobalThisのプロポーザルに書いてあります。既にこれを読んでいた方にとっては新鮮味がない内容だったかもしれませんが、まあそのような方は1行目で既に察したと思うので大丈夫でしょう。
要するに、グローバルオブジェクトを扱う既存の方法はどれも一長一短で統一された方法が無かったので、全ての環境で使えるglobalThis
を作りましたということです。現在のところこれが利用可能なのはGoogle Chromeのみですが、他のブラウザやnode.jsも近いうちに追随するのではないかと思います。
とはいえ、実務上は古い環境も考慮しなければならないのでglobalThis
だけ使えば他は要らない、というわけにはいかないのがつらいところです。むしろglobalThis
対応が加わった結果コードが複雑化するかもしれません。
皆さんももしグローバルオブジェクトを扱う機会があれば、globalThis
のことを思い出してあげてください。
余談
今回は余談をまとめの後に持ってきてみました。以降、仕様書という場合はECMAScript® 2018 Language Specificationを指すものとします。(globalThis
はES2018には入っていませんがまあ大丈夫です。)
モジュールシステムとグローバルオブジェクト
ところで、グローバル変数って難しい概念ですよね。ブラウザ上のJavaScriptでは、何も宣言せずにいきなり代入された変数や、関数の外でvar
で宣言された変数がグローバル変数となります。前者はstrictモードでは使えませんが。
下のコードで確かめてみると、var
で宣言した変数foo
がグローバルオブジェクトのプロパティとして出現していることが分かりますね。
<script>
'use strict';
var foo = 123;
console.log(globalThis.foo); // 123
</script>
……。
ここで、「いや、違うでしょ」と思った人はかなりJavaScriptに詳しいですね。「当たり前のことを何を今さら」と思った人は修行不足です。
そう、さっきの説明には実は誤っているところがあります。どこが違うのかお分かりでしょうか。
答えは以下のコードです。
<script type="module">
'use strict';
var foo = 123;
console.log(globalThis.foo); // undefined
</script>
なんと、script要素にtype="module"
属性を付けたら結果が変わりました。ブラウザ上でのJavaScriptを知っている方は、type="module"
というのがimport
文を使うときに必要なものであることはお分かりだと思いますが、ECMAScript的にはこれは内部のコードをScriptではなくModuleとして評価するようにする効果があります。実はECMAScriptではプログラムはScriptとModuleの2種類に分類されており、export
文やimport
文はModuleの中でしか使えない構文です。普通のscript
要素で読みこまれるような従来のJavaScriptはScriptです。
そして、トップレベルのvar
はScriptではグローバル変数を作る一方、Moduleではそうではないのです。なお、Moduleの中では自動的にstrictモードになるため、無宣言で変数に代入してグローバル変数を作ることはできません。グローバル変数を作りたければ、明示的にグローバルオブジェクトを介する必要があります。
この挙動はよくよく考えてみれば当然ですね。モジュールに分けられたプログラムにおいて、各モジュールは独立している(独立した変数スコープを持っている)べきです。モジュール内部でvar
で作った変数がグローバル変数になって気軽に他に影響を与えてもらっては困ります。
言葉だけだと分かりにくいかもしれませんので、例で説明します。index.html
とmod.js
からなるプログラムを考えましょう。
<script type="module">
import { setFoo } from './mod.js';
var foo = 0;
setFoo(123456);
console.log(foo);
</script>
var foo;
export function setFoo(value) {
foo = value;
}
この例で、index.html
のconsole.log
で表示される値はなんでしょうか。
答えは0です。123456ではありません。これはまさに、mod.js
内で定義された変数foo
がグローバル変数ではない(モジュールのスコープ内の変数になっている)ことの証左であることがお分かりになるかと思います。
このように、モジュールに分けられたJavaScriptではモジュール間で変数のスコープを分けることでモジュール間の独立性を高めています。
ところで、次の実験としてmod.js
を変えて次のようにしてみましょう。
<script type="module">
import { setFoo } from './mod.js';
var foo = 0;
setFoo(123456);
console.log(foo);
</script>
export function setFoo(value) {
globalThis.foo = 123456;
}
この場合はconsole.log
で何が表示されるでしょうか。
答えはやっぱり0です。「123456だろ」と思った方はちょっと前からもう一度読みなおすといいかもしれません。
仕様書で確かめる
今説明した諸々を仕様書で確かめてみたいという方向けの説明も用意しました。まずScriptにおけるグローバル変数の初期化は15.1.11 Runtime Semantics: GlobalDeclarationInstantiationで行なわれます。ステップ7でVarScopedDeclarationsを、ステップ15でLexicallyScopedDeclarationsを計算していますが、前者がvar
で宣言された変数たち、後者がlet
やconst
で宣言された変数たちに相当します。後者の処理はステップ16で行なわれており、流し読みするとenvRec.CreateMutableBinding
が呼ばれています。これは要するに、現在のスコープに変数を作成するということです。この「現在のスコープ」は実はグローバルスコープ(グローバルオブジェクトのプロパティとして現れるグローバル変数たちのスコープ)ではないということがポイントです。
一方、var
で宣言された変数たちの処理はステップ18で行なわれています。こちらはenvRec.CreateGlobalVarBinding
で処理されており先ほどとは違いますね。これを使った場合はグローバル変数(グローバルオブジェクトのプロパティ)として登録されます。ここまでをまとめて例で確かめてみましょう。
<script>
// 実行開始時点で上記のGlobalDeclarationInstatiationは
// 行なわれているので、グローバルオブジェクトに下で宣言されている変数fooが存在
console.log('foo' in globalThis); // true
// var宣言が実際に実行されるまで値はundefined
console.log(globalThis.foo); // undefined
var foo = 123;
// fooに代入されたのでglobalThis.fooも当然書き換わる
console.log(globalThis.foo); // 123
// globalThis.fooへの代入は反映される
globalThis.foo = 99999;
console.log(foo); // 99999
let bar = 345;
// letで宣言した変数はグローバル変数ではない
console.log('bar' in globalThis); // false
// グローバル変数のbarを書き換えてもこの変数barには影響しない
globalThis.bar = 99999;
console.log(bar); // 345
// 逆も同様
bar = -5;
console.log(globalThis.bar); // 99999
</script>
一方でModuleの実行時は何が起こっているでしょうか。それを確かめるには15.2.1.16.4 Instantiate() Concrete Methodから参照されている15.2.1.16.4.2 ModuleDeclarationEnvironmentSetupを見ます。さっきのGlobalDeclarationInstantiationとちょっと似たようなことが書いてありますね。
ただし、var
で宣言された変数たちを処理する部分であるステップ12を見ると、CreateGlobalVarBindingではなくCreateMutableBindingが使われていますね。これが先ほどのScriptの場合との違いであり、Moduleの場合にvar
で宣言した変数がグローバル変数とはならない(モジュールのトップレベルスコープの変数となる)ことの証左です。
<script type="module">
// 先ほどとは異なり、varで宣言した変数がグローバルオブジェクトに存在しない
var foo = 123;
console.log('foo' in globalThis); // false
// globalThis.fooに何かを代入してもこの変数fooには影響しない
globalThis.foo = 99999;
console.log(foo); // 123
</script>
このように、Moduleではvar
で宣言した変数がグローバル変数となりません。このことは注意していないと罠にはまることがあるかもしれません。
node.jsのモジュールシステム
では、仕様書からは離れて次の話題に移りましょう。JavaScriptをばりばり書く方の中でも、前節で取り上げたES Modules(import
とかexport
とかを使うモジュールシステム)にはあまり馴染みがないという方がいるかもしれません。node.js用のJavaScriptプログラムにおいてはまだまだCommonJS Modulesが現役です。require
を使ってモジュールを読み込むやつですね。node.jsもES Modulesの対応を進めていますが、v10でやっと試験実装が利用可能になった段階です。
そして、node.jsにおける各モジュールは、ScriptかModuleかの分類でいえばScriptとなります。じゃあ、トップレベルvar
で変数を作るとどうなるでしょうか。さっそく試してみます。(動作を確認できるようにglobalThis
の代わりにglobal
を使用しています。)
console.log('foo' in global); // false
var foo = 123;
console.log(global.foo); // undefined
……?
なんか、先ほどの説明と違いますね。var
で宣言した変数がグローバル変数になっていません。
これは実はnode.jsの挙動の特徴です。細かいことは省略しますが、各ファイル(モジュール)は暗黙のうちに関数に囲まれているため、var
で宣言した変数はグローバルスコープではなくその関数スコープに属することになります。これにより、各ファイルでvar
で宣言した変数がコンフリクトすることが無くなるという利点があるほか、__dirname
等の実装にも寄与しているらしいです。
ところで、今回の話題に関連するnode.jsの特徴的な挙動はもう1つあります。それがトップレベルでのthis
です。
実は、node.js環境においてはトップレベルの(先ほど説明したようにnode.jsでの各モジュールは関数に囲まれるので厳密にはトップレベルではありませんが)this
はグローバルオブジェクトではありません。その代わりにexports
オブジェクトがthis
に入っています。
// これはnode.jsではtrue
console.log(this === exports);
exports
というのはCommonJS Modules由来のものです。まだちゃんと調べていませんが、ES Modulesモードではまた違った結果になることでしょう。Moduleコンテキストのトップレベルではthis
はundefined
ですから、多分undefined
だと思います。globalThis
が聞いて呆れますね。
globalThis
はグローバルオブジェクトではない!?
ちょっと話は変わりますが、まとめの直前くらいにプロポーザルから引用した文を再掲します。
After some data-gathering to determine web compatibility of a short list of choices, we settled on globalThis, as this name is both highly likely to be web compatible, and also maps to the concept of being the “global this value” (and not the “global object”, per above).
最後の部分(筆者が太字で強調)に興味深いことが書いてあります。読んでみると、globalThis
はグローバルオブジェクトじゃないよという衝撃的なことを述べていますね。なんと、この記事でさんざんglobalThis
はグローバルオブジェクトだよと言ってきたのは嘘だったのです。
とはいえ別に実務上なにか問題があるわけではありません。グローバルオブジェクトだと思って安心して使ってください。最後に、これが言いたいのは何かということを解説したいと思います。
above
のところのリンクの先ではなにやらWindowProxy
というワードが出てきていますね。これはブラウザ上のJavaScriptにおけるwindow
オブジェクトのことを指しています。
ここでポイントとなるのは、JavaScriptでは他のページ(より正確にはブラウジングコンテキスト)のwindowオブジェクトを取得できるという点です。これには、window.open
を使用するとか、iframe
要素のcontentWindow
を使用するなどの方法があります。また、当然ながら同一オリジンポリシーの影響を受けます。
これはブラウジングコンテキストに対して紐付いているオブジェクトですから、参照されているブラウジングコンテキスト内でページが移動してもWindowオブジェクトは健在です。このことを確かめられるテストページを用意したので、PCの方は開きながら続きを読んでください。また、ソースコードは適宜参照してください。(スマートフォン等だと複数タブを行き来するのが面倒かもしれません。また、ソースコードを見ないと何を言っているかいまいち分からないかもしれません。)
テストページを開くとindex.html
が表示されます。「foo.htmlを開く」ボタンを押すとfoo.html
が新しいタブで開かれます。ソースコードを見ると、window.open
の結果(foo.html
のWindowオブジェクト)が(index.html
内の)fooWindow
変数に代入されることが分かります。
このfooWindow
変数は、今開かれたfoo.html
内におけるwindow
オブジェクトと同じです。そのことは、fooWindow
を通じてfoo.html
内のグローバル変数を参照したり操作できることから分かります。
foo.html
を見るとグローバル変数foo
に123
が代入されていることが分かります。一方でindex.html
にある「fooの値を表示」ボタンを押すとfooWindow.foo
が表示されますが、この結果は123
となります。確かにfoo.html
内のグローバル変数foo
が参照できていますね。
さらに、foo.html
内にある「fooの値を変更」ボタンを押すとグローバル変数foo
の値が123456
になります。その後、index.html
に戻って再度「fooの値を表示」ボタンを押すと今度は123456
が表示されます。このことからも、fooWindow.foo
がfoo.html
の中のグローバル変数foo
そのものであることが分かります。
問題はここからです。ブラウザのタブというのはウェブページを表示しており、ウェブページはリンクをたどって別のページに移動することができます。今回は、foo.html
にはbar.html
へのリンクが用意してあります。では、bar.html
に移動してみてください。
皆さんは当然ご存知かと思いますが、別々のページでJavaScriptの実行状態が共有されることはありません。別のページに移動した時点で全ての状態はリセットされます。当然、window
も違うものになるしグローバル変数も全部リセットされます。このことを確かめるためにindex.html
に戻って「fooの値を表示」ボタンを押してみましょう。そうすると、結果はundefined
となります。これは、fooWindow.foo
が無いことを示しており、bar.html
内にグローバル変数foo
が無いことと対応しています。
ここで矛盾が発生していることにお気づきでしょうか。index.html
内の変数fooWindow
は常に同じドキュメントです。これは、index.html
にfooWindow
に再代入するコードが無いことから明らかです。
それにも関わらず、foo.html
からbar.html
に移動した時点でfooWindow.foo
の値が変わりました。つまり、fooWindow
はfoo.html
のグローバルオブジェクトだったのに、bar.html
のグローバルオブジェクトに変化したということです。これは、foo.html
とbar.html
は別々のページなので別々のグローバルオブジェクトを持つという事実と矛盾しています4。
要約すると、index.html
から見るとfooWindow
に入っているオブジェクトは変わっていないのに、実際はページ遷移の過程で別のグローバルオブジェクトに変わっているという矛盾です。
この矛盾を解消するために導入されるのがWindowProxy
です。WindowProxy
は内部にWindow
オブジェクトへの参照を持っていて、基本的にWindowProxy
への操作は内部のWindow
への操作となります5。今回の例ではfooWindow
はWindowProxy
であり、最初は内部でfoo.html
のWindow
オブジェクトとつながっています。ですから、fooWindow
からの読み込みはfoo.html
のWindow
オブジェクトからの読み込みとなりなります。
foo.html
がbar.html
にページ遷移すると、fooWindow
が内部に持っているWindow
オブジェクトへの参照がbar.html
のWindow
オブジェクトへと差し替わります。これはあくまで内部的な動作であり、fooWindow
のWindowProxy
オブジェクトとしての同一性は保ったままです。しかし、この時点でfooWindow
への操作は内部のWindow
オブジェクト、すなわちbar.html
のWindow
オブジェクトへの操作となるため、実際に操作されるWindow
オブジェクトが最初と異なっています。
この2層構造により、前述の矛盾が解消されることになります。結局のところ何が言いたかったのかというと、「window
というのは実はWindowProxy
のことであってグローバルオブジェクトたるWindow
ではないから、今回作るglobalThis
をグローバルオブジェクトと呼ぶのは憚られる。なのでglobalThis
になった」という話でした。
長々と説明しましたが、なんだかとてもややこしいですね。ここでちょっと話を戻して、globalThis
のプロポーザルから一文引用します。
ES6/ES2015 does not account for the Window/WindowProxy structure, and simply refers to ”the global object” directly. This specification does the same.
(超意訳) なんかHTMLの人たちは
Window
とかWindowProxy
とか言ってるけど別にECMAScript的にはそういうのどーでもいいんで、面倒くさいから単に「グローバルオブジェクト」ってことにしてます。
というわけで、一応名前決めるときに気をつけたとはいえ、全くもってどうでもいい話でした。まさに余談ですね。
-
詳細は他のもっと詳しい記事に譲りますが、ECMAScriptに新機能が追加されるにはStage1 〜 Stage 4という4つの段階を経る必要があります。Stage 3は、機能の仕様が固まり、ブラウザ等に試験的に実装されるのを待っている状態です。 ↩
-
今さらな説明ですが、ECMAScriptというのはざっくり言うとJavaScriptを策定している標準仕様の名前です。この記事では、ブラウザとかnode.jsとかで動く実際のJavaScriptと対比して、仕様として定められたJavaScriptの挙動に触れたいときにECMAScriptという言葉を使っています。 ↩
-
。ただ、Chrome Appsは今はChrome OSのみが対象らしいですが。 ↩
-
この事実については理由を説明していませんでしたが、HTMLの仕様でそのように定義されています。別のHTML文書へのナビゲーションが発生すると、HTML文書をロードする過程で新しいDocumentが作成され、その過程でグローバルオブジェクトとして新しいWindowオブジェクトが作られることが明記されています。 ↩
-
ただし、オリジンをまたぐ場合は制限がかかります。これは
WindowProxy
の定義で規定されています。 ↩