CoffeeScriptで変数の衝突を避けるためにスコープのthisを利用する方法は?
#
# メインコンテンツのユーザタブのユーザ一覧の処理。
#
# 最後の要素の削除ボタンをクリックで要素を削除します。
$('.maincontent .tab.user .user-list .elem:last > .del').on 'click', ->
@parent().remove()
# 編集ボタンをクリックで選択以外の要素を薄くしてテキストエリアを表示します。
$('.maincontent .tab.user .user-list .elem > .edit').on 'click', ->
$('.maincontent .tab.user .user-list .elem').css(opacity: 0.5)
@parent().css(opacity: 1.0).find('textarea').show()
上記のコードは、特に複雑なことをしている訳ではありませんがセレクタの文字列が長いために、どうしても可視性の悪いコードになってしまっていると思われます。
そこで、下記のように変数tを用意し、ユーザ一覧の要素を先に抽出しておくことで、上記のコードよりスッキリと書くことができ、ある程度処理も高速化できると思います。
#
# メインコンテンツのユーザタブのユーザ一覧の処理。
#
t = $('.maincontent .tab.user .user-list') # 変数tを用意
# 最後の要素の削除ボタンをクリックで要素を削除します。
$('.elem:last > .del',t).on 'click', ->
@parent().remove()
# 編集ボタンをクリックで選択以外の要素を薄くしてテキストエリアを表示します。
$('.elem > .edit',t).on 'click', ->
$('.elem',t).css(opacity: 0.5)
@parent().css(opacity: 1.0).find('textarea').show()
しかし、上記のコードの同じスコープ上で変数tを再定義してしまうと、当然ですが同じ変数が利用されて、イベントが発生したときにtの対象が異なるために予期せぬ動きをしてしまいます。
そのための解決策として単純に変数名をt2
、t3
...のように変えていけば良いのですが、なんとなく好ましくありません。
ここに上記のコード...
#
# メインコンテンツのお気に入りタブのお気に入り一覧の処理。
#
t = $('.maincontent .tab.favorite .favorite-list') # これだと衝突するのでt2にして回避
$('.elem',t).each ->
...
変数をt
をmaincontent_usertab_userlist
のような名前に変更するという手法も考えられます。検索済みの要素を使いまわすため、こちらのほうが処理も高速になるはずです。しかし、これだと結局変数名が長ったらしくなってしまい、コードの可視性はあまり良いとは言えません。個人的に長い名前はよりスコープの範囲の広い変数のみに付けたいものです。一度だけ使う変数に長い名前をつけるのは好ましくありません(あと個人的に変数名を考えるのが面倒です)
そこでeach
を利用します。すると変数t
はthisでアクセスできるため定義しなくてよくなり、スコープも分かれるため、変数が衝突することもなくなって良い感じになります。(無駄に行を食う3行のコメントもコードが入れ子になるために必要性がなくなり、どこまでがメインコンテンツのユーザタブのユーザ一覧処理
なのかもハッキリするため個人的に好きです)
# メインコンテンツのユーザタブのユーザ一覧の処理。
$('.maincontent .tab.user .user-list').each ->
# 最後の要素の削除ボタンをクリックで要素を削除します。
$('.elem:last > .del',@).on 'click', ->
@parent().remove()
# 編集ボタンをクリックで選択以外の要素を薄くしてテキストエリアを表示します。
$('.elem > .edit',@).on 'click', (e)=>
$('.elem',@).css(opacity: 0.5)
$(e.currentTarget).parent().css(opacity: 1.0).find('textarea').show()
しかし、eachが利用できるのは、あくまで配列オブジェクトであり(jQueryオブジェクトですが)要素の数がたまたま一つだけだったために、このような処理ができているだけです。
というわけで、スコープを分けthisに好きな値を設定する方法として以下が考えられます。
(->
# ここのthisはtarget
).call(target)
thisはCoffeeScriptでは@で表記できるため、この書き方はjsで非推奨になったwith文に近い書き方ができているような気がします。が、これではせっかくCoffeeScriptがインデントによる入れ子の表現ができるのにJavascriptのようなカッコで閉じる書き方になってしまっています。末尾の).call(target)
も気持ち悪く、できれば先頭に持って行きたいものです。
ということで、これを関数にして定義してしまえば理想的な書き方ができるので定義しよう!と思い立ちました。
window.scope = (thisArg, func)->
func.call(thisArg)
scope target, ->
@text = 'ABCDE'
しかし、こういった独自実装の汎用的な関数はあまり利用したくありません。自分が知らないだけでこういったものは既にあるのでは…?と思いました。そこでlodash.jsのドキュメントを漁ったり、標準で用意されてないかざっくりと検索してみましたが、とくにそれっぽいものは見つからず、どのように検索すればいいかもよくわからないので質問させていただきました。
変数tの衝突を避けるためにスコープを定義し、かつthisを利用するベストな方法にはどんなものがあるでしょうか。