前提条件
被験者は、
- プログラミング未経験
- 大学学部卒
- 文系
事前課題
入社前に課題をやってもらっています。
- 自己紹介用のHPを作成
- ドットインストールのJS入門
- javascripting-jp
基本方針
対象ブラウザ Google Chrome
ただし、なるべく標準的なプログラムを書きます。
ライブラリを使わない
「ライブラリの使い方を覚える」ことに気をとられないために、ライブラリは使いません。
一度に覚えることが多すぎると、被験者が混乱すると考えました。
以下の仮説に基づいています
- JavaScriptを理解していれば、ライブラリを使うことはライブラリの使い方だけを覚えればよい。
- 「ライブラリの使い方」を覚えると、別のライブラリを使うときにはJavaScriptとライブラリを両方覚える必要がある
環境は指導者に合わせる
- OS X Yosemite
- Atom
- SourceTree
- fish shell
指導者の労力を減らすためと、異なる環境の話が出て被験者が混乱するのを防ぐためです。
事前に設定手順を用意しました。
ただし、キー配置は一般的なものにしました。
指導者のキー配置はDvorakです。学習コストが高すぎるので自重しました。
研修内容
- 主教本 ノンプログラマのためのJavaScriptはじめの一歩
- 補助課題
- 応用演習 TODOアプリ実装
ノンプログラマのためのJavaScriptはじめの一歩
本研修に向く点
- 4章までライブラリ(jQuery)を使わない
- 使用APIにquerySelectorが入っていて、現代的
本研修をに向かない点
基本的な文法の演習が少ない。文法に慣れるために手を動かす演習ができません。
補助課題
手を慣らすために小さな演習を幾つか追加しました。
文法でつまづいたところに追加しています。
ループと条件分岐 1
-
for文で1から10までをconsoleに出力するfor_10.htmlを書く
-
for文の中で偶数のときだけconsoleに出力するfor_even.htmlを書く
偶数を判定するにはモジュロ演算子を使います -
for文の初期値を2、インクリメントを2にして、2から10までをconsoleに出力するfor_even_step.htmlを書く
-
配列['海老名', '座間', '相武台', '小田急相模原', ’相模大野', '町田']の中身とfor文を使って、consoleに出力するfor_char.htmlを書く
-
for文をデクリメントにして、町田から座間をconsoleに出力するfor_decrement.htmlを書く
ループと条件分岐 2 FizzBuzz
1から100までの数値をconsoleに表示します。
ただし、表示する数値は以下の条件で文字列に変更しします。
- 3で割り切れる場合は 「Fizz」
- 5で割り切れる場合は 「Buzz」
- 両者で割り切れる場合は 「Fizz Buzz」
上記のJavaScriptを含むfizzbuzz.htmlを作成する
関数
ブラウザの開発コンソールで動かします。
1. 関数addを作る
- 関数addは引数num1とnum2を受け取る
- 関数addはnum1とnum2の和を返す
- console.logを使ってadd(1, 10)の結果を表示する
2. 関数mulを作る
- 関数mulは引数num1,num2を取る
- 関数mulはnum1とnum2の積を返す
- console.logを使ってmul(2, 3)の結果を表示する
3. 関数を組み合わせる
add、mul、console.logを使って、5 x 7 + 12の結果を表示する
スコープ
ブラウザの開発コンソールで動かします。
グローバル変数
- 変数messageに文字列global helloを代入する
- messageをconsole.logに出力する、showGlobal関数を定義する
- showGlobal関数を実行する
他の関数内の変数
- 変数message2を定義するdefileMessage関数を定義する
- message2をconsole.logに出力する、showMessage2関数を定義する
- showMessage2関数を実行する
※ undefinedが表示されます。
ローカル変数名でグローバル変数名を上書き
-
変数messageに文字列global helloを代入する
-
showLocal関数を定義する。
関数内で変数messageに文字列local helloを代入する
関数内でmessageをconsole.logに出力する -
showLocal関数を実行する
応用演習
ToDoMVCのようなToDo管理アプリケーションを実装します。
機能リスト
- タスクを追加できる
- タスクを完了にできる
- タスクを削除できる
- タスクを変更できる
- タスクを一括完了できる
- 完了タスクを隠す
- ファイルを分割する
- デフォルトタスクを表示する
進め方
gitlabを使いました。git−flow風に進めます。
- 指導者がissueを用意
- 被験者が実装
- 被験者がMerge Requestを作成
- 指導者がレビュー
- 被験者は指摘事項を修正
- 指導者がMerge
全issue
- タスクを追加できる
- Boilerplateを作る
- index.htmlに要素を追加
- タスク追加ボタンを押すと一覧にタスクが追加される
- タスク追加ボタンを押すとinput textが空になる
- input textが空の時はタスク追加ボタンを押せない
- タスクを完了にできる
- 追加したタスクにチェックボックスをつける
- スタイルシートを追加する
- style.cssにタスク一覧のスタイルを追加する
- チェックボックスにチェックを入れたら隣の文字列にdoneクラスを追加する
- チェックボックスにチェックを入れたらタスクの文字列がグレーになり、 打ち消し線が掛かる
- タスクを削除できる
- 追加したタスクに削除ボタンをつける
- 削除ボタンを押すとタスクを削除する
- タスクを変更できる
- タスクをクリックするとpromptでタスク内容が表示される
- promptのOKボタンを押すと入力内容をタスクの内容に反映する
- promptのキャンセルボタンを押した時は、タスクの内容を更新しない
- タスクを一括完了できる
- タスクを一括完了ボタンを追加する
- タスクを一括完了ボタンを押すとすべてのタスクのチェックボックスにチェックが入る
- 完了タスクを隠す
- 完了タスクを隠すチェックボックスを追加する
- doneクラスをliにつける
- 完了タスクを隠すチェックボックスにチェックをいれるとulにactiveOnlyクラスを追加
- スタイルシートで.activeOnly .doneを隠します
- ファイルを分割する
- createTaskElement.jsを作成
- createTaskElement関数を移動
- デフォルトタスクを表示する
- createTaskElement関数の引数にnameを追加
- デフォルトタスクHello Wold!を初期表示
- createTaskElement関数の引数にcheckedを追加
- デフォルトタスク「完了済みタスク」を初期表示
指摘内容
- scriptタグの位置。bodyの最後で読み込む。
- appendChildはなるべく最後に書きましょう。 appendする前に値を変える方が、appendした後に値を変更するより早く動きます。
- ifとelseを対応付けて書く
btn.disabled = false;
if (text.value === "") {
btn.disabled = true;
}
↓
if (text.value === "") {
btn.disabled = true;
} else {
btn.disabled = false;
}
- イベントハンドラーの引数を指定する(chromeではevent変数が参照可能)
function buttonDisabled() {
if (event.target.value === "") {
btn.disabled = true;
} else {
btn.disabled = false;
}
}
↓
function buttonDisabled(event) {
if (event.target.value === "") {
btn.disabled = true;
} else {
btn.disabled = false;
}
}
-
DOMのidを参照しない。document.querySelectorで取得する。
(chromeではid名でDOM要素を参照可能) -
処理を役割に合った関数に移動する
function createTaskElement(){
var li = document.createElement("li");
var text = document.querySelector("#text");
li.innerHTML = '<input type="checkbox">';
var createSpan = document.createElement("span");
createSpan.innerHTML = text.value;
li.appendChild(createSpan);
return li;
}
function buttonClicked(event){
var ul = document.querySelector("#ul");
var task = createTaskElement();
ul.appendChild(task);
var text = document.querySelector("#text");
text.value = "";
event.target.disabled = true;
var checkbox = task.querySelector("input[type='checkbox']"); // この2行
checkbox.addEventListener("change", checkboxChanged, false);
}
↓
function createTaskElement(){
var li = document.createElement("li");
var text = document.querySelector("#text");
li.innerHTML = '<input type="checkbox">';
var createSpan = document.createElement("span");
createSpan.innerHTML = text.value;
li.appendChild(createSpan);
var checkbox = task.querySelector("input[type='checkbox']"); // ここに移動
checkbox.addEventListener("change", checkboxChanged, false);
return li;
}
function buttonClicked(event){
var ul = document.querySelector("#ul");
var task = createTaskElement();
ul.appendChild(task);
var text = document.querySelector("#text");
text.value = "";
event.target.disabled = true;
}
- ブロック間の空行を1行で統一
- 余計なDOM操作を減らす
function spanClicked(event) {
var task = window.prompt(text.innerHTML);
var li = event.target.parentElement;
li.innerHTML = "<input type='checkbox'><span class='span'>" + task + "</span><input type='button' class='deleteBtn' value='削除'>"; // li全体を書き換える必要がない
}
↓
function spanClicked(event) {
var task = window.prompt(text.innerHTML);
var span = event.target
task.innerHTML = task;
}
- 名前の修正。(上記の例でtask -> newTask)
- 関数の引数をDOMから値に変更
var text = document.querySelector("#text");
var task = createTaskElement(text);
↓
var text = document.querySelector("#text");
var task = createTaskElement(text.value);