リビーリングモジュールパターンをクラス構文で置き変える
背景
JavaScriptにクラス構文が導入される以前の時代、JavaScriptプログラマーはメソッドを持ったオブジェクトを生成するために、それぞれ工夫した構文を使っていました。
その一つに書籍「JavaScriptデザインパターン」で紹介されているリビーリングモジュールパターンがありました。
例えば次のようなメソッドを持ったハッシュオブジェクトを返す関数です。
import getAnnotationBox from '../getAnnotationBox'
import createGrid from './createGrid'
import adaptWidthToSpan from './adaptWidthToSpan'
import getGridElement from './getGridElement'
export default function(editor, domPositionCache) {
const container = getAnnotationBox(editor)
return {
render: (spanId) => createGrid(editor[0], domPositionCache, container[0], spanId),
remove: (spanId) => {
const gridElement = getGridElement(spanId)
if (gridElement)
gridElement.parentNode.removeChild(gridElement)
domPositionCache.gridPositionCache.delete(spanId)
},
changeId: ({oldId, newId}) => {
const gridElement = getGridElement(oldId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
gridElement.setAttribute('id', `G${newId}`)
for (const type of gridElement.querySelectorAll('.textae-editor__type, .textae-editor__entity-pane')) {
type.setAttribute('id', type.getAttribute('id').replace(oldId, newId))
}
adaptWidthToSpan(gridElement, domPositionCache, newId)
}
domPositionCache.gridPositionCache.delete(oldId)
},
updateWidth(spanId) {
const gridElement = getGridElement(spanId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
adaptWidthToSpan(gridElement, domPositionCache, spanId)
}
}
}
}
課題
リビーリングモジュールパターンは、古いJavaScript文法の特徴(ブロックスコープがなく、関数スコープがある)に対応するための目的で用いられました。現代ではスコープに関しては、CommonJSモジュールを使ったときも、ES modulesを使ったときも、トランスパイラが自動的に作成してくれます。
現代のJavaScriptプログラマーが理解するには不要な文脈が多く含まれます。
リファクタリング手法
ES6で導入されたクラス構文で置き換えることで、現代のJavaScriptプログラマーに読みやすいソースコードに置き換えます。
手段1 export default
をexport default class
に置き換える
exportするものを関数からクラスに変更します。
export default function(editor, domPositionCache) {
を
export default class {
に変えます。
手段2 function
をconstructor
に置き換える
関数をコンストラクタに変更します。
export default function(editor, domPositionCache) {
を
constructor(editor, domPositionCache) {
に変えます。
手段3 プライベート変数をthisに代入する
リビーリングモジュールパターンでは同一のスコープだったので、生成するオブジェクトのプロパティから変数が参照できました。
クラス構文では直接参照することはできないので、this.
をつけて参照します。
生成するオブジェクトのプロパティから参照する変数をthis
のプロパティにします。
次の二種類の変数があります。
- 関数の引数
- 生成してローカル変数に代入した値
export default function(editor, domPositionCache) {
const container = getAnnotationBox(editor)
を
constructor(editor, domPositionCache) {
this.editor = editor
this.domPositionCache = domPositionCache
this.container = getAnnotationBox(editor)
に変えます。
手段4 プロパティをメソッドに置き換える
生成するオブジェクトのプロパティに設定している関数をメソッドに置き換えます。
無名関数を設定している場合はメソッド形式に書き換えます。
return {
render: (spanId) => createGrid(editor[0], domPositionCache, container[0], spanId),
を
render(spanId) {
return createGrid(this.editor[0], this.domPositionCache, this.container[0], spanId)
}
に変えます。
メソッド形式にの場合は、ほとんどそのまま、インデントを直すぐらいです。
updateWidth(spanId) {
const gridElement = getGridElement(spanId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
adaptWidthToSpan(gridElement, domPositionCache, spanId)
}
}
を
updateWidth(spanId) {
const gridElement = getGridElement(spanId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
adaptWidthToSpan(gridElement, this.domPositionCache, spanId)
}
}
に変えます。
手段5 プライベート変数への参照にthisをつける
リビーリングモジュールパターンでは同一のスコープだったので、関数の引数とローカル変数が参照できました。
クラス構文では直接参照することはできないので、this.
をつけて参照します。
render: (spanId) => createGrid(editor[0], domPositionCache, container[0], spanId),
を
return createGrid(this.editor[0], this.domPositionCache, this.container[0], spanId)
に変えます。
完成形
import getAnnotationBox from '../getAnnotationBox'
import createGrid from './createGrid'
import adaptWidthToSpan from './adaptWidthToSpan'
import getGridElement from './getGridElement'
export default class {
constructor(editor, domPositionCache) {
this.editor = editor
this.domPositionCache = domPositionCache
this.container = getAnnotationBox(editor)
}
render(spanId) {
return createGrid(this.editor[0], this.domPositionCache, this.container[0], spanId)
}
remove(spanId) {
const gridElement = getGridElement(spanId)
if (gridElement) {
gridElement.parentNode.removeChild(gridElement)
}
this.domPositionCache.gridPositionCache.delete(spanId)
}
changeId({oldId, newId}) {
const gridElement = getGridElement(oldId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
gridElement.setAttribute('id', `G${newId}`)
for (const type of gridElement.querySelectorAll('.textae-editor__type, .textae-editor__entity-pane')) {
type.setAttribute('id', type.getAttribute('id').replace(oldId, newId))
}
adaptWidthToSpan(gridElement, this.domPositionCache, newId)
}
this.domPositionCache.gridPositionCache.delete(oldId)
}
updateWidth(spanId) {
const gridElement = getGridElement(spanId)
// Since block span has no grid, there may not be a grid.
if (gridElement) {
adaptWidthToSpan(gridElement, this.domPositionCache, spanId)
}
}
}
参考
Author And Source
この問題について(リビーリングモジュールパターンをクラス構文で置き変える), 我々は、より多くの情報をここで見つけました https://qiita.com/ledsun/items/0963b6d88d6732362da2著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .