トップ «前の日記(2018-05-03(Thu)) 最新 次の日記(2018-08-01(Wed))» 編集

K2さんの雑記


2018-05-04(Fri) [長年日記]

引き続きJavaScript

二日目。atスターバックス。

Array.map

「配列の内容を順に加工」なので、破壊的メソッドかと思ったら、そうではないようだ。

配列を本当に加工するには、

var ary = [1, 2, 3, 4];

ary = ary.map(function(value, index, array) {
  return value * value;
});

としてやればよいみたい。

コールバック関数の第三引数は使えるんだろうか。

var ary = [1, 2, 3, 4];

ary.map(function(value, index, array) {
  return array[index] = value * value;
});

でもOKのようだ。ということはforEachを使っても同じかな。

var ary = [1, 2, 3, 4];

ary.forEach(function(value, index, array) {
  array[index] = value * value;
});

これもOK。

でもなんかスマートじゃないな…… 破壊的な同様のメソッドはないのだろうか。

Array.sort

これは破壊的メソッド。

Array.sort(function(a, b){
  return a > b;
});

コールバック関数がtrueを返すなら、aが後ろに配置される。

普通そうだっけ? 逆のイメージ(aが前に配置される)が感覚としてあるが、たぶん私の気のせいだろう。

Map

ES2015からMapオブジェクトが追加された。

ここを参照すると、

  1. 連想配列は、ObjectとMap、両方が使える
  2. Objectは、たまたま連想配列として使えるから使われているだけで、JavaScriptの実装としては、連想配列として用意されたわけではない
  3. Mapは連想配列として用意されたが、連想配列として欠点がある

やはり、歴史的背景から、オブジェクト、連想配列あたりはややこしいことになっているらしい。

なぜ、ここを疑問に思ったかというと、Mapのサンプルコードを書いているときに、 for-inが動作しない現象につきあたったから。

// mapオブジェクトに値を追加
let m = new Map();
m.set('dog', 'ワンワン');
m.set('cat', 'ニャー');
m.set('mouse', 'チュー');

//キーを順に取得
for (var key in m){
    console.log(key + '=' + m[key]);
}

for-ofなら動作する。またObjectの連想配列なら動作するのだが、上記コードでは、for内部を一度も通らないようだ。これはなぜだろう。

for-inはいろいろ難しいことがありそうなので、まだ近寄るには早すぎると言うことなのだろうか。多くのスクリプト言語では、よく使うのはfor-inなんだけどなぁ。

追記

for-ofは、ES2015から使用可能。ということは昔から使えたのはfor-in。ということはfor-inを使うべきなのか。なんかこの辺(連想配列、forによる列挙)は癖が強そうだ。

追記2

for-ofは、Objectの連想配列には使えないようだ。is not iterableのエラーが出る。

この辺は非常にややこしそうだ。

  1. 連想配列にはObjectを使う
  2. 連想配列の列挙にはfor-inを使う
  3. Arrayの列挙には、prototype拡張等をしない場合はfor-in、トラブった場合はfor-ofを使う

とすれば、とりあえずはしのげるか。

追記3

教科書でも、オブジェクトリテラル(Objectを使った連想配列をこう呼んでいるようだ)とMapの違いについて言及があるが、ここを読み解くにはプログラム言語初心者にはかなり難しいと思われる。

基本的に、参照型(orポインタ)を理解していないと、この手の理屈の裏面は理解できないということなんだけど、最近の言語を知っているだけでは、ポインタや参照型の本当の理解にはなかなか到達しないと思われるので。コンピュータを本当の意味で勉強するには、今でもC言語の理解(特にポインタ)が必須なのかもしれない。

追記4

オブジェクトの連想配列でも、for-ofが使えた。以下のObjectの静的メソッドを使う。

var data = {dog: 'ワンワン', cat: 'ニャー', mouse: 'チュー'};
for (var key of Object.keys(data)){
    console.log(key + '=' + data[key]);
}

data.keys()としてもうまくいかない。

Dateオブジェクト

特に難しいところは一つを除いては特になさそう。

その一つとは、Monthを単独で扱う場合は、1差し引いた値で扱わないとならないところ!

getMonth()メソッドで得られたNumberは、12月なら11。setMonth(7)なら、8月がセットされる。これは間違いやすそうだ。Date(日)はそんなことはなく、単にNuberと日は同じ値になる。なぜ?としか言えない。

なお、setDate()では、負の値や0もセットできる。ちなみに0をセットすると、前月の最終日を指定することになるようだ。

RegExpオブジェクト

言語仕様として正規表現が組み込まれているのはうれしい。

正規表現を知っている人は問題ないが、この本で始めて触れる人は、この本では正規表現には入門できないと思われる(ほとんど解説がないに等しいので)。参照本として傷害されているのは、オライリーの詳説 正規表現 第3版ということで、これまたスパルタンな仕様ですね(笑)。

正規表現のサブマッチ文字列

正規表現の中で、()でグルーピングされた部分にマッチした文字列を取得するのは、検索結果として返された配列のインデックス1以上の要素を使う。ただし、String.match()を使う場合でglobalオプション(g)が付いた場合には、結果の配列には、すべてのマッチした文字列が複数入っているため、サブマッチ文字列は取得できなくなる。

RegExp.exec()メソッドの場合は、グローバルオプションが付いていても、一回の検索では一つだけマッチし、サブマッチ文字列にもアクセスできる。複数回の検索結果を参照するには、結果がnullになるまでexec()メソッドを繰り返す。

rubyなどでは、サブマッチ文字列に$1などの表現でアクセスできるのだが、JavaScriptでは、そこまで言語仕様に密に組み込まれたものではない。正規表現ライブラリを使用する感覚ですね。

ただし、正規表現リテラルは存在するので、正規表現の記述は、//で囲むだけでよい。

追記

String.replace()メソッドでは、置き換える文字列表現の中で、$1等のサブマッチ文字列表現が使用できる。

ただし、$0(マッチ文字列全体)の表現は使えないので、マッチ文字列全体が必要な場合は、検索正規表現全体を()でグルーピングして、$1をマッチ文字列全体のサブマッチ文字列にしてやる工夫が必要。

これはあまり美しくないなぁ。

なお、String.replace()やspilit()メソッドでは、検索文字列の表現には通常文字列と正規表現とどちらでも使える。これはよい仕様。これはString.match()等でも同様のようだ。

やっと関数

教科書38%。1日目の進捗から考えるとGW中に全部読めるかと予想していたのだが、ちょっと無理そう。後半に行くほど難易度も上がっていくだろうし。

なかなか面白いのでこのまま続けられるとよいな。

関数定義

function hoge([引数リスト]) {
  ...
}

と書く。functionという命令が必要なのは、C言語とは違う。

swiftはfuncを使う。rubyやpythonはdef、pascalはfunction/procedure。

型宣言は必要なし。返り値があるかないかは、本体にreturnがあればあり、なければ返り値はundefinedとなる。

関数リテラル

こんな関数宣言もある。

var getTriangle = function(base, height) {
  return base * height / 2;
};

function(...){...}で名前のない関数を定義し、変数getTriangleに代入する。これを匿名関数、無名関数と呼ぶ。

Array.sort()等で使ったコールバック関数も、サンプルはこの形で書いた。もっともこの場合は、定義部分のみで、変数代入はしていないが。

Array.sort(function(a, b){
  return a > b;
});
アロー関数

ネットでJavaScriptのコードを見ていて意味不明な=>を見たことがあったが、これか!

関数定義を以下のようにできる。

   //アロー関数による関数定義
   let getTriangle4 = (base, height) => {
       return base * height / 2;
   }
   console.log('三角形の面積:' + getTriangle4(5, 2));

   //アロー関数で、関数定義が1行の場合は、中括弧を省略できる。
   //中括弧省略の場合は、文の返り値がそのまま関数返り値になる。
   let getTriangle5 = (base, height) => base * height / 2;
   console.log('三角形の面積:' + getTriangle5(5, 2));

   //アロー関数で、関数の引数が1個の場合は、括弧を省略できる。
   let getCircle = radius => Math.PI * radius * radius;
   console.log('円の面積:' + getCircle(2));

   //関数の引数がない場合は、括弧を省略できない。
   let show = () => console.log('Hello, world!');
   show();

これらは、初見でぱっと見ても何してるか全くわからないだろうな。

こう書く便利さは、まだよくわからない。

関数定義でreturnの直後の改行はNG
return
  base * height / 2;

と書くと、関数の返値はundefinedとなってしまう。returnの後ろに;が自動的に補われるかららしい。

return base
  * height / 2;

などのように、returnの直後で無ければ改行はOKのようだ。(Chromeで確認)。

function命令を使った関数宣言は、ユニットのどこに置いてもよい

通常言語の関数宣言と同様、function命令を使った関数宣言は、実際に関数が使われる後に置かれていても実行できる。

関数リテラルを変数に代入する方法では、使う前に変数宣言ができていないとエラーになる。

本日も終わり

本全体の半分くらいまで終了。

JavaScriptの基本的なところはだいたいわかったが、まだクラス宣言とかは見ていない。

複数ユニットでなる大規模プログラムはどう書くのかとかもよくわからない。これはhtmlファイルでのscriptタグとの複合技になるのかな。


1965|09|
2002|09|10|11|12|
2003|01|02|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|03|04|05|08|
2014|01|02|03|04|05|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|11|
2017|01|03|05|06|07|08|10|11|12|
2018|01|02|03|04|05|08|09|10|12|
2020|01|07|




2018年
5月
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
//
自己紹介
自己紹介
広告
計るだけダイエット
つっこみリスト
TrackBacks
日記仲間
/ / /
最近の日記