Javascriptメモ

Javascriptメモ

varとconst, letの違い

varは関数スコープだが、const, letはブロックスコープ。なので、const, letを無名関数で囲う以下のような書きかたは、動くけど誤解している。

こう書いたほうが無駄がない。

もちろん以下はまちがい。ブロック外でもaを参照できてしまう。

Promiseでforループを順番にまわす

↑は1~5がランダムな順番で表示される。これを1、2、3、4、5の順番で表示させるには以下。

あるいはもっと短くこう書くこともできる。

もっと簡潔に書くこともできて、時間のかかる処理を関数にしてしまえばループの中は1行になる。

data_arrayが非常に大きな配列の場合、Promiseを大量に生成するためメモリーをかなり圧迫してしまう。その場合には一定間隔でループをまわすといい。

あとで気づいたんだが、Promiseオブジェクトは大量に生成してもメモリーをあまり食わない。しかしDeferredオブジェクトはさらっとGB単位を食う。DeferredではなくPromiseを使っているかぎりは以下は不要。まずDeferredから入ったことですごく遠回りになってしまった。

forループを一定間隔でまわす

まずは一定間隔でまわすだけのコード。

forループを一定間隔でまわしつつ順番に実行する

上記ふたつのコードを組み合わせて、data_arrayが巨大な配列だった場合に大量に生成されるPromiseに対処する。

順番に実行したい場合

以下のようにDeferredを使えば、1のあとに2、そのあとに3を表示することができる。doneではなくthen。

ポイントは

  • 時間がかかる関数(この場合setTimeout関数)のコールバック関数の最後でresolveする。つまり時間がかかる関数が終わったらresolveする。
  • Deferredオブジェクトを受け取るthenメソッドの先頭で新たにDeferredオブジェクトを生成する
  • thenメソッドの最後に先頭で生成したDeferredオブジェクトをreturnしてつぎのthenメソッドにわたす

自分で生成しなくてもjQueryの関数はDeferredオブジェクトを返してくれるようだ。以下では1のあとに2が表示される。

ただ、以下だと1、3、2となる。1、2、3にしたい場合はひとつ目のthenメソッドのなかで自分でDeferredオブジェクトを生成する。

forループ内でDeferrdを使いたい場合は以下のような感じ。10秒後に0、そこから9秒後に1、そこから8秒後に2、…と表示される。

ポイントはthenメソッド内からreturnしたオブジェクトをforループ内の1行目で受けてあげること。

自分でイベントを発行する

button要素からカスタムイベントを発行するには以下のように書く。

jQuery版は以下。

イベントをキャッチするには、ふつうのイベントとおなじように以下のように書けばいい。

スコープと実行タイミングについて

まずは結論

素のjavascriptコードはbody要素の最後に置き、以下の無名関数で囲む。

↑はスコープを限定するだけのものであり、いつ実行するかは制御していない。しかし、body要素の最後であれば↓で囲むのとおなじ。

jQueryを使ったコードは以下で囲む。DOM構築完了後に実行されるのでどこに置いてもいい。

素のjavascriptコードも↑で囲むのであればどこに置いてもいい。

$とjQuery

chromeのコンソールでjQueryと$がおなじものか調べてみる。

ほかのライブラリがグローバルスコープで$を使っている場合に衝突してしまうので、あとからjQueryというグローバル変数が追加されたらしい。$を使うのはローカルスコープのみ。グローバルスコープでは$は使わず、jQuery変数を使う。よって、以下で囲むのはだめ。

以下はOK。

ちなみに↑は2番目に出てきた以下とおなじ意味。

なぜコールバック関数なのか

pythonとかふつうのプログラムでは

こんな感じで戻り値retを受け取る。一方Javascriptでは

みたいな感じでfuncの戻り値をコールバック関数で受け取って処理することが多い。

ずっと不思議に思っていたが、ようやくわかった。非同期に動作させるためだ。

pythonのコードではfunc関数が終わらないと次の行に処理は進まないが、Javascriptのコードではfunc関数が終わる前に次の行に処理が進んでいる。終わるとコールバック関数が処理される。逐次ではなく並列に処理が進んでいる時間がある。

次のコードでは、「あとに出るはず」より「先に出るはず」がさきにアラートされる。

setTimeout関数が終わるまえに次の行に処理が進んだので「先に出るはず」がアラートされる。

img要素をDOMで追加できない件

このように最初の子要素としてimg要素を追加しようとしても、なぜかできない。それ以外の要素(a要素、br要素、div要素、span要素など)は追加できる。なぜなのかは不明。とりあえずa要素やdiv要素で囲んでから追加することで回避。
ほんとうに謎。firstChildをlastChildにするとできたりする。つぎはぎの知識でやってるからわからないんだろう。いずれ体系的に学びたい。

→後日、現象を再現できず。ふつうにimg要素でも追加できる。ただ体系的に勉強しなおした結果わかったことがある。firstChildよりもfirstElementChildを使うべきとわかった。firstChildは最初の要素のまえに改行やスペースがあると、最初の要素ではなく改行やスペースを指してしまう。思ったように動作しないケースが多いので、firstElementChildを使うべし。同様にlastChildよりlastElementChildを使うべし。

ノードの追加・削除で配列のインデックスがずれる件

たとえばこのコードは、entryクラスの要素のそれぞれに対して、最初の画像リンクのa要素をentryクラスの要素の先頭子要素に移動するものだが、ループ中最後の処理であるa_elems[j]の削除がうまくいかない。苦労してデバッグした結果、ひとつまえのa要素が削除されている。先頭にa要素を追加した結果、jが指している要素がひとつ前にずれた。ということで、以下のようにj++してやることで解決できる。

ノードの削除で配列のインデックスがずれることでゴミが残る件

↑と類似の現象。idが〇〇〇ではじまるdiv要素を全部削除したくて以下のコードをこしらえた。

一部は消せているが一部残ってしまう。これもやはり↑と同様インデックスがずれることが原因。削除すると後ろが前にずれてきてしまうためひとつスキップしてしまう。というわけで以下のようにi–;を追加すればいい。

この件は↑の件があったのでそんなに時間をかけずに解決できた。成長してるぞー。

要素を複製するにはcreateElementとcloneNodeがある

google adsenseなんかを任意の位置に挿入するのは、createElementではできない。cloneNode(true)を使えばかんたんにできる。

google adsenseのコードをコピーして挿入するとエラーになってるっぽい件

google adsenseのコードを挿入したあとのコードが実行されない。ので、挿入するのはいちばん最後にしないといけない。どうしても挿入したあとに実行したいコードは、scriptタグをもうひとつつくって書く。

要素を操作するときは親の立場から

要素elemがあるとして、それ自身を削除するにはいったん親の立場になってから削除する。

要素elemのまえに要素elem_divを挿入したい場合、やはり親の立場になってから挿入する。

javascriptの変数をPHPでセットするとき

数値をセットするときはいいけど、文字列をセットするときは注意が必要。

javascriptには「”bold”」ではなく「bold」と展開されエラーとなる。以下のようにしなければならない。

正規表現

正規表現については各言語関数が用意されているが少しずつ違うのでメモ。
まずはマッチするかどうか

つぎに置換。自分的にはマッチした文字列を削除するときに置換を使うことが多い。

HTTPS化したら外部サイトのjavascriptを読み込めなくなったとき

おもむろにHTTPS化したら以下のjQueryが読めなくなった。

src属性のhttp:を削るだけでOK。↓で動く。HTTPS化して忍者ツールズのスクリプトが読めなくなったときもhttp:削るだけでOK。

超シンプルな閉じるボタン

最初HTML/CSSでやろうとしたが行数が多くなる。javascriptなら1行でいける。

nth-of-typeよりjQueryのeq

↑みたいにセレクタの最後が要素名だけならnth-of-typeを使えばいい。意図どおりsection.content直下のdivのなかの3番目をとってきてくれる。しかし次のようにすると意図どおりに動作しない。

.post以外のdivが混ざっている場合、section.content直下のdiv.postのなかの3番目をとってきてはくれない。divのなかの3番目をとってくる。3番目が.postでなければnullが返る。つまり

とほとんど動作が変わらない。
クラスでセレクトとするときはjQueryのeq使ったほうがいい。

これなら意図通りsection.content直下のdiv.postのなかの3番目をとってきてくれる。
nth-child/nth-of-type/eqのちがいは以下。

  • nth-childはsection.content直下のすべての要素(div以外も)が範囲
  • nth-of-typeはsection.content直下のdivだけが範囲
  • jQueryのeqはsection.content直下のdiv.postだけが範囲

IE11ではバッククォートが使えない

驚愕。IE11ではこういう書き方するとエラーになる。

こう書かないといけない。まじかよ

DOMオブジェクトとjQueryオブジェクトの変換

DOMオブジェクト→jQueryオブジェクトに変換するには、$関数(?)にDOMを入れるとjqueryに変換できる。

jQueryオブジェクト→DOMオブジェクトは先頭要素を指定。たぶんjQueryオブジェクトがひとつの要素を指す場合のみ。

jQuery超便利な件

余計な勉強したくないのでかたくなにjquery使ってなかったが、ひとたび使ってみると病みつきだ。いまのところ素のjavascriptとくらべて以下の点が便利だなぁと(随時更新予定)。

  • forループで回さなくても、指している全要素に対して処理できる
  • 要素を取得した結果がnullであっても、if文でnull判定する必要なし。nullの場合もよきにはからってくれているっぽい。

アロー関数

奇妙な感じがしてなかなかなじめなかったが、無名関数を短く書くためのもの、と理解した。名前ないならfunctionキーワードすらいらないんじゃね、ってことなんでしょうか。
さらに中身が1行だけなら{}も要らないし、引数がひとつなら()すら要らないんじゃね、と。

addEventListenerのコールバック関数中のthisの挙動

どういう理屈かまだ調べてないが、試した結果、無名関数をわたすとthisは要素を指すが、アロー関数をわたすとwindowオブジェクトを指す。
したがって、アロー関数のなかでthis.classList.add()とかすると「そんなメソッドは定義されてない!(windowオブジェクトはaddメソッドを持っていない)」と怒られる。10分くらいはまった。

jQueryでの要素の指定方法

  • セレクタ
    • 要素名
    • ID(#~)
    • クラス名(.~)
  • 属性セレクタ
    • 完全一致(=)
    • 部分一致(*=)
    • など
  • 疑似クラス
    • :nth-child
    • :nth-of-type
    • :first-child
    • :last-child
    • :hoverとかは使えない
  • フィルタ
    • :eq
    • :first
    • :last
    • :contains

セレクタ、属性セレクタ、疑似クラスはCSSでも使える。フィルタのみjQuery特有。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする