JavaScript

概要

JavaScriptはブラウザ上で動作するのを特徴とするProgramming Language。WEB開発のフロントエンドに多用される。多くのフレームワーク、ライブラリが存在していて、活発に開発が進んでいる。

古い本だと書き方が古くなってたり最悪動かなかったりする。最新のコードを読んでもよくわからない、ということも多い。書籍を買うときは発行年度を確かめることが必要。以前jsやってたのでReact,TypeScriptにいって大丈夫だろう、は罠。学ぶ前に最新のjsをキャッチアップしておかないと、理解しにくい。

Memo

新しく追加された構文などもあって、ややこしい。

npxでライブラリの場所を探す

どこに実行ファイルがあるのか知りたいことがある。

npx which textlint

home/silver/roam/node_modules.bin/textlint

文字コードの仕組み

パラメータを圧縮する。

  • 元データ → 圧縮 → base64エンコード(圧縮によってURLに使えない文字が含まれるため) → URL表示形式

パラメータを解凍する。

  • URL表示形式 → base64デコード → 解凍 → 元データ

プロジェクトすべてにprettierをかける

prettierは書式を整形しプロジェクトで一貫性をもたせられるツール。

npx prettier --write "**/*.js"

インストールしてるのに開発用サーバでライブラリのエラーが出るとき

npm install などでライブラリを追加したあとは、 npm start を再起動する必要がある。

create-react-appでエラーが出るとき

create-react-appを実行するとき、このようなエラーがでるとき。

“You are running create-react-app 4.0.3 which is behind the latest release (5.0.0)”

npx clear-npx-cache
npx create-react-app@latest my-app

関数の合成

関数型プログラミングでは小さい関数を定義する。 それらを合成して、目的を達成する。

  • 返り値を工夫してメソッドチェーンにする。

replace は引数、返り値が文字列なので…。

const template = "hh:mm:ss tt";
const clockTime = template
      .replace("hh", "03")
      .replace("mm", "33")
      .replace("ss", "33")
      .replace("tt", "PM");
console.log(clockTime);

といったことができる。

関数を集約させるところに注目。 compose で非破壊の関数をまとめて順番に評価している。

const oneSecond = () => 1000;
const getCurrentTime = () => new Date();
const clear = () => console.clear();
const log = message => console.log(message);

const serializeClockTime = date => ({
    hours: date.getHours(),
    minutes: date.getMinutes(),
    seconds: date.getSeconds()
});

const civilianHours = clockTime => ({
    ...clockTime,
    hours: clockTime.hours > 12 ? clockTime.hours - 12 : clockTime.hours
});

const appendAMPM = clockTime => ({
    ...clockTime,
    ampm: clockTime.hours >= 12 ? "PM" : "AM"
})

const display = target => time => target(time);

const formatClock = format => time =>
      format
      .replace("hh", time.hours)
      .replace("mm", time.minutes)
      .replace("ss", time.seconds)
      .replace("tt", time.ampm);

const prependZero = key => clockTime => ({
    ...clockTime,
    [key]: clockTime[key] < 10 ? "0" + clockTime[key] : "" + clockTime[key]
});

const compose = (...fns) => arg =>
      fns.reduce((conposed, f) => f(composed), arg);
// reduceの第一引数はコールバック関数、第二引数は初期値。
// 配列内の最初の関数は引数argで呼び出され、それ以降は前の関数の戻り値が次の関数の引数として渡される。
// 配列内の関数は順に呼び出され、最後の関数の戻り値が最終的に戻り値として返される。

const convertToCivilianTime = clockTime =>
      compose(
          appendAMPM,
          civilianHours
      )(clockTime);

const doubleDigits = civilianTime =>
      compose(
          prependZero("hours"),
          prependZero("minutes"),
          prependZero("seconds")
      )(civilianTime);

const startTicking = () =>
      setInterval(
          compose(
              clear,
              getCurrentTime,
              serializeClockTime,
              convertToCivilianTime,
              doubleDigits,
              formatClock("hh:mm:ss tt"),
              display(log)
          ),
          oneSecond()
      );

startTicking();

再帰

自身を呼び出す。非同期処理と組み合わさったときに真価を発揮する。 また、データ構造の探索に用いられる。

const countdown = (value, fn) => {
    fn(value);
    return value > 0 ? countdown(value - 1, fn) : value;
};

countdown(10, value => console.log(value));

高階関数/カリー化

  • userLogs 関数を定義。
  • userLogs に引数を渡して logs 関数を定義。
  • logs 関数に値を渡す。すべてのコンソールの先頭に“grandpa23”が出る。
const userLogs = userName => message =>
      console.log(`${userName} -> ${message}`);
const log = userLogs("grandpa23");
log("attempted to load 20 fake members");
getFakeMembers(20).then(
    members => log(`successfully loaded ${members.length} members`)
).catch(
    error => log("encountered an error loading members")
);
// grandpa23 -> attempted to load 20 fake members
// grandpa23 -> successfully loaded 20 members
// grandpa23 -> attempted to load 20 fake members
// grandpa23 -> encountered an error loading members

reduce

配列を単一の値へ変換する。

const ages = [21, 18, 42, 40, 64, 63, 34];
const maxAge = ages.reduce((max, age) => {
    console.log(`${age} > ${max} = ${age > max}`);
    if (age > max) {
        return age;
    } else {
        return max;
    }
}, 0);

console.log("maxAge", maxAge);

// 21 > 0 = true
// 18 > 21 = false
// 42 > 21 = true
// 40 > 42 = false
// 64 > 42 = true
// 63 > 64 = false
// 34 > 64 = false
// maxAge 64

命令形 vs. 宣言型

関数型プログラミングは、宣言型プログラミングというより大きなプログラミングパラダイムの 一部です。宣言型プログラミングにおいては、「何をするのか」(what)が重要で、「どのようにす るのか」(how)は重要ではありません。ひたすらwhat を記述することでアプリケーションを構築 するプログラミングのスタイルを宣言型と呼びます。 一方で、従来のプログラミング言語は、結果を得るための手順(how)を記述します。これを命 令型プログラミングと呼びます。

export

単一のオブジェクトをエクスポートする場合は、 export default と記述する。

export default new Expedition("Mt. Freel", 2, ["water", "snack"]);

インポート側。

import freel from "./mt-freel"

エクスポートするときの default キーワードによって、インポートの記述が変わる。

commonJSでは require 関数を使う。

async

async 関数は、非同期関数を同期関数のように呼び出せる。

エラー処理。

const getFakePerson = async () => {
    try {
        const res = await fetch("https://api.randomuser.me/?nat=US&results=1");
        const { results } = await res.json();
        console.log(results);
    } catch (error) {
        console.error(error);
    }
};

単に関数呼び出しを囲うだけでいい。

promise method

非同期のアクセス処理を行う関数。 アクセスに時間がかかる間待ってるのは無駄なので、平行処理させる。

fetch("https://google.com") // promiseオブジェクトが返ってくるだけ

fetch("https://google.com").then(res =>
    res.json() // 非同期処理した結果が返ってくる
)
fetch("https://google.com")
  .then(res => res.json())
  .then(json => json.results)
  .then(console.log)
  .catch(console.error);

spread syntax

... : スプレッド構文。いくつかの用途がある。

  • 配列の連結に使用される。
const peaks = ["Tallac", "Ralston", "Rose"];
const canyons = ["Ward", "Blackwood"];
const tahoe = [...peaks, ...canyons];

console.log(tahoe.join(", ")); // Tallac, Ralston, Rose, Ward, Blackwood
  • 配列のコピー作成(イミュータブル)
const peaks = ["Tallac", "Ralton", "Rose"];
const [last] = [...peaks].reverse(); // peaksがreverse()で破壊するのを防ぐため、コピーを作成する。そして配列の最初の要素をlastに代入する。

console.log(last); // Rose
console.log(peaks.join(", ")); // Tallac, Ralston, Rose => 破壊してない
  • 配列の「残り全部」を表現する
const num = [1, 2, 3]
const [first, ...others] = nums;
console.log(others.join(", ")); // 2, 3
  • 関数の引数を配列として受け取る
funciton directions(...args) {
    let [start, ...remaining] = args;
    let [finish, ...stops] = remaining.reverse();

    args.length
    start
    finish
    stops.length
}

directions("Truckee", "Tahoe City", "Sunnyside") // 任意の引数を受け取れる
  • オブジェクトの連結

    const morning = {
      breakfast: "oatmeal",
      lunch: "peanut butter and jelly"
    };
    
    const dinner = "mac and cheese";
    const backpackingMeals = {
      ...morning,
      dinner
    };
    
    console.log(backpackingMeals);
    
    // {
    //     breakfast: "oatmeals",
    //     lunch: "peanut butter and jelly",
    //     dinner: "mac and cheese"
    // }
    

object literal

変数をオブジェクトのプロパティ名として記述する場合、プロパティ名を省略できる。

const name = "Alice";
const age = 9;

const person = { name, age };
console.log(person); // { name: "Alice", age: 9 }

関数もオブジェクトリテラル内に記述することが可能。

const name = "Alice";
const age = 9;
const print = function() {
  console.log(`${this.name}` is `${this.age} years old.`)
}

const person = { name, age, print };
person.print(); // "Alice is 9 years old."

オブジェクトリテラル内に関数を記述する際に、functionキーワードを省略できる。

const skier = {
    name,
    sound,
    powderYell() {
        let yell = this.sound.toUpperCase();
        console.log(`${yell} ${yell} ${yell}`);
    },
    speed(mph) {
        this.speed = mph;
        console.log("speed:", mph);
    }
}

destructuring

必要なプロパティのみを取捨選択して、代入できる機能。

const aaa = {
    bread: "aaa",
    meat: "bbb",
    cheese: "ccc"
};

const { bread, meat } = sandwitch // オブジェクトから必要なプロパティのみを取捨選択する
sandwitch // => aaa, bbb

読み飛ばすことも可能。必要な要素のみを取得することをリストマッチングという。

const [, , thirdAnimal] = ["Horse", "Mouse", "Cat"];

console.log(thirdAnimal); // Cat

arrow function

const aaa = (arg) => `return_value: ${arg}`;

戻り値のオブジェクトを括弧で囲む必要がある。

const aaa = (firstName) => ({
    first: firstName,
    last: lastName
})

アロー関数は独自のスコープを持たないので、アロー関数の外側と内側でスコープが保持される。

巻き上げ

関数式は変数宣言の前で呼び出せない。

aaa() // これは呼び出せない

const = aaa = function() {
  "aaa"
}

関数宣言は宣言の前で呼び出すことができる。(巻き上げ)

bbb() // これは呼び出せる

function bbb() {
  "bbb"
  }

node

フルスタックアプリケーションを構築するために設計されたJavaScript実行環境。

npm

Node.jsのパッケージ管理システム。 package.jsonで管理する。

npm init
npm init -y # デフォルトの設定を使用する

yarn

Node.jsのパッケージ管理システム。 当時npmではできなかった依存パッケージのバージョン固定化のために実装された。

バージョンを無視してインストール

yarn install --ignore-engines

スプレッド演算子

...state といった記法。中身を展開するスプレッド演算子。 reactjs - What does the three dots notation do in Javascript? - Stack Overflow

↓2つは同じ意味。

<Modal {...this.props} title='Modal heading' animation={false}>
<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>

特徴

JavaScriptのオブジェクトとクラス - サバイバルTypeScript-TypeScript入門

JavaScriptの特徴はオブジェクトリテラル{}という記法を用いて、簡単にオブジェクトを生成できる点です。

JavaやPHPなどの言語では、オブジェクトを生成するにはまずクラスを定義し、そのクラスを元にインスタンスを作るのが普通ですが、JavaScriptはクラス定義がなくてもこのようにオブジェクトリテラルを書くと、オブジェクトをインラインで作れます。 オブジェクトリテラルがあるおかげで、JavaScriptでは自由度の高いコードが書けるようになっています。

// 空っぽのオブジェクトを生成
const object = {};

// プロパティを指定しながらオブジェクトを生成
const person = { name: "Bob", age: 25 };
メソッド
オブジェクトに関連づいた関数のことです。
const object = {
  // キーと値に分けて書いたメソッド定義
  printHello1: function () {
    console.log("Hello");
  },
  // 短い構文を用いたメソッド定義
  printHello2() {
    console.log("Hello");
  },
};

JavaやPHPでは、オブジェクトのフィールドとメソッドははっきり区別されます。 一方、JavaScriptではその区別はきっちりしていません。Javaで言うところの メソッドとフィールドは、JavaScriptでは同じように扱われます。たとえば、 メソッドにnullを代入することで、フィールドに変えてしまうこともできます。

このようにclassでクラスを定義し、newでインスタンスを生成するスタイルは、 JavaやPHP、Rubyなどと使用感がよく似ています。

JavaScriptのクラスの特徴は、クラスもオブジェクトの一種というところです。 オブジェクトとは、プロパティの集合体だと前述しましたが、クラスもオブジェ クトなのでプロパティの集合体としての性質を持ちます。したがって、定義し たクラスはプロパティを追加したり、変更したりできます。

const myObject = {};
myObject.key = "value"; // プロパティを追加class MyClass {}
MyClass.key = "value"; // プロパティを追加

ほかの言語ではクラスを後から変更できない。

interface Person {
  name: string;
  age: number;
}

インターフェースでの型ガードを自前で実装する必要がある。

type UnKnownObject<T extends object> = {
  [P in keyof T]: unknown;
};

function isStudent(obj: unknown): obj is Student {
  if (typeof obj !== 'object') {
    return false;
  }
  if (obj === null) {
    return false;
  }
  const {
    name,
    age,
    grade
  } = obj as UnKnownObject<Student>;
  if (typeof name !== 'string') {
    return false;
  }
  if (typeof age !== 'number') {
    return false;
  }
  if (typeof grade !== 'number') {
    return false;
  }
  return true;
}

配列

  • 配列を処理するときはやりたいことに応じて関数を選択します。するとやりたいことが明確になるのでわかりやすくもなります。除去したときは`filter()`、新しい配列を作るときは`map()`という感じです。

コールバック関数

  • コールバック関数: 他の関数に引数として渡す関数のことです。

JavaScriptの「コールバック関数」とは一体なんなのか

Eslint無視の方法

"rules": {
    "comma-dangle": "error",
    "require-yield": "error",
    "no-unused-vars": "off",
    "no-undef": "off",
},

などと書きます。

クラスを使わずにモジュール分割する

var Msg = (function() {
  function test() {
  }
}
Msg.test()

命名の意味

  • _ で始まるのはプライベートメソッドです。言語によっては未使用変数ということもあります。

関数宣言と関数式

2つの関数宣言の方法があります。

// 関数宣言
function hello() {
  return "hello";
}
// 関数式で関数を定義
let hello = function () {
    return "hello";
}; //←セミコロン

2つの関数式の書き方

関数式には2つの書き方があります。

// function式を用いた関数式
const hello = function (name) {
  return `Hello, ${name}!`;
};

// アロー関数の関数式
const hello = (name) => {
  return `Hello, ${name}!`;
};

短く書けるのでアロー関数が好まれます。

NodeList

  • NodeList - https://developer.mozilla.org/ja/docs/Web/API/NodeList
  • text node: タグに囲まれたもの。 <p>これ</p>。
  • element node: <p>や、</p>
  • DOMツリー: ブラウザがアクセスしてHTMLを解析すると文書の内容を表すオブジェクトのツリー構造が構築されます。これがDOMツリーです。DOMツリーを形成する1つ1つのオブジェクトがノードです。さらに子のオブジェクトを持っている可能性があります。

https://qiita.com/KDE_SPACE/items/e21bb31dd4d9c162c4a6

package.json

パッケージを理解するために不可欠です。

scripts

makeのようなもの。

npm run <タスク名>

で実行できる。

"scripts": {
    "build": "NODE_ENV=production babel src --out-dir lib --source-maps",
    "watch": "babel src --out-dir lib --watch --source-maps",
    "prepublish": "npm run --if-present build",
    "test": "mocha --require @babel/register test/*test.js"
}

files

npm installされたときに展開されるファイル、実行されるファイルを指定する。

"main": "lib/index.js",
"files": [
    "lib",
    "src"
],

srcをコンパイルしてlibに格納、パッケージとしてはこちらを使用する、みたいなことを指定する。はず。

GitHubリポジトリから読み込むとき

npm install git@github.com:kijimaD/textlint-plugin-org#develop

.babelrc

babelの設定。presetは使うbabelと合わせる必要がある。

{
  "presets": [
      "@babel/preset-env"
  ],
  "env": {
    "development": {
      "presets": [
          "jsdoc-to-assert",
          "power-assert"
      ]
    }
  }
}

.eslintrc

extends

事前にnode i airbnb-baseしておいて .eslintrcに追加するとルールセットを追加できる。

"extends": "airbnb-base",

パッケージ関連の言葉

ややこしい。

npm パッケージマネージャ

package.jsonに書かれた設定でいろいろ実行。 パッケージインストール、アップデート、タスク実行、などいろいろ。 パッケージをインストールするときには、これが実行されてコンパイルされて使える状態になってたりする。

npm i

パッケージ内のコマンドを実行する。おそらくbundle execとかと同じ。

npx lint

node 実行環境

node # repl起動
node -v

本体をバージョンアップしても反映されないときは実行場所を確認する。 nでバージョンしても、読んでるのはnvmだったりする。.bash_profileなどを確認。

便利コマンド

ファイルに更新があったらテスト実行する。超便利。

npm test -- --watch

npm publishのやりかた

  • npm publishはローカルのファイルをアップロードして公開するので、ブランチ、 git status に気をつける。
# タグ付けとコミットを行う。package.jsonも更新される。
npm login
npm version [ major | minor | patch ]
npm publish
git push --follow-tags

Tasks

TODO ECMAScript · JavaScript Primer #jsprimer

ECMAScriptの仕様策定プロセス。

TODO ネストしたコードの解消方法

やたらネストして大きな関数になりがち。 どうやって解消すればよいのだろう。

TODO 実際にinstallしてCIで確かめる

プラグイン本体でテストしてるが、実際にnpm installするわけではないので依存パッケージ検知できないことがある。 ローカルでクリーンインストールでもglobalにインストールしてたりしてるので検知できないのだろう。

References

Webで3Dモデルを扱いたい |

WebGLによる3Dモデルの概要。

この書籍について · JavaScript Plugin Architecture

プラグインのアーキテクチャに関する本。