見出し画像

ClusterScriptを見るときあるといいJavaScript基礎知識

ClusterScriptで使う、基礎的なところのみに絞って、
JavaScriptを説明します。
※ClusterScriptはJavaScriptで説明される全機能を使えるわけではないので、
 お気をつけください。


【参考】

「$」って?

Cluster Scriptで必ず使う「$」ですがこれは、「ClusterScript」本体を定義している記号と思ってください。
ClusterScriptの内容にアクセスする為には必要なものと覚えてください。

では、なんで「$」なの??

と思う人もいるかもしれませんが、これは、、、
Cluster Scriptを作った ”Cluster社の中の人みぞしる” です。(ぉぃ

あ、いや、ほんとわからんのです。(^^;
そういうもんだと覚えてください。
jQueryを意識してかもしてるのかなとかおもたりもしなかったり。。。

ログ出力

この記事は、Javascriptを意識して、
デバック用出力を「console.log」としていますが、
ClusterScriptでログ出力する場合は、
$.log」としてください。

変数定義

Cluster Scriptの値を保持する為の型は基本JavaScriptで使える型はつかえるようです。
変数は、値を入れるための器を宣言していると思ってください。
変数を宣言しただけで値を入れない場合は、
undefined」で初期化されます。

※変数名はルールがあり、
 変数名の最初は
  ・「A-Z」、「a-z」文字(大文字小文字)
  ・「_」(アンダーバー)
  ・「$」
 から始まる必要があります。
 それ以降になると、数字[0-9]も使えます。
※最初の文字は一部Unicode文字も使えるようですが、
 アルファベットの範囲で使っておいた方がわかりやすいと思います。

let data; // ←"data"と言う名前の変数定義
console.log(data); // undefinedと出力される

【const】

値を一度入れたら変更できない変数宣言(定数とも言います。)
例) const data = 5;
※dataと言う変数を定義して5を代入。
 data変数は、この宣言以降は値を書き換えできない。

【let】

スコープ内({}で囲まれた内側)で使用できる変数として使えます。
スコープ外ではundefinedになります。

$.onGrab((isGrab, isLeftHande, player) => {
  if (isGrab) {
    let data = 1;

    console.log(data) // スコープ内なので、「1」が出力

  }
  console.log(data) // スコープ外なので「undefined」が出力
});

【var】

関数スコープまたはグローバルスコープで使用できる変数として使えます。
変数の定義範囲がわかり辛くなるので、
Cluster Scriptでは、極力使わない方がいいと個人的には思います。
(関数については後述)

$.onGrab((isGrab, isLeftHande, player) => {
  if (isGrab) {
    var data = 2;

    console.log(data) // 2が出力

  }
  console.log(data) // 「var data」は、ifの内側スコープで宣言していても関数内で有効なので「2」が出力
});


コメント

JavaScriptはプログラムを書いている中(ソースコード)に、コメントを残す機能があります。
コメントは実行時には無視される部分ですので、いろいろ記載することができます。

「//」

「//」を書くと、「//」以降の文字は改行があるまで、その行はコメントとして処理されます。

「/*」~「*/」

「/*」~「*/」で囲まれた範囲はコメントとして処理されます。
範囲のコメントなので、行をまたいでコメント1行内の一部にコメントを入れるといったことができます。


代入

変数定義する際や計算結果の値を変数に入れることができます。
これを「代入」とよびます。

let data1 = 1;    // data1という変数に「1」を代入する。 
let data2 = 10.5; // data2という変数に「10.5」を代入する。 


代表的なデータ型

JavaScriptには、データの型というのが存在しています。
下記に示します。

Boolean (論理値)

true または falseの値を取る。
例)let new_data = true;

null

null 値を意味する特殊な値。
例) let new_data = null;

undefined

値が未定義という値。
初期値を入れず、変数を宣言しただけの場合この値で初期化される。
例) let new_data; // undefinedで初期化される

NaN

数値でないことを表します。

Number

整数または浮動小数点数を扱います。
例)
let new_number_data =100;
let new_number_data =1.41421356;

String

テキストなどの文字列の値。
例)
 let new_string_data1 ="あいうえお";
 let new_string_data2 ='あいうえお';
※文字列定義には、
 ["(ダブルコーテーション)」か「'(シングルコーテーション)
 で、囲うと文字列として扱われます。

Object

データをやり取りするためのデータ構造体。
いろいろな値をひとまとめにしたもの)やClassなどを定義出来る。

基本「{」で始まり「}」で閉じる。
定義は、keyと言う変数名みたいなものとその値を「:」つなげる
形で定義する。
  →「key」:「value」
複数定義する場合は、「.」(カンマ)で区切る。
「[」で始まり「]」で閉じるものは配列(array)として扱われる。

例)
let new_data = {"number_data1":100, "number_data2":1.41421356, "string_data":"あいうえお", "boolean_data":true, "array_data":[1,2,3,4,5]};

let new_data = {"number_data1":100, "number_data2":1.41421356, "string_data":"あいうえお", "boolean_data":true, "array_data":[1,2,3,4,5]};

// Number(整数)
console.log("number_data1:"+new_data.number_data1);   // "number_data1:100"
// Number(小数)
console.log("number_data2:"+new_data.number_data2);   // "number_data2:1.41421356"
// String
console.log("string_data:"+new_data.string_data);     // "string_data:あいうえお"
// Boolean
console.log("boolean_data:"+new_data.boolean_data);   // boolean_data:true 
// 配列の0番目(先頭)の値を出力("array_data":[1,2,3,4,5])
console.log("array_data[0]:"+new_data.array_data[0]); // array_data[0]:1 

データ型の混在

ObjectやArrayの中には、その中にいろいろなデータ型を入れられます。

/*
Obect自身はいろいろな型を含めることができます。
追加も自由にできます。
自由度が高い分、変数名など意識してデータ管理する必要があります。
*/

// 配列として変数定義
let data_array_and_object = [];

// 配列[0]は、Object型にする。(data_array_and_object[0].data1 = 123)
data_array_and_object[0] = {"data1":123};
// 配列[1]は、Object型にする。(data_array_and_object[1].data1 = 456.789)
data_array_and_object[1] = {"data1":456.789};
// 配列[2]は、配列にする。
data_array_and_object[2] = [1,2,3];
// 配列[3]は、100を入れる。
data_array_and_object[3] = 100;

console.log(data_array_and_object[0].data1);  // 123
console.log(data_array_and_object[1].data1);  // 456.789
console.log(data_array_and_object[2][0]);     // 1
console.log(data_array_and_object[2][1]);     // 2
console.log(data_array_and_object[2][2]);     // 3
console.log(data_array_and_object[3]);        // 100


四則演算

四則演算は、「+(加算)」、「-(減算)」、「*(乗算)」、「/(除算)」を使います。

// 配列として変数定義
let data1 = 100, data2 = 10.5;

console.log(data1 + data2);  // 110.5
console.log(data1 - data2);  // 89.5
console.log(data1 * data2);  // 1050
console.log(data1 / data2);  // 9.523809523809524

演算の優先順位とカッコ「()」

四則演算には、普通の算数や数学同様の計算の優先順位があります。
「*」、「/」が高く、「+」、「-」はそれより低くなります。
この優先順位を変更する場合、こちらも同様に「()」を使って明示します。

例1)
let  data = 3 - 2 * 20 / 4 + 1; // data = -6
 1.優先順位により「2 * 20 = 40」が計算される。(3 - 40 / 4 + 1)
 2.次に「40 / 4 = 10」が計算される。( 3- 10 + 1 )
 3.「3 -10」が計算される。( -7 + 1)
 4.最後に「-7 + 1」が計算され、dataに「-6」が入る。

例2)
let  data = (3 - 2) * 20 / 4 + 1; // data = 6
 1.「()」の優先順位により「3 - 2 = 1」が計算される。( 1 * 20 / 4 + 1 )
 2.次に「1 * 20 = 20」が計算される。( 20 / 4 + 1  )
 3.「20 / 4」が計算される。( 5 + 1 )
 4.最後に「5 + 1」が計算され、dataに「6」が入る。


文字列結合

文字列は、「+」で結合することが出来ます。

let str_data1 = "あいうえお";
let str_data2 = "かきくけこ";

let cmb_str_data = str_data1 + str_data2;

console.log(str_data1);    // あいうえお
console.log(str_data2 );   // かきくけこ
console.log(cmb_str_data); // あいうえおかきくけこ


条件分岐

条件分岐には「if~else」、「switch」を使います。

if文

if文は、「if~else」などで、条件が成り立つ場合と成り立たない場合を分けて書くことができます。
「else if」で、最初のif文で成り立たなかったとき次に判定する書くこともできます。
※「check % 10」は、
  checkを10で割った余りを返す乗余の書き方です。(算術演算子で後述)

let check = 0;

//なんやかんやでcheckの値が変化

if ((check % 10) == 1) {
  // ①(check % 10)の計算結果が1の時の処理
} else if ((check % 10) == 2) {
  // ②(check % 10)の計算結果が2の時の処理(①の判定が成り立たなかったときこの処理が入る)
} else if ((check % 10) == 3) {
  // ③(check % 10)の計算結果が3の時の処理(①、②の判定が成り立たなかったときこの処理が入る)
} else {
  // ①、②、③いずれも成り立たなかった時の処理
}

ちなみに、「else if」をつかわず、「if」だけで書く場合は、
すべてのif文がつど判定されます。
「else」は最後のif文に対してのみに対応付けされます。

let check = 0;

//なんやかんやでcheckの値が変化

if ((check % 10) == 1) {
  // ①(check % 10)の計算結果が1の時の処理
}
if ((check % 10) == 2) {
  // ②(check % 10)の計算結果が2の時の処理
}
if ((check % 10) == 3) {
  // ③(check % 10)の計算結果が3の時の処理
} else {
  // ③が成り立たなかった時の処理
}

if文の条件式について

if文の条件式は、
if (条件式) {
   // 条件式に結果が真(true)の時の処理
} else
  // 条件式の結果が偽(false)の時の処理
}
となります。
この条件式に使える下記になります。

「a == b」 
  「a」と「b」が同じ値の場合「真(true)」になります。

「a === b」
  「a」と「b」が同じ値で且つ変数の型も同じ場合
  「真(true)」になります。

「a >= b」
   
「a」の値が「b」の値以上の場合「真(true)」になります。

「a > b」
   
「a」の値が「b」の値を超えている場合「真(true)」になります。

「a <= b」
   
「a」の値が「b」の値以下の場合「真(true)」になります。

「a < b」
   
「a」の値が「b」の値未満の場合「真(true)」になります。

「a != b」 
  「a」と「b」が同じ値のでない場合「真(true)」になります。

「a !== b」
  「a」と「b」が同じ値でないか、型が違う場合
  「真(true)」になります。

条件(三項)演算子

「if~else」は、別の書き方もできます。
変数 = (条件式) ? 条件式が真(true)の時の処理 : 条件式が偽(false)の時の処理

const check_result = ((check % 10) == 1) ? (check % 10)が1の時の処理 : (check % 10)が1以外の時の処理

論理演算子

条件式は、論理演算子を使って複数組み合わせたり反転したり出来ます。

「&& 」(論理積)
 
両方の条件式が成り立つ場合真(true)となります。
  ※「&&」と二つ重ねることに注意してください。
    (「&」は別の意味になります。)
 if ((a == b) && (a >c))
 「aの値がbの値と同じ」且つ「aがcを超えている」場合、true。

「|| 」(論理和)
  ※"小文字のL(l)"でなく、
   バーティカルバー(「|」 通称「縦棒」)であることに注意
 
どちらかの条件式が成り立つ場合真(true)となります。
  ※「||」と二つ重ねることに注意してください。
    (「|」は別の意味になります。)
 if ((a == b) || (a >c))
 「aの値がbの値と同じ」もしくは「aがcを超えている」場合、true。

「! 」(否定)
 
条件式が成り立つ場合偽(false)となります。
 if (!(a == b))
 「aの値がbの値と同じ」場合、false。((a != b)と同じ)
 if ( !( (a == b) && (a >c) ) )
 ※複数の条件式の結果を否定することも可能。
 「aの値がbの値と同じ」且つ「aがcを超えている」場合、false

switch文

switchは判定する値をもとに「case」ごとに処理を書くことができます。
各caseの最後に「break」を書くことで、switch文の処理を終了させることができます。(逆に書かない場合、次のcaseの処理に入る。)
最後の「default」は、「case」がすべて成り立たなかったときにする処理を書きます。
※必要がない場合は省略できます。(breakも省略可能)

let check = 0;

//なんやかんやでcheckの値が変化

switch((check % 10)) {
  case 1:
    // ①(check % 10)の計算結果が1の時の処理
    break;
  case 2:
    // ②(check % 10)の計算結果が2の時の処理
    break;
  case 3:
    // ②(check % 10)の計算結果が3の時の処理
    break;
default:
  // ①、②、③いずれも成り立たなかった時の処理
  break;
}
let check2 = 0;

//なんやかんやでcheck2の値が変化

switch((check2 % 100)) {
  case 1:
  case 2:
    // ②(check2 % 100)の計算結果が1、2の時の処理
    break;
  case 3:
    // ②(check2 % 100)の計算結果が3の時の処理
    break;
default:
  // ①、②が成り立たなかった時の処理
  break;
}


ループ処理

ループは繰り返し処理するときに使います。
色々ありますが、
ここでは「for」、「for in」、「for of」、「while」について記載します。

for文

for ([初期値]; [ループする条件式]; [1ループ毎に処理する式]) {
   [ループを行う処理]
}

// 配列の内容を先頭から最後まで表示する処理

const array_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; // 配列定義

let now_loop_cnt; // ループ回数をカウントする変数
let data_max = array_data.length; //配列の値の数(今回は10)

// array_data[0]~array_data[array_data.length(=10) - 1]までの内容を表示
for (now_loop_cnt=0; now_loop_cnt < data_max; now_loop_cnt++) {
  // [0]~[array_data.length - 1]までの内容を順番に表示
  console.log( array_data[now_loop_cnt] );
}

上記で、for処理を補足。。。

for (now_loop_cnt=0; now_loop_cnt < data_max; now_loop_cnt++) {
  // 繰り返し処理する内容
}

(1)forの最初は「now_loop_cnt=0」として、
  now_loop_cntの値が初期化される。
(2)「now_loop_cnt 」と「data_max」の値比較処理(0<10)が行われ、
  条件を満たしているので「{」~「}」の内容を処理する。
(3)次の処理に入る前に
  「now_loop_cnt++」(インクリメント(算術演算子で後述)
 の処理がされ、now_loop_cntの値が"+1"される。
(4)書き換わった「now_loop_cnt 」と「data_max」の値比較処理が行われ、
  条件を満たしているので「{」~「}」の内容を処理する。

以下、
「now_loop_cnt < data_max」の条件が成り立たなくなる
(now_loop_cntの値が10になる)まで、
(3)と(4)を繰り返す。(ループ)

for in 文

for ([変数] in [Object]) {
   [ループを行う処理]
}

forは、値を初期化して条件式を満たす間、ループする処理のものでした。
「for in」は、Objectのデータをみて、オブジェクトのkeyが無くなるまでループする形を取ります。
「for」文で書いた処理と同じ事を「for in」で書くと下記のようになります。
用途に応じて使い分けると良いです。

// 配列の内容を先頭から最後まで表示する処理

const array_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; // 配列定義

// array_data[0]~array_data[array_data.length(=10) - 1]までの内容を表示
for (const array_pos in array_data) {
  // array_dataのkey(配列番号)がarray_posに入る。
  console.log( array_data[array_pos] );
}

for of 文

for ([変数] in [Object]) {
   [ループを行う処理]
}

「for of」は「for in」とほぼ一緒ですが、動作が異なります。
「for in」では変数にkeyの値が入ったのに対し、
「for of」は変数に実際の値が入ります。
「for」文で書いた処理と同じ事を「for of」で書くと下記のようになります。
こちらも、用途に応じて使い分けると良いです。

// 配列の内容を先頭から最後まで表示する処理

const array_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; // 配列定義

// array_data[0]~array_data[array_data.length(=10) - 1]までの内容を表示
for (const data of array_data) {
  // array_dataの値がdataに入る。
  console.log( data );
}

while文

while ([ループする条件式]) {
   [ループを行う処理]
}

while文はループする条件式が成りっている限りずっとループします。
あまり意味はありませんが、for文の処理をwhile文で書くと下記のようになります。
用途に応じて使い分けると良いです。

// 配列の内容を先頭から最後まで表示する処理

const array_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; // 配列定義

let now_loop_cnt = 0; // ループ回数をカウントする変数
let data_max = array_data.length; //配列の値の数(今回は10)

// array_data[0]~array_data[array_data.length(=10) - 1]までの内容を表示
while (now_loop_cnt < data_max) {
  // [0]~[array_data.length - 1]までの内容を順番に表示
  console.log( array_data[now_loop_cnt] );
  now_loop_cnt++;
}


算術演算子

「if文」で、
checkを10で割った余りを返す乗余の書き方です。(算術演算子で後述)

先ほどの「for文」で「now_loop_cnt++」という表現が出てきて、
(インクリメント(算術演算子で後述))と書いていましたが、
これらは算術演算子の1つとなります。
この算術演算子について記載します。

剰余 (%)
  「x % y」と書いたとき、xをyで割った余りを返します。

インクリメント (++)
  
「x++」と書いた場合、xの値に1を加算して代入します。
   (x = x + 1と同じ)

デクリメント (--)
  
「x--」と書いた場合、xの値に1を引いて代入します。
   (x = x - 1と同じ)

べき乗演算子(**)
  
「y = x ** 3」と書いた場合、xの値を3乗した値をyに代入します。


関数

同じような処理を複数回行うような場合、関数としてその処理を定義すると機能をひとまとめに出来ます。
作り方は2通りあります。

function

旧来型の関数の書き方です。
function 関数名(引き数1, 引き数2, ...)
と書きます。
引き数は無しも出来ます。
また値を返したい場合は、
関数内で 「return [返したい値]」とすれば、値を返すことが出来ます。
※引き数を還して値を返すことも出来ますが、
 値渡し・参照型の説明は難しいためあえてここでは触れません。

function calcurate(data1, data2, mode) {
  let ret;
  switch (mode) {
  case 1:
    ret = data1 + data2;
    break;
  case 2:
    ret = data1 - data2;
    break;
  case 3:
    ret = data1 * data2;
    break;
  case 4:
    ret = data1 / data2;
    break;
  }
  return ret;
}

console.log( calcurate(2, 4, 1) );  // 6
console.log( calcurate(2, 4, 2) );  // -2
console.log( calcurate(2, 4, 3) );  // 8
console.log( calcurate(2, 4, 4) );  // 0.5
console.log( calcurate(2, 4, -1) ); // undefined

ラムダ(アロー関数)式

最近の関数の書き方です。
Cluster Scriptでもこちらの書き方を使用していることが多いです。
上記関数をラムダ式に書くとこのようになります。

const calcurate = (data1, data2, mode) => {
  let ret;
  switch (mode) {
  case 1:
    ret = data1 + data2;
    break;
  case 2:
    ret = data1 - data2;
    break;
  case 3:
    ret = data1 * data2;
    break;
  case 4:
    ret = data1 / data2;
    break;
  }
  return ret;
}

console.log( calcurate(2, 4, 1) );  // 6
console.log( calcurate(2, 4, 2) );  // -2
console.log( calcurate(2, 4, 3) );  // 8
console.log( calcurate(2, 4, 4) );  // 0.5
console.log( calcurate(2, 4, -1) ); // undefined

function calcurate(data1, data2, mode) {

と言う部分が、

const calcurate = (data1, data2, mode) => {

になっただけで、この例だと余り変わらないですが、
ラムダ式で書くと、場合によっては、省略できるような書き方も出来るので、最近はこちらの書き方が主流になりつつあります。
※無名関数などについてはここでは触れません

たとえば、、、

const calc_add = (x, y) => {
  return x + y;
};
console.log( calc_add(2, 4) );  // 6

は、下記のように省略して書くことも出来ます。

const calc_add = (x, y) => x + y;

console.log( calc_add(2, 4) );  // 6

うまく使うと、コードの行数を減らせたり出来るため便利に使うことが出来ます。


型の変換

JavaScriptは、代入する値によって自動的に変化するので、計算時などは代入や計算する変数を意識して扱う必要があります。

数値->文字列変換

いくつかやり方はあります。
代表的なものだけここでは取り上げます。

let num_data = 12.5;

 // 数値->文字列変換
let str_data1 = "" + num_data;
let str_data2 = num_data + "";
let str_data2 = num_data.toString();
let str_data3 = String(num_data);

「toString」は、データ型を定義したときに、プロトタイプとして自動的に実装される文字変換メソッドになります。
このようなものはほかにもいくつかあります。

文字列->数値変換

こちらもいくつかあります。
代表的なものだけここでは取り上げます。

let str_data = "12.5";

 // 数値->文字列変換

// +でやると、文字結合になるので注意(+以外の四則演算なら数値変換になる)
let num_data1 = str_data - 0;
let num_data2 = Number(str_data);
let num_data3 = parseFloat(str_data); // 整数の場合はparseInt

※「Number」や「parseFloat」、「parseInt」は標準組み込みオブジェクトとしてJavaScriptで定義されています。


標準組み込みオブジェクト

先ほど使った、「Number」や「parseFloat」、「parseInt」は標準組み込みオブジェクトと話しましたが、
ClusterScriptでよく使う、「Date」や「Math」も、標準組み込みオブジェクトになります。
他にも定義されているものはあります。
下記URLで確認出来ます。

Math

数学系のメソッドやプロパティを持っているオブジェクトです。
2D/3D計算とかする時によく使います。

Date

日付・時刻を扱うときに使うオブジェクトです。

プロパティ(Properties)とメソッド(Methods)

プロパティとメソッドについては、JavaScriptに限った話ではないのですが、簡単に説明します。(詳しく知りたい方は「オブジェクト指向」で調べると色々説明が出てきます。

プロパティ(Properties)

それ自体が処理するものではなく、値(データ型)を定義しているものをプロパティといいます。

メソッド(Methods)

オブジェクトに定義された、呼び出すことで処理を行うものをメソッドといいます。
ざっくりいうと、「関数=メソッド」と思ってもらうのがはやいです。


以上が、
大体基礎として知っておくと、
ClusterScriptのサンプルなどを見たとき、
内容を把握しやすいと思います。
がんばってください!


Appendix.「$.state」はなぜ必要?

ここまでくると、変数定義などあるのに、「$.state」がなぜ必要なの?
と思う方が出てくると思います。
「$.state」に値を設定すると、インスタンスが存在している限り、必ず値が保持が保証される特別なオブジェクト変数になります。
「$.aaaa = 1」などと、値を入れておくこともできますが、どこかのタイミングでリフレッシュされ定義が消えてしまったりするので、値を保持しておきたい場合は、$.stateに配列やオブジェクト型の値を代入して、値を複数持てるようにしておき管理するようにします。