JavaScriptにおけるカンマ演算子について

今回はJavaScriptにおける演算子の中でもカンマ演算子を取り上げたいと思います。

カンマ演算子は、演算対象を左から右へ評価し、最後に評価した値を返します。

簡単な例を示します。

let name = "john", age = 35, personalInfo;
personalInfo = (name, age);
console.log(personalInfo); // 35

personalInfoには最後に評価された値が代入されています。

もう一つ例を挙げます。

let name = "john", age = 35, personalInfo;
personalInfo = (name, age);
console.log(personalInfo); // 35
personalInfo = name, age
console.log(personalInfo) // john

まず2行目でカンマ,を使っているので最後に演算された値が代入されるはずです。ですが4行目では、最初に評価された値johnが代入されています。 このような結果になるのは、演算子の優先順位が関わっています。

カンマ演算子よりも代入演算子の方が演算子の優先順位が高いため、4行目では、nameが代入されています。括弧()をつけることでカンマ演算子を先に演算するようにしています。

演算子の優先順位についてはこちらを参考にしてみてください。

developer.mozilla.org

簡単ではありますが以上です。

JavaScriptの真・偽とみなされる値について

今回はJavaScriptにおけるとなる値、となる値について説明していきます。

偽とみなされる値

  • null
  • undefined
  • flase
  • 0
  • NaN
  • 空文字('')

上記の値以外は真となりますが、その中でも覚えておきたいものをピックアップしたいと思います。

真とみなされる値

  • オブジェクト
  • 配列
  • 空白のみ
  • 文字列の"false"

まず偽とみなされる値についてコードを書いて検証します。

null ? true : false
// → false

undefined ? true : false
// → false

false ? true : false
// → false

0 ? true : false
// → false

NaN ? true : false
// → false

'' ? true : false
// → false

次に真となる値について検証したいと思います。

const obj = {}
obj ? true : false
// → true

const arr = [];
arr ? true : false
// → true

const spaceString = ' '
spaceString ? true : false
// → true

const bool = "false"
bool ? true : false
// → true

空オブジェクト、空配列のときに偽としたいケースが多いと思います。 そのようなときは、

const obj = {};
console.log(Object.keys(obj).length ? true : false) // false

const arr = [];
console.log(arr.length ? true : false) // false

このように書くといいです。

ちょっとした備忘録でした。

Javascriptのイベントフェーズについて

今回はJavascriptイベントフェーズについて説明していきたいと思います。

そもそもイベントにはどのようなものがあるのか?

イベントの種類

onClick・・・ボタンやリンクをクリックした時

onSubmit・・・フォームの送信ボタンをクリックした時

onKeyDown・・・キーを押した時

onWheel・・・要素のコンテンツをスクロールした時

他にも色々とあります。 気になる方は調べてみてください。

イベントフェーズ

次にイベントが始まって終わるまでのフェーズは全部で3段階あります。 順を追ってみてみましょう。

  1. キャプチャーフェーズ
  2. ターゲットフェーズ
  3. バブルフェーズ

1. キャプチャーフェーズ

ドキュメントルートツリーからイベントの発生する要素へ伝播するフェーズ

2. ターゲットフェーズ

イベントが発生した要素を特定するフェーズ

3. バブルフェーズ

2で発生したイベントがルートまで遡っていくフェーズ

このようにちゃんと順序があるんですね。

javascriptにおいてイベントの発火を検知するのに addEventListenerを使うことが多いと思います。 addEventListenerはさまざまなイベント処理をハンドリングできるメソッドです。

ターゲットとなる要素.addEventListener(type, listener, useCapture);

type:イベントの種類

listener:主に関数

useCapture:親要素などが同じイベントを持っている時に、キャプチャーフェーズでイベントを発生させたい時は、trueを設定します。そうでなければバブルフェーズで親のイベントが実行されます。デフォルトではfalseとなっています。

実際によくあるケースを例にだしてみます。

モーダルの例

お問い合わせのモーダルはあまり見ないですが、このようにモーダルの中にフォームなど記入するコンテンツがある場合です。

f:id:top_men:20180912191008p:plain

よくある仕様としては閉じるボタンを押すか、背景を押した時にモーダルが閉じるというものです。

実際にコードを書いてみます。※cssは省略します

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>イベントフェーズについて</title>
  <link rel="stylesheet" href="main.css">
</head>
<body>
<div class="modal" id="js-modal">
  <div class="modal_content">
    <form class="form" id="js-form">
      <h2 class="form_title">お問い合わせフォーム</h2>
      <div class="form_content">
        <input type="text" name="name" placeholder="名前を記入してください">
      </div>
      <div class="form_content">
        <input type="email" name="" placeholder="emailを入力してください">
      </div>
      <div class="button"><button>送信</button></div>
    </form>
  </div>
</div>
<script src="main.js"></script>
</body>
</html>
const modal = document.getElementById("js-modal");

modal.addEventListener('click', () => {
  modal.classList.add("is-inactive")
};

f:id:top_men:20180912201421g:plain

背景をクリックしてみてください。 モーダルが消えますよね?

次にフォームのコンテンツ部分をクリックしてください。

これもまたモーダルが消えます。

なぜこうなるかは先ほど説明したバブルフェーズが関わってきます。 モーダルのコンテンツの部分のクリックイベントがモーダルの背景の部分まで伝播してしまっているんですよね。

そのイベントの伝播を防いであげればいいですね。

コードの方をちょっと修正します。

const $modal = document.getElementById("js-modal");
const $modalContents = document.getElementById("js-form");

$modal.addEventListener('click', () => {
  $modal.classList.add("is-inactive");
});

$modalContents.addEventListener('click', (event) => {
  event.stopPropagation();
});

event.stopPropagation();で親へのイベントの伝播を防いでいます。

実際にブラウザで検証した結果です。

f:id:top_men:20180912200209g:plain

モーダルのコンテンツの部分をクリックしてもモーダルが消えなくなりました。

まとめ

イベントの伝播の流れを知っておかないと、上のモーダルの件のように原因が突き止めずらくなると思います。イベントはどの場面でも必ず出てくるのでこの機会にちゃんと頭の中に叩き込んでおきます。

JavaScriptのプリミティブ型とオブジェクト型って?

最近写経をはじめました。 そこでの気づきや学びを備忘録としてここに残していきます。 おかしなところがあればご指摘ください。

写経する教材はこちらになります。

www.oreilly.co.jp

今回はJavaScriptプリミティブ型オブジェクト型についてです。

プリミティブ型とオブジェクト型

JavaScriptでの値はプリミティブ型オブジェクト型のふたつになります。 プリミティブという言葉の意味は調べると 『原始的、素朴な、幼稚な』といった意味がでてきます。

具体的にプリミティブを表現するデータ型は6つあります。

  • 数値(Number)
  • 文字列(String)
  • 論理値
  • null
  • undefined
  • シンボル(Symbol)

※シンボルはES2015から新しく導入されました。

プリミティブ型の例

プリミティブ型を判別するためにtypeof演算子を使います。

typeof 123 // 'number'
typeof "hoge" // 'string'
typeof true // 'boolean'
typeof null // 'object'
typeof undefined // 'undefined'

nullの型がobjectとなってしまっています。nullはプリミティブ型です。 これは修正すべきだと提案はされているみたいなのですが、依存するコードが多く、いまだに言語仕様として残ってしまっているみたいです。

一方で以下がオブジェクト型の例です。

プリミティブ型は一つの値しか表現できず、不変です。一方でオブジェクトはプロパティやメソッドなどで値を変化させることができます。

オブジェクト型の例

typeof {} // "object''
typeof [] // "object"
typeof new Date() // "object"
typeof new RegExp() // "object"
typeof new Map() // "object"

そこで自分はふと思いました。

プリミティブ型でもプロパティやメソッド使っているよな?と。

例えば文字列についてです。

const str = "あいうえお";
const strNum  = str.length;
console.log(strNum) // 5 

あれ? プリミティブ型ってメソッドもプロパティもっているじゃん! この疑問を解消してくれるのがラッパーオブジェクトです。

プリミティブ型のデータの中で

  • 数値
  • 文字列
  • 真偽値

などはそれぞれ対応するオブジェクトがあります。

// String
const str = new String("あいうえお");
console.log(str.length); // 5

// Number
const num = new Number(9);
num.toString()
console.log(num) // "9"

// Boolean
const bool = new Boolean(true)
bool.hasOwnProperty() // false

このように初期化されたものは、プリミティブ型を内包したオブジェクトといえます。 このようなオブジェクトのことをラッパーオブジェクトと言います。

お気付きの方もいるかもしれませんが、上の例ではnewをしていないのにも関わらずstr.lengthといった形でlengthメソッドを使い文字数を出力しています。

結論を言うとラッパーオブジェクトへの変換は自動的にしてくれているようです。 プリミティブ型に対応するラッパーオブジェクトのプロパティやメソッドにアクセスすると一時的にラッパーオブジェクトに変換してくれるようです。

まとめ

普段なんとなく使っているプログラムもこのような仕組みを知ることで言語仕様への理解が少し深まりました。他の言語のRubyにはプリミティブ型はないのか?など比較をする基準ができるため今後も写経を続けていこうと思います。

reduxのcomposeが何をしているかちょっと覗いてみた

最近、業務でreactを使用しています。 その中でredux周りのライブラリを使うことが多いです。そんなreduxと仲良くしたいと思い今回はcomposeがどんなことをしているのか見ていきたいと思います。

そもそもcomposeって?

composeは任意の数の関数を引数に受け取りその関数を右から左の順番に実行していきます。 以下のコードではcomposeを使用してapplyMiddlewareでstoreを拡張する方法と、redux-devtoolsからいくつかの開発ツールを使用する例が挙げられています。 (公式サイトから引用)

import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import DevTools from './containers/DevTools'
import reducer from '../reducers'

const store = createStore(
  reducer,
  compose(
    applyMiddleware(thunk),
    DevTools.instrument()
  )
)

実際のcomposeのコード

github.com

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

中身は10行程でとてもシンプルです。

そこで大事なのが最後の行の箇所です。

return funcs.reduce((a, b) => (...args) => a(b(...args)))

高階関数について

上記のコードがどのようになっているかを理解するためには高階関数を知る必要があります。実際に上記のコードと同じようなものを書いてみます。

const first = num => num + 1;
const second = num => num * num;

function compose(f1, f2){
  return (value) => {
    return f1(f2(value))
  }
}

const result = compose(first, second);
console.log(result(3)); // 10

まず引数の値に1足した値を返す関数aと、引数の値を二乗した値を返す関数bを用意しました。 そしてcompose関数を作成しました。この関数の特徴としては以下が挙げられます。

  • 引数に関数を受け取る
  • 戻り値として関数を受け取る

returnしている箇所に注目してみます。

return (value) => {
    return f1(f2(value))
}

f1関数が実行されるとf2関数が実行されます。 f1f2の戻り値を引数にもつので、ここでいうとvalueを2乗した結果が入ります。composeの仕組みとしては今説明したものになります。ですが今のままのコードですとcomposeのように任意の数の引数を受け取ることができず、また引数の数だけ関数を実行することができません。そのためコードを公式サイトと同じようにしていきます。

まず引数の数を固定にせず、呼び出すとき任意の数だけ受け取れるようにします。 上記のコードを修正します。

function compose(...funcs){
  return (value) => {
    return f1(f2(value))
  }
}

こうすることでfuncsには引数に設定した関数の数だけ入った配列が入ってきます。 これで引数に任意の数だけ関数を渡せるようになりました。

次に引数の数だけ関数を実行できるようにします。

function compose(...funcs){
  return funcs.reduce((before, after) => (...args) => before(after(...args)))
}

ポイントはreduceを使うことでfuncsに入っている関数を順に取り出してきて、その中のコールバック関数で右にある関数から順番に実行されていきます。

const first = num => num + 1;

const second = num => num * num;

const third = num => num * num * num

function compose(...funcs){
  return funcs.reduce((before, after) => (...args) => before(after(...args)))
}

const result = compose(first, second, third)(3);
console.log(result);

新たにthird関数を作成してみてcompose関数の引数に三つの関数を渡してみました。 このファイルを実行した結果をみてみます。

720

意図した通りに結果が出力されました。

まとめ

reduxのライブラリを読む上で高階関数やJsのスプレット演算子の使い方などを学ぶことができました。今度はcompose以外のredux周りのライブラリを解読していきたいと思います。

ducksというデザインパターンを使ってTodoListを作ってみた

ducksというデザインパターンを使用して簡単なTodoアプリを作成しようと思います。

f:id:top_men:20180820105137p:plain

機能としては以下になります。

  • タスクを追加できる
  • タスクが終わったかどうかのラベルをつけることができる

環境構築

今回は自分みたいにブログの更新に時間がない方などが手軽にちょちょいと構築できるツールでcreate-react-appを使用します。

以下のコマンドを実行します。

create-react-app ducks_todo

ducksについて

ducksの公式サイトには以下のようなルールが記載してあります。

  • reducerはexport deaultをしなければならない
  • action creatorは関数してexportしなければならない
  • actionは定数で定義する

などが挙げられます。

早速実際にアプリを作成していきましょう。

まずプロジェクトフォルダに移動してください。

cd ducks_todo

必要なパッケージをインストール

redux周りのモジュールをインストール

yarn add redux react-redux

次にmaterial-uiをインストールします。 reactに特化したマテリアルデザインCSSフレームワークです。webサイトでよく見かけるボタンやフォームなどの部品を爆速で作ることができます。

material-ui.com

yarn add @material-ui/core

moduleの作成

ducksの肝となる部分をまず初めに作っていきます。 src/modules以下にtodo.jsを作成します。

/* State */
let nextTodoId = 0;

/* Action */
const ADD_TODO = "ADD_TODO";
const TOGGLE_TODO = "TOGGLE_TODO";

/* Action Creator */
export const addTodo = text => {
  return {
    type: "ADD_TODO",
    id: nextTodoId++,
    text
  };
};
export const toggleTodo = id => {
  return {
    type: "TOGGLE_TODO",
    id
  };
};
/* Reducers */
export default function reducer(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case TOGGLE_TODO:
      return state.map(
        todo =>
          todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
}

actionreducerを同じファイルに配置します。 ducksの公式サイトにも書かれていますが、actionとreducerは密結合な関係であるために同じファイル内にあると何かと見つけやすいですし、ファイルを跨いで確認しなくなるので個人的には良いなと思います。

componentsの作成

src直下にcomponents/Form.jsを作成してください。

import React from "react";
import Button from '@material-ui/core/Button';
export default class Form extends React.Component {
  render() {
    return (
    <form className = "addToto"
      onSubmit = {e => {
        e.preventDefault()
        if (!this.refs.inputText.value.trim()) {
          return
        }
        this.props.addTodo(this.refs.inputText.value)
        this.refs.inputText.value = ""
      }}
     >
      <input type = "text" className = "addToto_input" ref = "inputText" />
      <Button variant = "contained"
        color = "primary"
        style = {{marginLeft: "20px"}}
        onClick = {() => this.props.addTodo(this.refs.inputText.value)}
        >
        Submit
        </Button>
      </form>
    );
  }
}

次にTodoList、Todoコンポーネントを作成します。

import React from "react";
import Todo from "./Todo";
import Card from "@material-ui/core/Card";

export default class TodoList extends React.Component {
  render() {
    return (
      <Card className="todoList">
        <h2 className="todoList_title">TODO</h2>
        <div className="notCompleted">
          {this.props.todos.map(todo => (
            <Todo key={todo.id} toggleTodo={this.props.toggleTodo} {...todo} />
          ))}
        </div>
      </Card>
    );
  }
}
import React from "react";
import Card from "@material-ui/core/Card";
import Checkbox from "@material-ui/core/Checkbox";
import Typography from "@material-ui/core/Typography";

export default class Todo extends React.Component {
  render() {
    return (
      <Card className="todo">
        <div className="todo_header">
          <Checkbox
            tabIndex={-1}
            disableRipple
            onClick={() => this.props.toggleTodo(this.props.id)}
          />
          <Typography
            className={
              this.props.completed ? "completed todo_content" : "todo_content"
            }
          >
            {this.props.text}
          </Typography>
        </div>
        <div className="toggleBtn" />
      </Card>
    );
  }
}

こちらふんだんにmaterial-uiが用意してくれているコンポーネントを使用しています。本当に手軽で使いやすいです。

containerの作成

containerはstoreとcomponentを紐づけるための橋渡し役です。 container/AddTodo.jsを追加してください。

import { connect } from "react-redux";
import * as addTodoModule from "../modules/todo";
import Form from "../components/Form";

const mapDispatchToProps = dispatch => {
  return {
    addTodo: (text) => dispatch(addTodoModule.addTodo(text))
  };
};

export default connect(null, mapDispatchToProps)(Form);

FormコンポーネントにはActionしか利用しないのでconnectの第一引数はnullにするところがポイントです。

次にcontainer/VisibleTodo.jsを作成してください。

import { connect } from "react-redux";
import * as todoListModule from "../modules/todo";
import TodoList from "../components/TodoList";

const mapStateToProps = state => {
  return {
    todos: state.Todo
  };
};

const mapDispatchToProps = dispatch => {
  return {
    toggleTodo: (id) => dispatch(todoListModule.toggleTodo(id))
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList);

TodoListには完了したかどうかのフラグを変更するためのtoggleTodoアクションと、追加したTodoのstateを渡します。

containerを配置するだけの役割のsrc/components/App.jsを作成します。

import React, { Component } from "react";
import "../App.css";
import AddTodo from "../containers/AddTodo";
import VisibleTodoList from "../containers/VisibleTodoList";

class App extends Component {
  render() {
    return (
      <div className="App">
        <AddTodo />
        <VisibleTodoList />
      </div>
    );
  }
}

export default App;

その後ルートのコンポーネントである`index.jsを作成し、そこにApp.jsを配置します。

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./components/App";
import { Provider } from "react-redux";
import configureStore from "./configureStore";

const store = configureStore();

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

AppコンポーネントをProviderでラップしてあげれば完成です。

まとめ

actionとreducerが同じファイル内にあるだけで探す手間が省け、actionのディレクトリが減るので個人的には使いやすいと感じました。 まだunduxというデザインパターンもあるので他のものを試してみたいと思います。

unduxについて

github.com

今回作成したTodoをgithubにあげたのでよかったらcloneして試してみたください。

github.com

z-indexで躓いてしまうこと

今回はz-indexで思い悩んでしまうことについて取り上げてみます。


この記事を書く前までz-indexを指定した値の大きな要素ほど前面に配置されると思っていました。


しかしこれは間違いでした。。。
z-indexのことを知らなすぎました。


それでは何を知らなかったのか、どのような局面で自分は躓いたのかを説明していきます。




html

<div class="white">
  <div class="red"></div>
</div>


css

.white {
  position:relative;
  z-index:1;
  width:60px;
  height:60px;
  background:#fff;
  border:solid 1px #000;
}

.red {
  position:absolute;
  top:10px;
  left:20px;
  z-index:-1;
  width:60px;
  height:60px;
  background:red;
  border:solid 1px #000;
}


あれれ、思った通りの場所に配置されないな汗
.whiteの方がz-indexの値が大きいから前面にくるでしょ?
と、結構思い悩みました。。


z-indexは値の大きさだけで要素が前面に配置されるわけではないのです!
z-indexプロパティは、positionプロパティの初期値(static)以外の値が指定されている要素には、自動的にautoが適用された状態になります。
要するに今回の場合、.redのpositionがabsoluteと指定されているので親要素である.whiteと同じz-index:auto;が自動的に指定されるのです。
しかしこれだけでは.whiteと.redのz-indexの値は同じだけであって何故.redが前面に配置されるかわかりませんよね。。


では他に何か要因があるのでしょうか??


スタック文脈スタックレベルというものが関係している模様!



まずスタック文脈です。


スタック文脈は階層構造のことをいいます。

・ルート要素(HTML)
・位置指定されていて、z-index 値がauto以外の要素
・z-index 値が "auto" 以外の flex アイテム
・1 未満の opacity 値を持つ要素(不透明度の仕様をご覧ください)
・transform 値が "none" 以外の要素
・mix-blend-mode 値が "normal" 以外の要素


上記のようなスタック文脈を形成する条件を満たした要素は、自身を基準としたローカルスタック文脈を形成します。
スタック文脈を形成しない要素はその親のスタック文脈に内包されることになります。



次にスタックレベルは、同一のスタック文脈内での重なり順です。z-indexの値が、同一スタック文脈内でのスタックレベルになります。同じスタック文脈内に、同じスタックレベルの要素がある場合、その構造内でより後方にある要素が前面に配置されます。


少し例を挙げてみたいと思います。


blue
red
yellow
green
black


html

<div class="white_box">
  <div class="blue"></div>//z-index:5;
  <div class="red"></div>//z-index:3;
  <div class="yellow">//z-index:4;
    <div class="green"></div>//z-index:10;
    <div class="black"></div>//z-index:7;
  </div>
</div>


css

.white_box {
  position:relative;
  width:800px;
  height:300px;
}

.blue {
  position:relative;
  top:50px;
  z-index:5;
  width:500px;
  height:200px;
  background:rgba(0, 0, 255, 0.7);
  color:#fff;
}

.red {
  position:absolute;
  top:100px;
  left:50px;
  z-index:4;
  width:500px;
  height:200px;
  background:red;
  color:#fff;
}

.yellow{
  position:absolute;
  top:30px;
  left:100px;
  z-index:3;
  width:300px;
  height:100px;
  background:rgba(255, 255, 0, 0.8);
  color:#fff;
}

.green {
  position:absolute;
  top:40px;
  left:200px;
  z-index:10;
  width:250px;
  height:80px;
  background:green;
  color:#fff;
}

.black {
  position:absolute;
  top:70px;
  left:100px;
  z-index:7;
  width:150px;
  height:80px;
  background:black;
  color:#fff;
}


まず黒色のボーダーで囲まれた箱の中に色がついた箱が合計5個あり、その箱すべてにz-indexの値をつけました。
これらはスタック文脈が形成されています。
blue,yellow,redの箱は同一のスタック文脈の中にあるのでスタックレベルで前面か、後方に配置されるかが決まります。
z-indexの値が大きいほど前面に配置されます。
z-indexが定められていない要素が複数ある場合は下に記述した要素の方が前面に表示されます。


そして注目してほしいのはyellowの中にある要素.greenと.backです。
どちらも.blue,redの箱よりもz-indexの値が大きいにも関わらずblueより前面に配置されません。


これは先ほど説明した通りスタック文脈の中にある要素はそのスタック文脈の外にでることができません
したがって.blueやredの箱よりも後方に配置されるのです。



このようにz-indexを扱う上ではスタック文脈、スタックレベルまたpositionの知識が必要とされます。
知らなかった人は実践してもう一度確かめてください!!