1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[JavaScript, eslint]for...in→forEachに変更した時の注意点

Last updated at Posted at 2022-05-27

はじめに

最近アサイン中のプロジェクトでのeslintが厳格化したので、その解析エラーの対応をしていた時のメモ

ハマったとこ①

エラーを吐くソースコードはコレ。

ちなみにVueで書いてます。

data() {
  return {
    amountErrors: []
  };
},
created() {
	for (let formName in this.errors) {
	  let messageOrder = formName.split('.')[1];
	  if (formName.match(/amount/)) {
	    this.amountErrors[messageOrder] = this.errors[formName][0];
	  }
	});
},

実際のエラーはコレ。

for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array. (no-restricted-syntax

Deeplによる日本語訳は

for..inループはプロトタイプチェーン全体を繰り返し処理しますが、これは事実上望んだものではありません。Object.{キー,値,エントリ}を使用し、結果の配列に対して反復処理を行います(無制限構文)。

とのこと。

ちょっと理由まで把握しきれませんでしたが、eslintとしては「事実上望んだものではありません」とのことなので、エラーが出るんだな、と把握

ググってみると…

とはいえ、先ほどのエラー文で検索した結果の記事の一つに

「私はforEachに変更して解決しました。」

とあったので、参考にさせてもらった

「なんでforEach?」と思ったが

  • 反復処理ができる(これは知っている。PHPとかとイメージ合う)
  • コールバック関数で引数を持たせることができる(?!)

ことが分かりました。

後者の「引数を持たせることができる」ってのが前述のeslintのエラー文の「Object.{キー,値,エントリ}を使用し」に該当するんだな、と推測


※追記
ここについては @htsign さんのコメントが参考になるので、合わせてご確認くださいませ。


対応

なので、さっきのコードを以下のように変更

Object.keys(this.errors).forEach(function makeErrors(formName) {
  let messageOrder = formName.split('.')[1];
  if (formName.match(/amount/)) {
    this.amountErrors[messageOrder] = this.errors[formName][0];
  }
});

for...in構文をforEachに変更しただけ

これでeslintは通るようになりました。よかったよかった。

その他参考

ハマったところ②

さらにハマりました。笑

原因は元々for分内で使っていたthis

forEachに変更したコードをブラウザで動かすと

Uncaught TypeError: Cannot read properties of undefined (reading 'amountErrors')

と言うコンソールエラーが表示されてコードが動きませんでした。

dataプロパティであるamountErrorsを参照しているthisがundefinedになっているらしい。

対応

どうもstrict モードだと関数コンテキスト内ではグローバルコンテキストが参照できずundefinedが返る。

なのでforEach内でなにもしなければthisundefinedになる。

じゃあどうするのかと言うとforEachは第二引数に値を渡すと、渡された値がforEach内でも使える、とのこと。(先ほどのforEachの説明のところにしれっと書かれている)

なので、こうしてみた。

Object.keys(this.errors).forEach(function makeErrors(formName) {
  let messageOrder = formName.split('.')[1];
  if (formName.match(/amount/)) {
    this.amountErrors[messageOrder] = this.errors[formName][0];
  }
}, this);

これで今まで通りカードが動くようになり、マージリクエストを出すことができました〜

おわりに

普段PHP使ってる自分からしたらJSの forEachはPHPと同じように使えない点で、すごくクセがある気がしてなるべく避けていたんですが今回いい機会でした。笑

特にforEachはreturn;break;による中断処理ができない点で詰まったトラウマがあって…

第一引数のクロージャーの中にもいろいろ引数を渡すことができて、できること多いんだなぁと知れたので、少しづつ慣れていこうかと思います

お読みいただきありがとうございました。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?