【STEP.16】
組み込みオブジェクト・グローバルオブジェクト・Objectオブジェクトについて学ぼう!

オブジェクトとは

JavaScriptのオブジェクトとは、名前をキーにアクセスできる配列、要するに、連想配列(ハッシュ)です。

これは、JavaScriptのオブジェクトを実装的な視点からとらえるといった意味では正しいのですが、オブジェクトという概念そのものを説明するには不足しています。

オブジェクトとは、単に名前が付いた入れ物の集合ではありません。

オブジェクト自体が1つのモノであり、中に含まれる要素は、このモノの特性や動作を表すために存在します。

要は、(連想配列ではなく)「オブジェクト」という言葉を使う場合には、主役は個々の要素ではなく、オブジェクト(モノ)そのものということです。

連想配列,複数要素の集合体(個々の要素が主体),オブジェクト,1つのモノを表現するために、複数の属性情報を持つ(モノ全体が主体),JavaScriptでは構文的には連想配列もオブジェクトも同じ名前付きの配列です。しかし、言葉として使い分ける場合、それぞれの意味合いは全く違うものとなります。

プログラム上で扱う対象をオブジェクト(モノ)に見立てて、オブジェクトを中心としてコードを組み立てていく手法のこと、オブジェクト指向と呼びます。

オブジェクト指向言語として有名なのはJava、C#、Rubyなどですが、JavaScriptもまたその一員です。

オブジェクト=プロパティ+メソッド

オブジェクトはプロパティとメソッドから構成されます。

プロパティとは、「オブジェクト(モノ)の状態や特性を表すための情報」のことです。

たとえば、入力フォームを表すFormのようなオブジェクトであれば、フォームの名前、フォームによる送信先などが、プロパティに相当します。

対して、メソッドはオブジェクト(モノ)を操作するための道具です。

Formオブジェクトであれば、「フォームの情報をサーバーに送信する」「フォームの内容をクリアする」などの機能がメソッドに相当します。

オブジェクト,プログラムで扱うう対象(モノ),プロパティ,モノの状態や特性を表す情報,フォームの名前,フォームの送信先URL,フォームに含まれる要素群,メソッド,モノを操作するための道具,フォームを送信する,フォームをクリアする,フォームを無効化する,フォームを表すフォームオブジェクト

プロパティ/メソッドという視点から見た時、オブジェクトとは「データを操作するための様々な機能を持った」高機能の入れ物、といえます。

オブジェクトを利用するための準備【new演算子】

オブジェクト指向の世界では、例外を除いて、もともと用意されたオブジェクトを直接利用することを認めていません。

というのも、オブジェクトは「自分自身の中でデータを保持できる」という性質を持っているからです。

たとえば、オブジェクトに対して、アプリが複数の箇所から異なる目的でデータを書き込んでしまったらどうでしょう。

オブジェクト,衝突,オブジェクトはそれ自身がデータを保持するもの,複数の箇所から異なる値をセットしようとるすれば不整合が発生します。

当然、データは互いに衝突して、アプリは正しく動作しません。

そこで、オリジナルのオブジェクトには手を加えず、「オリジナルを複製したコピー」を操作することで、データの競合を防ぐようにしているのです。

このように、オブジェクトの複製を作ることをインスタンス化、インスタンス化によって出来上がった複製のことをインスタンスと呼びます。

インスタンス化とは、「オブジェクトを扱うための《自分専用の領域》を確保する行為」と言い換えることもできます。

オブジェクト,プロパティ,インスタンス,インスタンス化,オブジェクトのコピーを生成,それぞれ別物なので、操作がバッティングする心配がない

オブジェクトをインスタンス化するにはnew演算子を利用します。

let 変数名 = new オブジェクト名([引数,...])

オブジェクトには、オブジェクトを初期化するために、オブジェクトと同名のメソッドが用意されています。

この初期化メソッドのことをコンストラクターと呼びます。

オブジェクト名は、正確にはコンストラクター名です。

生成されたインスタンスは変数に格納され、以降は、その変数をオブジェクトとして扱えるようになります。

インスタンスが格納された変数は、インスタンス変数オブジェクト変数と呼ぶ場合もあります。

インスタンス変数からプロパティ/メソッドを呼び出すには、ドット演算子を使って以下のように記述します。(ブラケット構文を利用することもできます。)

変数名.プロパティ名[=設定値];
変数名.メソッド名([引数[,...]]);

静的プロパティ/静的メソッドとは

ただし、プロパティやメソッドによっては、例外的にインスタンスを生成せずに利用できるものがあります。

このようなプロパティ/メソッドのことを静的プロパティ静的メソッド、またはクラスプロパティクラスメソッドと呼びます。

静的プロパティ/静的メソッドを呼び出すための一般的な構文は、以下の通りです。

オブジェクト名.プロパティ名[=設定値];
オブジェクト名.メソッド名([引数[,...]]);

これらの静的メソッドは、インスタンス変数から呼び出そうとするとエラーとなります。

なお、静的プロパティ/静的メソッドに対して、インスタンスを経由して呼び出すプロパティ/メソッドのことをインスタンスプロパティインスタンスメソッドといいます。

組み込みオブジェクト

組み込みオブジェクトとは

JavaScriptでは多くのオブジェクトが公開されていますが、その中でも最も基本的なものが組み込みオブジェクト(Built-in Object:ビルトインオブジェクト)です。

組み込みオブジェク

「組み込み」というのは、「JavaScriptに標準で組み込まれた」という意味です。

ブラウザーオブジェクトが特定の環境(ブラウザー上)でしか動作しないのに対し、組み込みオブジェクトはJavaScriptが動作するすべての環境で利用できます。

JavaScriptでは自分でオブジェクトを定義することもできますが、これらの組み込みオブジェクトは、特別な宣言や定義なしで利用することができます。

JavaScriptで利用可能な組み込みオブジェクト

JavaScriptで利用可能な組み込みオブジェクトは以下の通りです。

【JavaScriptで利用可能な組み込みオブジェクト】
オブジェクト概要
(Global)JavaScriptの基本機能にアクセスするための手段を提供
Number数値を操作するための手段を提供
String文字列を操作するための手段を提供
Boolean真偽値を操作するための手段を提供
Symbol
ES2015
シンボルを操作するための手段を提供
Array配列を操作するための手段を提供
Objectすべてのオブジェクトのひな型となる機能を提供
Function関数を操作するための手段を提供
Math数値演算を実行するための手段を提供
Date日付を操作するための手段を提供
RegExp正規表現に関わる機能を提供
Error/XxxxxErrorエラー情報を管理
Promise
ES2015
非同期処理を実装するための手段を提供
Map/WeakMap
ES2015
キー/値から成る連想配列を操作するための手段を提供
Set/WeakSet
ES2015
一意な値の集合を管理するための手段を提供
Proxy
ES2015
オブジェクトの挙動をカスタマイズする手段を提供

NumberStringBooleanSymbolArrayObjectFunction、までが、JavaScriptのデータ型に対応しています

ここで、Stringオブジェクトのlengthプロパティを利用して文字列長を求める例を見てみましょう。

let str = 'こんにちは!';
console.log(str.length);
// 文字列長を取得(結果は6)

オブジェクトを利用するには、インスタンス化という手続きを踏む必要があります。

しかし、JavaScriptでは、リテラルをそのまま対応する組み込みオブジェクトとして利用できるので、インスタンス化をほとんど意識する必要がありません。

基本データ型ではnew演算子は利用しない

もっとも、基本データ型でも、new演算子を使って、明示的にオブジェクトを生成することはできます。

let str = new String('こんにちは!');

しかし、ほとんどの場合、これは冗長であるだけでなく、むしろ有害です。

たとえば、以下のような例を見てみましょう。

// 本来は「let flag = false;」と書くべき
let flag = new Boolean(false);
if (flag) {
  console.log('flagはtrueです!');
}
// 結果:flagはtrueです!

変数flagの値がfalseであるにも関わらず、Booleanコンストラクターで生成されたオブジェクトは無条件にtrueと見なされているのです。

これは、JavaScriptが、

null以外のオブジェクトをtrueと見なす

ために起こる挙動です。

もちろん、これは意図した挙動ではなく、このような記述は避けるべきです。

繰り返しになりますが、

基本データ型をnew演算子を使ってインスタンス化するのは、原則として避けてください。

ラッパーオブジェクトとは

JavaScriptが標準的なデータ型を扱う組み込みオブジェクトの中でも、特に基本型である文字列、数値、論理値を扱うためのオブジェクトのことをラッパーオブジェクトといいます。

ラッパーオブジェクトとは、「単なる値にすぎない基本型のデータを包んで(ラップして)、オブジェクトとしての機能を追加するためのオブジェクト」です。

JavaScriptでは、基本データ型と、オブジェクトとしての体裁を持つラッパーオブジェクトとを、自動的に相互変換するため、アプリ開発者がこれを意識する必要はありません。

ラッパーオブジェクト

Objectオブジェクト

Objectオブジェクトとは

Objectオブジェクトは他のオブジェクトとはやや毛色が異なります。

というのも、Objectオブジェクトはそれ自身をインスタンス化できるだけではなく他のオブジェクトに対して

オブジェクトの共通的な性質/機能を提供する

という役割を担っているからです。

Objectオブジェクトは

すべてのオブジェクトの基本オブジェクトである

ともいえます。

つまり、組み込みオブジェクトでも、ユーザー定義のオブジェクトでも、「オブジェクト」と名前の付くものはすべて、Objectオブジェクトで定義されたプロパティやメソッドを共通して利用することができます。

Objectオブジェクトは全てのオブジェクトの基本

MEMO
ー例外的にObjectクラスの機能を引き継がないものもあるー
ただし、Object.createというメソッドを利用することで、Objectオブジェクトの機能を引き継がないオブジェクトを生成することもできます。

Objectオブジェクトで利用可能な主なメンバー

Objectオブジェクトで利用可能なメンバーは、以下の通りです。

【Objectオブジェクトで利用可能な主なメンバー(*は静的メンバー)】
分類メンバー概要
基本constructorインスタンス化で使用されたコンストラクター(読み取り専用)
toString()オブジェクトの文字列表現を取得
toLocaleString()オブジェクトの文字列表現を取得(地域依存)
valueOf()オブジェクトの基本表現(多くは数値)を取得
*assign(target,src,…)オブジェクトtargetにオブジェクトsrcをコピー
ES2015
*create(proto [,props])オブジェクトprotoをもとに、新たなオブジェクトを生成(引数propsは生成するオブジェクトに含まれるプロパティ情報)
*is(v1,v2)引数v1,v2が等しいかどうかを判定
ES2015
プロパティ*keys(obj)オブジェクト自身に存在する列挙可能なプロパティを取得
hasOwnProperty(prop)オブジェクトが指定したプロパティを持つかを判定
propertyIsEnumerable(prop)プロパティpropが列挙可能であるかを判定
*defineProperties(obj,props)オブジェクトobjにプロパティ群propsを設定
*defineProperty(obj,prop,desc)オブジェクトobjにプロパティpropを設定
*getOwnPropertyDescriptor(obj,prop)プロパティpropの属性情報を取得
*getOwnPropertyNames(obj)オブジェクトobjに含まれるすべてのプロパティ名を取得(列挙可能/不可を区別しない)
*getOwnPropertySymbols(obj)オブジェクトobjに含まれるすべてのシンボルプロパティを取得ES2015
プロトタイプ*getPrototypeOf(obj)オブジェクトobjのプロトタイプを取得
*setPrototypeOf(obj,proto)オブジェクトobjにプロトタイプを設定
ES2015
isPrototypeOf(obj)呼び出し元のオブジェクトが指定したオブジェクトのプロトタイプであるかを判定
変更不可*preventExtensions(obj)プロパティの追加を禁止
*freeze(obj)オブジェクトを凍結
*seal(obj)オブジェクトを封印
*isExtensible(obj)プロパティを追加可能であるか
*isFrozen(obj)オブジェクトが凍結されているか
*isSealed(obj)オブジェクトが封印されているか

分類「プロパティ」「プロトタイプ」に属するメンバーを中心として、Objectオブジェクトのメンバーは、オブジェクト指向構文に密接に関係します。

ここではそれらを除くメンバーについて、主要なものを解説します。

オブジェクト指向構文に関しては以下のリンクを参照ください。

オブジェクトを基本型に変換する【toString/valueOf】

toStringvalueOfメソッドは、それぞれオブジェクトの内容を基本型の値に変換します。

両者の役割はよく似ていますが、以下のような違いがあります。

  • toStringメソッド 文字列を返す
  • valueOfメソッド 文字列以外の基本型の値が返されることを「期待して」使われる

いずれもアプリ開発者が自分で呼び出す状況は少なく、オブジェクトを文字列、または基本型の値に変換すべき文脈で、暗黙的に呼び出されます。

window.alertメソッド、+演算子などは、暗黙的にtoStringメソッドが呼び出され、文字列表現に変換されています。

では、標準的な組み込みオブジェクトで、toStringvalueOfメソッドが返す値を見てみましょう。

let obj = new Object();
console.log(obj.toString());
// 結果:[object Object]
console.log(obj.valueOf());
// 結果:Object
	
let dat = new Date();
console.log(dat.toString());
// 結果:Sat Feb 01 2020 10:28:01 GMT+0900 (日本標準時)
console.log(dat.valueOf());
// 結果:1580520481682
	
let ary = ['JavaScript','PHP','Java'];
console.log(ary.toString());
// 結果:JavaScript,PHP,Java
console.log(ary.valueOf());
// 結果:["JavaScript", "PHP", "Java"]
	
let num = 10;
console.log(num.toString());
// 結果:10
console.log(num.valueOf());
// 結果:10
	
let reg = /[0-9]{3}-[0-9]{4}/g;
console.log(reg.toString());
// 結果:/[0-9]{3}-[0-9]{4}/g
console.log(reg.valueOf());
// 結果:/[0-9]{3}-[0-9]{4}/g

まず、ObjectオブジェクトのtoStringメソッドは、意味ある情報を返しません。

オブジェクトを自作する場合には、組み込みオブジェクトがそうであるように、個々のオブジェクトで意味ある情報を返すよう、toStringメソッドを定義するようにしてください。

toStringメソッドでオブジェクトが管理している情報を適切に返すようにすることで、デバッグ/テストに際してもオブジェクトの内容確認が簡単になります。

一方、valueOfメソッドは、ほとんどの組み込みオブジェクトは自分自身を返すだけです。

唯一、Dateオブジェクトだけが、日付/時刻の数値表現(タイムスタンプ値)を返しています。

こちらも、オブジェクトが基本型として表せる値を持っているならば、個々に定義するようにしてください。

オブジェクトをマージする【assign】

オブジェクト/ハッシュ(連想配列)を連結する場合には、Objectオブジェクトのassignメソッドを利用します。

assignメソッド

Object.assign(target, source, ...)
target    ターゲット
source    コピー元

引数source…のメンバーを、引数targetにコピーします。

let data1 = {
  id: 01,
  name: 'Yokohama',
  description: {
    birth: '1996-9-16'
  },
};
let data2 = {
  age: 23,
  married: false,
  description: {
    job: 'actor'
  },
};
let data3 = {
  blood: 'O',
  name: 'Ryusei Yokohama',
};

Object.assign(data1, data2, data3);
console.log(data1);
{
  id: 1,
  name: "Ryusei Yokohama", 
  description: {
    job: "actor"
  },
  age: 23,
  married: false,
  blood: "O"
}

assignメソッドを利用する際には、以下の点に注意する必要があります。

  • 同名のメンバーが存在する場合には、後のもので上書きされます。(上記のソースコードではname)
  • 再帰的なマージには非対応です。(上記のソースコードではdescriptionプロパティは丸ごと上書きされます。)

また、assignメソッドは、先頭のオブジェクトを書き替えます。

もしも元のオブジェクトに影響を及ぼしたくない場合には、「Object.assign(data1, data2, data3);」の部分を以下のようにします。

let merged = Object.assign({},data1, data2, data3);

これによって、空のオブジェクト({})に対してdata1~3をマージしなさいという意味になります。

data1~3には影響は及びません。

マージされた結果は、assignメソッドの戻り値として返されます。

オブジェクトの生成時にプロトタイプ/プロパティを細かく設定する【create】

createメソッドを利用することで、オブジェクトを生成する際に、元となるプロトタイプ、プロパティが列挙可能か、読み取り専用かなどを設定できます。(細かい設定が不要ならば、オブジェクトリテラルを利用すれば十分です。)

createメソッド

Object.create(proto [,props])
proto    生成するオブジェクトのプロトタイプとなるオブジェクト
props    プロパティ情報({ プロパティ名: { パラメーター: 値,...},... }の形式。)
【プロパティの主な構成パラメーター(引数props)】
パラメーター概要既定値
cofigurableプロパティの削除や属性の変更が可能かfalse
enumerable列挙が可能かfalse
value
writable書き換え可能かfalse
getゲッター関数
setセッター関数

以下は、nameagememoなどのプロパティを持ったオブジェクトmemberを定義した例です。

'use strict';
		
let member = Object.create(Object.prototype, {
  name: { value: '横浜流星', writable: true, configurable: true, enumerable: true },
  age: { value: 23, writable: true, configurable: true, enumerable: true },
  memo: { value: 'スターダスト所属の俳優', writable: true, configurable: true, enumerable: true }
});
// プロパティ値を変更
// member.memo = 'モデル';
// プロパティを削除
// console.log(delete member.memo);
// プロパティを列挙
// for (let key in member) {
// console.log(key + ':' + member[key]);
// }

writablecofigurableenumerableなどのパラメーターは、いずれも既定でfalseです。

たとえば上のソースコードの太字となっている部分writable: true, configurable: true, enumerable: true )を削除したうえで、コメントアウトされた「member.memo = 'モデル';」「console.log(delete member.memo);」「for (let key in member) {...}」のコードを、それぞれ有効化してみます。

その結果は、以下の通りです。

  1. Cannot assign to read only property ‘memo’ of object ‘#<Object>’
    (memoプロパティは読み取り専用)
  2. Cannot delete property ‘memo’ of #<Object>
    (memoプロパティの削除は不可。)
  3. name:横浜流星/age:23
    (memoプロパティが列挙されない)
MEMO
ーStrictモードー
サンプルでStrictモードを明示的に有効化しているのは、非Strictモードでは制約違反にもかかわらず、例外が発生しないからです。(=無視されるだけで通知されません。)

これでは問題あるコードを特定しにくいので、writable/cofigurableなどを利用する際にはStrictモードを有効にすべきです。

完全に空のオブジェクトを生成する

オブジェクトリテラルでは、暗黙的にObject.ptorotypeをプロトタイプとしています。(つまり、「Objectオブジェクトの機能を引き継いだオブジェクトを生成しなさい」という意味です。)

そのため、createメソッドでオブジェクトリテラルと同じ挙動をとるには、引数protoに明示的にObject.prototypeを渡す必要があります。

逆に、引数protonullを渡すことで、Objectオブジェクトすら引き継がない完全に空のオブジェクトを生成することもできます。

let obj = Object.create(null);

あとからプロパティを追加する【defineProperty】

definePropertyメソッドを利用することで、既存のオブジェクトに対して、後付けでプロパティを追加することもできます。

definePropertyメソッド

Object.defineProperty(obj, prop, descriptor)
obj         プロパティを定義するオブジェクト
prop        プロパティ名
descriptor  プロパティの構成情報

たとえばmemberオブジェクトに対して、memoプロパティを追加するならば、以下のように表します。

Object.defineProperty(member, 'memo',
  {value: 'モデル', writable: false, configurable: false, enumerable: false}
);

グローバルオブジェクト

グローバルオブジェクトとは

Globalオブジェクト(グローバルオブジェクト)は、他のオブジェクトと異なります。

たとえば

let g = new Global();

のようにインスタンス化することもできませんし、

Global.メソッド名(...);

のように配下のメンバーを呼び出せるわけでもありません。

グローバルオブジェクトとは、いうなれば、グローバル変数やグローバル関数を管理するために、JavaScriptが自動的に生成する便宜的なオブジェクトです。

グローバル変数/グローバル関数とは、要は、関数配下に属さないトップレベルの変数/関数のことです。

グローバル変数/関数は、自分自身で定義することも可能ですが、JavaScriptでもいくつかのグローバル変数/関数をデフォルトで提供しています。

これらグローバル変数/グローバル関数は、「Global.~」ではなく、ただ単に

変数名
関数名(引数,...)

のように参照することが可能です。

Globalオブジェクトには、プログラミングを行ううえで重要な(また、よく利用する機能)が含まれています。

JavaScriptで利用可能なグローバル変数/関数

JavaScriptで利用可能なグローバル変数/関数には次に掲げる表のようなものがあります。

【JavaScriptで利用可能なグローバル変数/関数】
分類メンバー概要
特殊値NaN数値でない(Not a Number)
Infinity無限大(∞)
undefined未定義値
チェックisFinite(num)有限値かどうか(NaN、正負の無限大でない)
isNaN(num)数値でない(Not a Number)かどうか
変換Boolean(val)真偽値に変換
Number(val)数値型に変換
String(val)文字列型に変換
parseFloat(str)文字列を浮動小数点数に変換
parseInt(str)文字列を整数値に変換
エンコencodeURI(str)文字列をURIエンコード
decodeURI(str)文字列をURIデコード
encodeURIComponent(str)文字列をURIエンコード
decodeURIComponent(str)文字列をURIデコード
解析eval(exp)式/値を評価

JavaScriptで利用可能なグローバル変数/関数のうち、文字列をURIエスケープするencodeURIencodeURIComponentメソッド、及び、動的に生成したスクリプトを実行するeval関数について解説していきたいと思います。

文字列をURIエスケープする【encodeURI/encodeURIComponent】

「https://www.google.co.jp/search?q=javascript」のように、URLの末尾に「?キー名=値&…」の形式で渡される文字列のことを、クエリ情報といいます。

サーバーで動作しているアプリに対してパラメーター情報を引き渡すために利用する簡易な情報です。

ただし、クエリ情報では区切り文字である「?」「=」「%」をはじめ、空白、マルチバイト文字などを含めることができません。

これらの文字が入っている可能性がある文字列をクエリ情報として送信したい場合には、あらかじめ文字列をURIエンコードしておく必要があります。

これを行うのが、encodeURIencodeURIComponentメソッドの役割です。

let data = '+/,;:,#$@?=&';
console.log(encodeURI(data));
// 結果:+/,;:,#$@?=&
console.log(encodeURIComponent(data));
// 結果:%2B%2F%2C%3B%3A%2C%23%24%40%3F%3D%26

encodeURIencodeURIComponentメソッドの違いは、後者が「+」 「/」 「;」 「:」 「,」 「#」 「$」 「@」 「?」 「=」 「&」をエスケープの対象とするのに対して、前者がこれらをそのままにする点です。

エスケープされた文字列を元に戻すには、decodeURIdecodeURIComponentメソッドを利用します。

よく似たメソッドとして、escapeメソッドもありますが、こちらは環境、文字コードによって結果が変わります。

下位互換性を維持したいなどの目的がない限り利用してはいけません。

動的に生成したスクリプトを実行する【eval関数】

eval関数は、与えられた文字列をJavaScriptのコードとして評価/実行します。

たとえば以下は、文字列として渡されたconsole.logメソッドをeval関数で解析/実行する例です。

let str = 'console.log("eval関数");';
eval(str);
//「eval関数」とログ出力

このようにしてみると、eval関数には自在にJavaScriptのコードを引き渡し、実行できるので、柔軟にコーディングできるように見えます。

しかし、以下のような理由から、乱用は避けるべきです。

  • 第3者が任意のスクリプトを自由に実行できてしまう可能性がある(セキュリティリスク)
  • 通常のコードを実行するよりも、処理速度が遅い(パフォーマンスの劣化)

一般的な用途であれば、eval関数には、より安全な代替策があります。

たとえば「変数(式)の値によってアクセスすべきプロパティを切り替えたい」という状況を考えてみましょう。

eval関数を利用することで、以下のようにも書けます。

let obj = { hoge: 1, foo: 2};
let prop = 'hoge';
eval('console.log(obj.' + prop + ');');
// 結果:1

しかし、ブラケット構文を利用したほうがよりシンプルで、しかも安全なコードを書くことができます。

console.log(obj[prop]);
// 結果:1

また、「'{ "hoge": 1, "foo": 2 }'」のようなデータ文字列を取り込むようなケースで、以下のようなコードを書く必要はありません。

eval('let data = {"hoge": 1, "foo": 2}');
console.log(data.hoge);

代わりに、JSON.parseという専用のメソッドを利用します。

let data = JSON.parse('{"hoge": 1, "foo": 2}');
console.log(data.hoge);
// 結果:1

JSON.parseメソッドで処理できるコードは、eval関数よりも限定されているので、データ変換を目的とするならば、よりスマートに、より安全に処理できます。

このように、eval関数を利用したくなるような局面では、たいがい代替策が用意されています。

まずは、他の方法で置き換えられないかを検討するようにしてください。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です