Javascriptのここが少しムズイ8選

はじめに

この記事はJavascript関連の技術を勉強していて,ここが少しムズイなと個人的に思ったことを記載します.基本的にはネットに乗っていることが多いので,この記事は私が勉強したという備忘録です.内容的には,Javascriptとnpmとnode関連のことを記載します.

this

thisはモジュール内で定義した変数を指し示すときによく使います.

class Hoge {
  constructor(name) {
    this.name = name;
    console.log(this.name) // nameが出力
}

console.log(this) //window.

個人的にthisの覚え方として,一個前のものを指し示すという感じで覚えています.上の例であると,thisはコンストラクターの一個前のHogeをさします.何も書かれていない場所で,thisを記載するとwindowの内容が現れます.

class Hoge {
  constructor(name) {
    this.name = name;
    setTimeout(function(){
        console.log(this) //window
    }, 1000);
  }
}

上の例であると,windowの内容が現れます.先ほども言った通り,thisは一個前のものを指し示すので,setTimeoutが指し示されます.setTimeoutはwindowのオブジェクトなので,thisはwindowをさします.

class Hoge {
  constructor(name) {
    this.name = name;
    setTimeout(function(){
        console.log(this) //bindを入れると,Hogeのクラス情報が出力.
    }.bind(this), 1000);
  }
}

setTimeoutや他のWindowObjectの中で,コンストラクターや他の関数で定義した内容を入れたい場合は,bindを使います.bindは関数の中に〇〇というオブジェクトの内容を入れたい!と思ったときに使えばいいかと思っています.とはいえ,使い方は以下の通りです.

function名.bind(thisに対応する変数[, 引数に対応する値...])

面白い例に以下があります.

class Hoge {
  constructor(name) {
    this.name = name;
    const word = "hoge";
    setTimeout(
      function () {
        console.log(this); //hoge
      }.bind(word),
      1000
    );
  }
}

これで出力されるのはwordで格納されたhogeとなります.bindはthisになりかわるものであることがわかります.thisの記載方法は他にアロー関数がありますが,他のセクションで扱います.

参考:

developer.mozilla.org

techacademy.jp

vueの記載の方法ですが,面白い内容です. tadaken3.hatenablog.jp

分割代入

バラバラの変数で受け取りたいときに使います.

const obj = { A: 1, B: 2, C: 3 };
const { A, B } = obj;
console.log(A); //=> 1 value側が取得できる.
console.log(B); //=> 2
console.log(b) ; //=> undefined

受け取りたいものがオブジェクトであったとき,key側の変数を指定することで,valueの値を取得できます.この書き方であると記載が完結になるとともに,見やすくなります.また,一括に一つの変数に格納したい場合は以下のように記載する.

const obj = { A: 1, B: 2, C: 3 };
const { A, B } = obj;
const C = { ...obj };
console.log(C); // { A: 1, B: 2, C: 3 }

...と3個繋げて,入れてやると全部代入されます.フレームワークであると,gulpやvueStoreで同じような記載が見られるます.

const { src, dest } = require("gulp");
// const gulp = require("gulp")
// gulp.srcと一緒.gulp.dest

gulpであると上記のようにrequireでモジュールを格納し,{src,dest}で受け取る方法がある.一方で,gulp.srcで記載する方法があります.ネットではこのような書き方が見受けられたので載せておきます.一緒です.

actions: {
  increment ({ commit }) { //分割代入
    commit('increment')
  }
 increment (context) { //これでも一緒の書き方です
    context.commit('increment')
  }
}

vue storeのaction内で第一引数に受け取るものが,contextです.その中に{commit,dispatch,}やらが入っているので,それを省略して取得するために,上記のような書き方で省略して使います.

参考:

developer.mozilla.org

qiita.com

import/export

import/exportとexports/requireの書き方があります.やっていることはほぼ同じなのに二つのやり方で外部モジュールを読み込むことができます.さて,このセクションではimport/exportです.これは,ES2015(ES6)での書き方です.nodeとか関係なく使うことができます.

//hoge.js
class Hoge {
  constructor(name) {
    this.name = name;
  }
}


export default  Hoge; //defaultを使う


//index.js
import Hoge from "./hoge.js";
const hoge = new Hoge("hogehoge");

exportしたモジュールはimport文にて受け取ることができます.defaultはその名の通り,importしたとき基本的にはこれが呼び出されるぞ!!というもの.そのため,from ./hoge.jsをしたらdefaultで記載されているものが呼び出されていると解釈してもいい,という風に私は覚えています.この場合であると,Hogeというモジュールがパッケージ化され,それがindex.jsで使えるようになっているというわけです.

//hoge.js
export class Hoge {
  constructor(name) {
    this.name = name;
  }
}

//index.js
import { Hoge } from "./hoge.js";
const hoge = new Hoge("hogehoge");

上の例はexportするときにdefaultをつけていない例です.from "./hoge.js"しても,importするときにどのモジュールを呼び出すかがわかりません.そのため,{}を記載して,明示的にどのモジュールを呼び出すかを記載する必要があります.

import/exportを使うことはたくさんあります.例えば,vueStoreの書き方でよく取る方法は以下の通りです.

import Vuex from "vuex";
import createPersistedState from "vuex-persistedstate";
import auth from "./auth/index";
import error from "./error/index";

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    auth,
    error
  },
  plugins: [
    createPersistedState({
      key: "vuex",
      paths: ["auth.isLogedIn", "auth.user"],
      storage: window.sessionStorage,
    }),
  ],
});
export default store; //exportしている.

vueStoreの場合はstoreの内容をexport defaultしているので,どこでもimport して使うことができます.

参考:

qiita.com

github.com

export/require

exports/require は、Node.js(CommonJS)での書き方です.

//hoge.js
class Hoge {
  constructor(name) {
    this.name = name;
  }
}

module.exports = Hoge; //moduleを使って外部に渡す.

//index.js
const Hoge = require("./hoge.js");
const hoge = new Hoge("hogehoge");
console.log(hoge.name);

という感じでrequireを使います.import文で外部モジュールを受け取るときは,export defaultとしているところをmodule.exportsにしています.const Hogeはそのモジュールをrequireで受け取っているという内容です.ここで,注意なのが,console.log()と記載していますが,これはブラウザで見るとエラーがおきます.require構文はnode.js上で動かすことができる機能なので,ブラウザでは動作しません.実行したいときは,

node index.js

とすれば,ターミナル上で実行結果を見ることができます.exportは違う書き方があるのですが,個人的に使い道があるのか判断が難しいので,ここでは割愛します.フレームワークでrequireを使う例は以下のgulpとかで見られます.

//分割代入
const { src, dest } = require("gulp");
// const gulp = require("gulp")
// gulp.srcと一緒.
const rename = require("gulp-rename");

function copyFiles() {
  return src("./src/*.html")
    .pipe(
      rename({
        prefix: "hello-", //先頭につけたい言葉
      })
    )
    .pipe(dest("./dist"));
}

module.exports = copyFiles;

gulpはnode上で動くのでimport文を使わずに,requireで構文を書くことが個人的にはいいのかなと思っています.

参考:

qiita.com

qiita.com

npm install --save-dev --save global

npm install --save-dev //今のプロジェクトに記載
npm install --save 

npm install -g //globalで今のプロジェクト以外にも--save-devをしなくても使える.

があります.これらのコマンドは外部パッケージをインストールしている内容です.基本的にはよく使うパッケージはglobalでいいと思います.ちなみに,--save-devのdevはpackage.jsonの devDependencies に記載されます.

"dependencies": {
    "gulp": "^4.0.2"
  },
  "devDependencies": {
     "mocha": "^3.4.2"
  }
}

上のように二つあるんです.これらの使い道は,dependenciesがnpm installをしたときに書かれている内容全てインストールしてくれるものです.一方で,devDependenciesはnpm installしてもインストールされません.理解が難しいようであれば,脳死で,npm install --save していれば多分大丈夫です. これらの使い道は以下の通りかなーと思います.

npm install -g @vue/cli //これはよく使うのでね.
npm install --save-dev @vue/cli  //開発するときにこれは必要ないのでインストールしてもしゃーない.
npm install --save @vue/cli //まぁとりあえず.

みたいな感じで使い分けたらいいんじゃないですかね.

参考:

qiita.com

qiita.com

アロー関数

アロー関数は関数の省略記法です.ですので,別に使わなくても問題なくjavascriptは書けます(本当だろうか?).とはいえ,その書き方が読みやすいらしいので,色々な場所で書かれています.さて,個人的には関数は「通常関数,アロー関数」の二つがあると思っています.

//通常関数
function hoge() {}

//アロー関数
const hoge = () >= {}

という記載方法です.特段通常関数という名前はないですが,この記事ではfunction()ベースの関数を指し示すことにします.これら記載の違いは=>の書き方です.この場合だと,書き方が変わっただけなので,中身が変わるというわけではありません.じゃあ,何が便利かというのを記載します.

//普通
const arrow = () => {
  console.log("hoge");
};

//省略
const arrow = () => console.log("hoge"); //カッコを省略できる.

//通常関数
const arrow = function(){console.log("hoge")}

という感じで,カッコを省略できます.言ってしまえばアロー関数ってそんなもんです.通常関数だとかっこが多くて見にくいところをアロー関数は限りなくシンプルに記載できます.しかし,これらの大きな違いは,thisを使った時,通常関数とアロー関数とで,挙動が変わるので注意するところはここです.

//通常関数
class Hoge {
  constructor(name) {
    this.name = name;
    const word = "hoge";
    setTimeout(function () {
      console.log(this); //windowオブジェクト
    }, 1000);
  }
}

//アロー関数
class Hoge {
  constructor(name) {
    this.name = name;
    const word = "hoge";
    setTimeout(() => {
      console.log(this); //Hogeオブジェクト
    }, 1000);
  }
}

アロー関数のthisは内包しているクラスを指ししめます.アロー関数はレキシカルスコープであり,最初に定義したオブジェクトが優先されます.setTimeOutはHogeクラスが定義されたあとで,実行されるので,thisはHogeを指します.レキシカルスコープについては以下のサイトがわかりやすいです.

wemo.tech

さて,アロー関数は他に違いがあるのかといえば,そこそこにあります.「newができない,argumentsがない」など通常関数とアロー関数とでは挙動が異なります.最初に「書き方が違うだけ」と記載しましたが,そんなわけないです.この記事では全て記載しませんが,以下のサイトを見ると理解が深まるので,読んでください.とはいえ,実用的に使う部分ではこの記事で書いたもので事足りることが多いです.

参考:

qiita.com

forEach()

for文を使うならば,forEachでできるか一回考えてみよう精神.これもアロー関数と同じように情緒な書き方にならないような方法です.

const hoge_list = ["hoge", "hogehoge"];
hoge_list.forEach((element) => {
  console.log(element);
}); //hoge hogehogeが出力

forEachをすることで,リスト型の中身に入っているものを取得できます.for文だとごちゃごちゃ書かないといけないけど,スッキリしますね.

const hoge_list = ["hoge", "hogehoge"];
hoge_list.forEach((element, idx) => {
  console.log(element, idx); //hoge 0  hogehoge 1
});

idxはリストのindex値を参照します.forEachは便利ですが,ファンクション型のオブジェクト({"hoge":1}とかの内容は使えないです.)には利用できないので注意が必要.

参考:

developer.mozilla.org

eslintエラーが出まくる問題

eslint使っているとよくわからないエラーがよく出ます.vueとかを立ち上げるとdefaultでeslintの設定がされます.そして,エラーが出るという流れです.その時はエラーが出ていいる場所に以下のコードを描いてあげましょう!!

 // eslint-disable-next-line
register({}, authData) {
      await axios
        .post("api/rest-auth/ragistration/", {
          username: authData.username,
          email: authData.email,
          password1: authData.password1,
          password2: authData.password2,
        })

これで,{}が何も指定されなくてもエラーが出ません!便利ですね.

querySelector とか getElemetIdとかgetElementsByClassNameどっちやねん.

個人的にはquerySelectorを使う方法で統一した方がいいと思います.getElementIdはそのなの通りIDしか取得できません.またgetElementByClassNameとかありますが,これもclassのみです.一方で,querySelectorは両方取得できます.なので,こっちを使うほうが便利です.

  <body>
    <div id="hogehoge">
      <p class="hoge">hoge</p>
    </div>
    <script src="sample.js"></script>
  </body>
const hoge = document.querySelector(".hoge");
hoge.classList.add("hogee");
console.log(hoge);

const hogehoge = document.querySelectorAll("#hogehoge");
hogehoge.forEach((element) => {
  element.style.color = "red"; //文字の色が赤色になる.
  element.classList.add("hogee"); //hogeeのクラスが付与される.
});

querySelectorAllは指定したクラスを持っている全てのDOMを取得します.実用的には以下のような形でDOMを取得したり変更したりします.

class ClassObserver {
  constructor(entries, addoptions) {
    this.entries = document.querySelectorAll(entries); //targetはclass名前id名前に指定
    this._init(addoptions);
  }
  //parameterの追加
  _init(addoptions) {
    this.options = {
      root: null,
      rootMargin: "-100px", //交差点の差分(発火)
    };
    if (addoptions !== undefined) {
      this.options.update(addoptions);
    }
  }
  cb(entries, observer) {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        //要素が入っている時
        entry.target.classList.add("inview");
        observer.unobserve(entry.target); //targetの中にDOM要素がある。これ以降は監視対象から除外
      } else {
        entry.target.classList.remove("inview");
      }
    });
  }
  run() {
    this.entries.forEach((entry) => {
      const io = new IntersectionObserver(this.cb, this.options); //画面に入った時と出た時を監視
      io.observe(entry); //監視
    });
  }
}
export default ClassObserver;

終わりに

Javascriptって結構よくわからない書き方が多いですね.でも,一つ一つ見ていけば,案外ふーんというものが多かったります.