コンポーネント指向とデザインに対するメモ
知り合いでWebの会社やってる人が、ツイッターでコンポーネント指向について、Vueを始めて初めて実感として理解できたと言ってた。
その人はデザイナーがメインで、フロントエンド周りはある程度というスタックの人だけど、確かにそうかも知れんなーと思ったので、いつかネタにするかもと思いながらメモしとく。
コンポーネントベースの考え方ってアトミックデザインやらなんやらの文脈で散々語られているけれど、デザインの文脈から理解するのが結構難しいのかも知れない。
僕も普通にWebサイト作っていた時は実感として理解できていたかわからないし、Vue始めた頃になってようやくコンポーネントで組み立てるってなんなのかわかった気がする。
昨今の開発において、デザインとフロントの距離がどんどん近づいていることは周知の通りだと思うし、デザインベースでコンポーネントが考えられていないと、もちろん実装が破綻する。
なのでこの辺の意識のすり合わせを行う時に、知識差や実感としての理解のあるなしが、デザインとフロントの間の距離に関係してしまうことになる気がしているので、そういう何かがあってもいいかもなーと思った。
ただただ `import ’hoge’;` とか `require('hoge')`とかした場合について
ただただ import ’hoge’;
とか require('hoge')
とかした場合について確認した
会社で闇のコードを弄っているときにrequire('hoge')
みたいなのが無限に出てきた。
こいつってどんな形でimportされるんだ?ときになったので試してみた。
結論から言うと、クロージャの中にいて中のファイルたちはどこからも参照されない。
windowになんかぶら下げるとかする場合は意味があるけど、それ以外はまるで無視される。
経典MDNでは?
MDN曰く
付随効果のためだけにモジュールをインポートする
付随効果だけのためにモジュール全体をインポートしたときは、何もインポートされません。 モジュールのグローバルコードが実行されるだけで、実際の値はインポートされないのです。
まぁ、試すか、と言うわけで試した。
コード
■ test.js
const TEST = 'test';
■ index.js
require('./test.js') const peace = 'peace'
↓
/***/ "./src/index.js": /*!**********************!*\ !*** ./src/index.js ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { __webpack_require__(/*! ./test.js */ "./src/test.js"); const peace = 'peace'; /***/ }), /***/ "./src/test.js": /*!*********************!*\ !*** ./src/test.js ***! \*********************/ /*! no static exports found */ /***/ (function(module, exports) { const TEST = 'test'; /***/ })
TEST
を参照することはできなさそう🤔
export
を追加
■ test.js
const TEST = 'test'; export const Dog = 'DOG' // 追加
■ index.js(一緒)
require('./test.js') const peace = 'peace'
↓
/***/ "./src/index.js": /*!**********************!*\ !*** ./src/index.js ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { __webpack_require__(/*! ./test.js */ "./src/test.js"); const peace = 'peace'; /***/ }), /***/ "./src/test.js": /*!*********************!*\ !*** ./src/test.js ***! \*********************/ /*! exports provided: Dog */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Dog", function() { return Dog; }); const TEST = 'test'; const Dog = 'DOG'; /***/ })
export
されてるものに関しては__webpack_require__
でなんかされそうな雰囲気があるので試した。
■ test.js
export const TEST = 'test';
■ index.js(一緒)
require('./test.js'); console.log(TEST); // Uncaught ReferenceError: TEST is not defined
というわけで、MDNとおりでした。
以上です。
CSSのサイズ指定時の単位について、というか%について詳しく述べる
CSSのサイズ指定時の単位について、というか%について詳しく述べる
モチベーション
今、仕事でフロントエンドエンジニアをやっているけど、案外CSSが苦手な人が多い。CSSはスタイルシート言語なので他のプログラミング言語のパラダイムがまるで当てはまらないことが大きな理由の一つなのかなと思う。
特に細かい仕様を頭に入れていないと何やねんみたいな挙動することも多い。
僕はCSSの肝は以下の3点だと考えていて、それさえマスターすればあとはプロパティを死ぬ程覚えるだけでなんとでもなるんでは?と思っている。
- ボックスモデル
- レイヤー
- 単位
というわけでこの内の単位について、忘れないうちに記述しておこうと思う。
単位の種類
単位の種類は他にもあるだろうけど、主に以下の種類がある
- px
- em
- rem
- vw
- vh
- vmin
- vmax
- %
それ以外にも様々あるので、上記以外のものも含むざっくりとした説明は以下の記事を見たらわかる。
[CSS]長さの単位
というか、ちゃんと調べたいのであれば仕様書はこれです。
Distance Units: the
そういうわけで具体的に見ていく
px
言わずもがな。
pxはwebの世界では絶対値としての地位を確立していると思う。絶対値といえばこれ。最も多くのシーンで使われているわかりやすい単位。
そういうわけで特に説明はないです🥰
em
pxは絶対値だったけど、これ以降に出てくるものはすべて相対値になる。
相対値とは、何かの値をもとに決まる値のこと。emの場合はそのコンテキストのフォントサイズで値が決まる。
<div class="target"></div>
.target { font-size: 20px height: 2em; /* 40px */ }
使い所
コンテキストに完全に依存するので割と使いにくいイメージがあるけど、逆にいえばコンテキストに依存させたいようなときはいいかもしれない。
例えば、ブログなんかの文字は1行あたりざっくり40文字未満が読みやすいらしいので、そういうときは
.entry { width: 40em; }
のようにしておくと、勝手に(font-size * 40)px
を計算してくれる。それ以外にも最大で3行だけ表示したいときの高さなんかも以下のような感じで指定できる。
.block { overflow: hidden; line-height: 1.6; height: calc(3em * 1.6) /* line-heightを掛けてやる */ }
box-sizing: border-box
の場合でpadding
を上下にとっている場合はそれも勘案してやる必要がある。
rem
root要素のfont-sizeを基準にした相対値。
root要素ってのはなにかというとhtmlのことです。
html { font-size: 62.5%; /* フォントサイズはデフォで16pxなので16 * 0.625 = 10px */ } body { font-size: 1.4rem; /* 14px */ }
こんな感じでメインで使う場合はhtmlにfont-size: 62.5%;
として、10px基準で扱い、px
の代わりに扱うことが多い。
pxかremか
戦争がおきそうなテーマでもあるし、僕も一度この件でtwitterで争いがおきたことがあったけど、やっぱ用途としてはpxの代わりという感じ。
ブラウザの機能ではフォントのサイズを変更することができる。これを変更した時にブラウザはhtml要素のfont-sizeを変更するので、remで指定しておくことでその変更を各要素のサイズに反映することが可能になる(そのために上記の指定ではhtmlのfont-sizeは%指定がされている)。
または今後2000pxを大きく超える閲覧環境が出てくるような場合、そのサイズに応じてhtmlのfont-sizeを変更してやれば閲覧環境に合わせたサイズで対応することも可能になる。
ただ、フォントサイズの変更も今どき単純にzoom機能を使うのでは?という気もするので、どこまで考えるかは要件次第って気もするけども。
rem を指定する場合の注意点
上記の通り、remだとサイズが可変することがある。
つまりここはpx、ここはremみたいに混ざってると上記のようなときに可変する場所としない場所が出てわけわからんことになるので、基本的にはpxかremかは統一したほうがいい。(多くの場合emと%はどれとも共存できるとおもう)。
逆にpxだとサイズが可変しないことから、remを使用している場合でも可変してほしくないborder-width
なんかはpxを指定するとかはありだと思う。
vw, vh
viewportのwidth、またはheightをもとにした相対値。
100vw = 画面の横幅という感じ。1vwなら画面の横幅の1%の値。
使いどころ
これはちょいちょい使う。例えばファーストビューにheight: 80vh
みたいにしたりとか。
あとはスマホ時にすべてのサイズをvwで指定することによって、デバイスのサイズが変わっても全く同じ見た目にするみたいなこともできる。代理店案件とかだと、デバイスが変わることによって、文字の入り方が変わるの嫌がられるみたいな闇があるらしく、そういう時に使ってたみたいな地獄みたいな話も聴いたことある。(その場合はぶっちゃけviewportを固定値にしてやれば済む話だけど)
body { font-size: 3.73333333vw /* 14 / 375 */ }
iframe内のvw, vhの値
iframe内の要素にvw, vhがあたっている場合は、iframeのサイズが基準になる。
そりゃそうかという感じだけど、stack overflowにそういう質問が上がっていた。
vhの問題点
vhは便利だけど、スマホのブラウザだとアドレスバーの表示非表示によって若干意図しない挙動になる場合がある。
The trick to viewport units on mobile
そういう場合はおとなしくJavaScriptに頼るのが話が早そう。
vmin, vmax
viewportの短い方、長い方の値を基準にした値ということらしい。
全然使ったことない。
%
%は非常にややこしい。というかこの記事は基本的に%を説明したくて書いているので、ようやく本番がきたなと言う気持ち。 それではやっていくぞ💪
そもそも%ってなんやねん
今までのものは明確な基準値のある相対値だった。それはたとえば、font-sizeだったりview-portだったり、ある値をもとに数値が決定されていたわけだけど、この%というものに関しては付与するプロパティや状態によって基準値が変わる。
整理した感じ4種類あるので順にみていく。
1. コンテキストの相対値としての%
font-sizeのパターン。当たるはずの値を基準に割合を決定する。
body { font-size: 20px; } h1 { font-size: 200%; /* 40px */ }
逆にfont-size以外思いつかん。
2. 親の表示領域の相対値パターン
- width
- height
- margin
- padding
width、heightに関してはそうかなという感じがすると思う。親の表示領域の中での割合が値になる。
ただ、marginとpaddingに関しては親の表示領域の中でもwidthのサイズを基準とした値になる。
.parent { width: 600px; } .child { margin-top: 100%; /* 600px */ }
この特性をうまく使うと画像の縦横比を固定することができる。
.fixRatio { display: block; width: 100%; } .fixRatio::before { content: ''; display: block; padding-top: 75%; /* 横幅に対して75%の高さ */ }
どっちかにbackground-imageなんかを指定したり、.fixRatio
にposition: relative
して中でimg要素をposition: absolute
したりして使う。
結構よく使うイディオム。
親レイヤーを基準にするパターン
position: absolute
のときの%のパターン。
このときは祖先で1番近縁のposition: relative
を持つ要素のボックスを基準に相対値が決まる。
(position: fixed
のときはhtml?なのかな?)
- top, left, right, bottom
- width
- height
自分のサイズに依存するパターン
transfrom
プロパティのtranslate
がこれ。
自分のサイズに依存する。
.block { width: 600px; height: 200px; transform: translate(50%, 50%); /* x方向に300px, y方向に100px移動する */ }
この特徴を活かして、中央表示なんかによく使われる。
全然どうでもいいことだけど、例えば以下のようにしてblock要素を左右中央表示することも出来る。
.block { margin-left: 50%; transform: translate(50%, 50%); }
まぁ、margin: auto
でいいんだけど。
%が使えないプロパティ
以下のやつは使うことができない。
- border-width
- box-shadowのカラー以外の値
- opacity
多分他にもありそう。
そういうわけで
単位と仲良くなりましたか?
これでCSSと戦っていきまっしょう💪
今更だけどまぁまぁ便利なのでgulp4を使う
今更だけどまぁまぁ便利なのでgulp4を使う
はい。
今更gulpって、、、って感じもあるし、webpackだのparcelだの、いやいや、そもそも最近はcliが充実してるので、設定ファイル自体書かないんだよってことがあるのは知ってるけど、gulp4結構便利だったので書いておく。
ちなみに今普通にgulp落としてきたら4以降が落ちてくるけど、3.9.1との明確な差別化のためにこの記事ではgulp4と呼びます。
モチベーション
gulpが便利なときがある
業務でプラグインみたいなやつ書いてて、webpackだけだと結構だるかった。
要件的には以下。
- htmlとかcssとか扱いたいファイルが複数ある
- ただコピーしたいのがちょいちょいある
- 上書きしたいファイルが有る
- develop用に生成されたファイルをちょくちょく確認したい
特に【上書きしたいファイルが有る】はwebpackだと面倒そうな気配が結構あったので、それが決め手だった。
個人的には未だにウェブサイト作るなら、正直gulpは必須だと思う。
npm scriptsで頑張ってもいいけど、gulpの手軽さには代え難いし、特にウェブサイトの制作だと最終的にWordPressのテーマにしちゃうことも多いと思うし、そういう場合だとNuxtとかは向かなそうな印象ある。
◾️ gulp@3.9.1に引きずられた記事が多い
gulpは@3.9.1の時代が長かったので、巷にはその時代の書き方は死ぬ程転がってる。
もちろんgulp4の書き方も山のように転がってるけど、案外3.9.1を引きずった書き方ばっかだったので、そうじゃない書き方の紹介があってもいいだろうと思った。
個人的には4系の書き方のほうが断然好き。gulp書いてる感でなくjs書いてるなーって感じがするから。
gulpfile.js
まぁ、公式を読めって話だけど、往々にして人間は公式でなく日本語で書かれた記事を先にあたるので、こっちでも書いていく。
大雑把に何が違うかというと、大きいところではseries
とparallel
が入った。
今まで直列処理するのにrun-sequence
が必要だったけど、series
が代替できるようになった。
並列処理の場合はその名の通りparallel
を使う。
あとはmodule.exports
にタスクをぶら下げる事になったところも大きな変更点だと思う。
今までgulp.task()
で文字列と処理を渡してたけど、その書き方は非推奨になった。今でもそのAPIは使用可能なこともあって、この書き方を紹介した記事が多い。ここでは紹介しないですが。
そういうわけで以下、具体的な書き方。
■ 基本形
const { src, dest } = requore('gulp'); const default = () => src(`path/to/src/files`).pipe(dest(`path/to/dest`)); module.exports = { default };
ばりシンプル🥰
ちなみにdefault
で外出ししたやつが gulp
実行時に呼ばれる。
以下のようにやるとgulp hoge
で呼べるようになる。
const hoge = () => src(...).pipe(dest(...)); module.exports = { hoge };
各タスクの基本的な流れは3.9.1時代と変わらない。src
でファイルを指定してやって、pipe
で様々な処理を行って最終的にdest
の中で吐き出し先を指定してやるという感じ。
src
に複数渡したいときは今まで通り配列で渡せばオッケー👌globで渡せます。
3.9.1時代もそうだったけど、src
からの処理をきちんとreturnしてやる必要がある。
例)sassの場合
const { src, dest } = requore('gulp'); const sass = require('gulp-sass'); const sassGlob = require('gulp-sass-glob'); const sourcemaps = require('gulp-sourcemaps'); const please = require('gulp-pleeease'); // 僕は基本めんどいのでpleeeaseでsassの処理やってる const styles = () => src(`${DIR.SRC_ASSETS}sass/**/*.{sass,scss}`) .pipe(sourcemaps.init()) .pipe(plumber()) .pipe(sassGlob()) .pipe( sass({ outputStyle: ':expanded' }).on('error', sass.logError) ) .pipe( please({ sass: false, minifier: false, rem: false, pseudoElements: false, mqpacker: true }) ) .pipe(sourcemaps.write('./')) .pipe(dest(`${DIR.DEST_ASSETS}css`));
例にするには長々した記述のタスクだった。
■ parallel
とseries
並列処理と直列処理。
上記の感じで設定したタスクを渡すだけなのでスーパー簡単。
const { src, dest, series, parallel } = require('gulp'); const del = require('del'); // ejsなどの処理を書いておく // parallelに書いたやつは並列で走る const devTask = parallel(ejs, styles, scripts, imageMin); const clean = dir => del([dir]); // seriesで書いたやつは直列で走るので、これだと、clean.bind(null, DIR.DEST) => devTaskの順で走る。 const build = series(clean.bind(null, DIR.DEST), devTask);
■ watch
watchだけ結構癖があるというか、watchの場合は何も返さない。
ただ発火させるだけでいい。
watchもおんなじ感じで行ける。
const watchEjs = () => watch(`${DIR.SRC}**/*.ejs`, series(ejs, reload)); const watchSass = () => watch(`${DIR.SRC_ASSETS}sass/**/*.{sass,scss}`, series(styles, reload)); const watchJs = () => watch(`${DIR.SRC_ASSETS}js/**/*.js`, series(scripts, reload)); const watches = parallel(watchEjs, watchSass, watchJs); const dev = series(build, devServer, watches); module.exports = { default: dev }
■ browserSync
こいつは最近公式に乗った気がするけど、gulp4で使う場合は引数でdone
を受け取って、処理後にそれを発火させる必要がある。
const browserSync = require('browser-sync'); const devServer = done => { browserSync.init({ server: { baseDir: DIR.DEST }, ghostMode: { clicks: true, forms: true, scroll: false } }); done(); }; const reload = done => { browserSync.reload(); done(); };
これでwatchなんかの文脈で使ってもうまいことreloadしてくれる。
■ その他
gulp-ejs
こいつはgulp4から自分で拡張子の名前を変えることができなくなってやがる。
ので、gulp-renameを入れて別途拡張子の変更をしてやる必要がある。
const { src, dest } = require('gulp'); const plumber = require('gulp-plumber'); const gulpEjs = require('gulp-ejs'); const gulpRename = require('gulp-rename'); const ejs = () => src([`${DIR.SRC}**/*.ejs`, `!${DIR.SRC}_inc/**/*.ejs`]) .pipe(plumber()) .pipe(gulpEjs({ INC: Path.resolve(__dirname, `${DIR.SRC}_inc`) + '/' })) .pipe(gulpRename({ extname: '.html' })) .pipe(dest(DIR.DEST));
そんなとこですかね。
だいたいそんな感じでいけます。
関数ベースで組み立てられるので僕としては書いてて割と楽しい。
あと、結構スッキリ書けるようになったと思う。
今回の記述とってきたレポジトリはぼくのgithubに上がってるのでわかんないところあれば、参考にどうぞ
JavaScriptで素数を求めるなどやった
以前、と言っても相当前のことだけれど、会社をやっている友達と飲んでいて、そこの強いエンジニアの人も来ていた。その人はプログラミングを教えるということも行なっており、生徒に出した問題についての話になった。
それが、ある数までの素数を求める、というようなものだった。
調べることは許可されており、確か100までの全ての素数を吐き出すようなプログラムを書け、というような話だったと思う。
僕は素数ってどうやって求めるんやろ、と思いながらも相槌を打っていた。
簡単そうでもあるけど、難しそうでもある。
それからしばらく全く忘却していたが、昨日、全く退屈で終わらないような作業をしている時に、ふとその事を思い出してしまった。
全く脳の使わない仕事をしばらくしている反動か知らないけれど、どうにもやりたくなってしまったのでやってみた。
素数
素数とは、その数と1以外では割り切ることのできない数のことで、それは学校で習った気がする。というか、こいつ公式ないのか?という気持ちになって調べてみたらなかった。
というわけで力技で考えるのならば、与えられた数までの全ての数をその数までの全ての数字で割ってみて、1つも割り切れなければ素数だという事を繰り返せば求めることができそうだ。
その場合だけど、5までの素数を求めるとして
5 / 4, 5 / 3, 5 / 2, 4 / 3, 4 / 2, 3 / 2
という形になる。
まぁ、5位なら求められそうなもんだけど、10000とかだと計算量が増えて、たちまちブラウザがフリーズしそうな気持ちがしてくる。
そういうわけでこんな非効率なことはやっとれんな、と思ったところで、良さそうなものを発見した。
エラトステネスの篩(ふるい)
これが何かと言うと、Wikipedia先生に聞いてみるとなんとなくわかる。
わかるとか言ったけど、僕はすぐには分からなかった。なんとなく読んで、へーっとなって、寝て起きるとようやく理解できてきたというのが本当のところだった。
これはつまり、以下の手順で求めることができそうなことがわかった。
- ある数(max)の平方根を求める(maxDevide)
- maxDevideまでの素数を全て求める(primesForDevide)
- maxDevideより大きい、maxまでの全ての整数をprimesForDevideで割り、1度も割り切れなかったものと、primesForDevideを合成する
そういうわけでやってみた。
実装💪
const getPrimes = max => { if (max < 2) { return []; } const getMaxDevide = n => Math.floor(Math.sqrt(n)); const maxDevide = getMaxDevide(max); const getPrimesByMaxDevide = (num, primesArray = []) => { if (num <= 2) { return [2, ...primesArray]; } const isPrime = ![...Array(getMaxDevide(num) + 1).keys()] .filter(n => n > 1) .some(n => num % n === 0); return getPrimesByMaxDevide( num - 1, isPrime ? [num, ...primesArray] : primesArray ); }; const devidePrimes = getPrimesByMaxDevide(maxDevide); return [ ...devidePrimes, ...[...Array(max + 1).keys()].filter( n => n > maxDevide && !devidePrimes.some(d => n % d === 0) ) ]; };
こんな感じでうまいこといけてるみたい。 1,000,000くらいの数与えてもいけるみたいなので、パフォーマンス的にも問題なさそう。
だけどダラッとかかれているので読みにくすぎるのでリファクタする。
const getMaxDevide = n => Math.floor(Math.sqrt(n)); const isPrime = num => ![...Array(getMaxDevide(num) + 1).keys()] .filter(n => n > 1) .some(n => num % n === 0); const getPrimesByCheckingAllNumbers = (num, primesArray = []) => num <= 2 ? [2, ...primesArray] : getPrimesByCheckingAllNumbers( num - 1, isPrime(num) ? [num, ...primesArray] : primesArray ); const getPrimes = max => { if (max < 2) { return []; } const maxDevide = getMaxDevide(max); const devidePrimes = getPrimesByCheckingAllNumbers(maxDevide); return [ ...devidePrimes, ...[...Array(max + 1).keys()].filter( n => n > maxDevide && !devidePrimes.some(d => n % d === 0) ) ]; };
マシになりましたか?
所感
なんというか、素数を調べるところからやっても2時間くらいかかりそう。
コード書くだけでも割とかかった。
これが入社試験とかで、素数を調べるところからだったら死んでたな。
direnvでプロジェクトごとのnodeのバージョンを切り替える
今更ですが、会社でまとめたのでこっちにも貼っときます。
プロジェクトごとにnode
のバージョンが違って、そのたびに思い出しては切り替えることがあります😇
こっちではv8
なのにこっちではv10
、、、
そもそも覚えてられないし、切り替えるのが面倒、、、、🤯
そういうわけでdirenvを使いましょう。
direnv
これです。 https://github.com/direnv/direnv
こいつを入れて各プロジェクトに.envrcをおくと、そのプロジェクトに移動したタイミングで、.envrcの中身が実行される、みたいなことになります。
■ install
$ brew install direnv
それからシェルにパスを通します。 公式に書いてますが、
☆ ~/.bashrc
eval "$(direnv hook bash)"
☆ ~/.zshrc
eval "$(direnv hook zsh)"
☆ .config/fish/config.fish
eval (direnv hook fish)
あるいは
direnv hook fish | source
後は、direnv edit
コマンドがかけるように以下も追記します。
export EDITOR=vim
後は $ source 設定したファイル
で再読込して$ direnv --version
が通ればバッチリです。
■ .envrc
を作成
作りたいディレクトリに移動して、$ direnv edit .
で.envrc
の作成が始まります。
別に.envrc
を普通においてもいいです。
そのプロジェクトに移動すると、.envrc
の中身が走るわけですが、
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.
とか言われることがあります。その場合は書かれてますが、
$ direnv allow
でOKです。
nodeのバージョン切り替え
それぞれのバージョン管理ツールによりますが、以下のような感じで.envrc
を書くと動くはず。
■ nodebrew
nodebrew use 使いたいバージョン
■ nvm
nvm use 使いたいバージョン # これだとだめ。nvm command not found的なこと言われる
あるいは.nvmrc
を置いてやることもできます。
上の方法だとだめなので、.nvmrc
を作ってやりましょう。
.nvmrc
10.16.0
.envrc
nvmrc=~/.nvm/nvm.sh source $nvmrc nvm use
nvmrc=
のところはnvmの場所です。
【やっとくと良いぞ】.gitignore_globalに設定
他の人がnodeのパッケージ管理に何使ってるかわからんので、こういうファイルはgitに含めたくないものですが、かといって毎回書くと忘れちゃう。
なので、.gitignore_global
を用意して、そこに記述しておきましょう。
$ touch ~/.gitignore_global
.gitignore_global
.DS_Store .nvmrc .envrc
$ git config --global core.excludesfile ~/.gitignore_global
あるいは ~/.gitconfig
に
[core] excludesfile = /Users/<username>/.gitignore_global
はい。
参考 - gitignore_globalを作成する on OSX - nvmとdirenvを使ってディレクトリ毎にnodeのバージョンを指定する方法 - fishでdirenvを設定する
EventTarget.addEventListenerでイベントを登録した時、そのイベントの実体はどこに登録されるのか
モチベーション
要素追加時に、その要素にeventを登録したとする。
その後、その要素が削除され、かつその要素がどこからも参照されていない時、登録されたイベントはどうなるのか気になった。
もし、上記の状態でもイベントが削除されないのであれば、removeEventListenerで明示的に削除する必要がでてくる(ただ、残っていたからといってどの程度影響があるのかという話もあるが未調査)。
そもそもこいつはどこにイベントを登録するのだろう。
そういう訳で仕様書を当たることにした。
仕様書ではEventTargetのEvent Listener List に登録される事になっている。
MDNを見ると、addEventListener
の項目は EventTarget
項目内にある。
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
つまり、addEventListener
は EventTarget
インスタンスのメソッドで、こいつを調べようと思ったら、EventTarget
について調べたら良さそう。
ちなみにEventTarget
オブジェクトはwindow
から生えている。
念の為。
document instanceof EventTarget // true document.createElement('div') instanceof EventTarget // true
へぇ、そうなんだ🤔という気持ちになった。
以下はwhatwgのEventTargetの仕様書。
原文
日本語
この中にこのような記述がある。
Each EventTarget object has an associated event listener list (a list of zero or more event listeners). It is initially the empty list.
日本語だとこう
各 EventTarget object には、 イベントリスナリスト が結び付けられる — それは、 0 個以上のイベントリスナからなるリストであり,初期時には空とする。
更にFor web developers (non-normative)(このブロックは Web 開発者向けであり、規範的ではない。)
という緑色の項目内、 target . addEventListener(type, callback [, options])
の欄に以下の記述もある
Appends an event listener for events whose type attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.
target のイベントリスナリストに[ type 属性値が type であるイベント用のイベントリスナ ]を付加する。 callback 引数が,イベントが配送されたときに呼び出される callback として設定される。
ということは、EventTarget
が addEventListener
で登録されるEvent Listener List
を保持していると言えそうなので、EventTarget
が削除されれば、自ずとEvent Listener List
も削除されるのでは?ということがわかる。
ちなみに、Event Listener List
を観測するためのメソッドはEventTarget
インスタンスに生えていないため、Event Listener List
を実際に観測することは現状できない様子。
(それを観測するためにどうこうするみたいな記事は出てくるが、実体を確認することは今の所できない様子。)
以上
よろしく💪