7588

[code]

    #
    # メインコンテンツのユーザタブのユーザ一覧の処理。
    #
    
    # 最後の要素の削除ボタンをクリックで要素を削除します。
    $('.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()
    
[/code]

上記のコードは、特に複雑なことをしている訳ではありませんがセレクタの文字列が長いために、どうしても可視性の悪いコードになってしまっていると思われます。

そこで、下記のように変数tを用意し、ユーザ一覧の要素を先に抽出しておくことで、上記のコードよりスッキリと書くことができ、ある程度処理も高速化できると思います。

[code]

    #
    # メインコンテンツのユーザタブのユーザ一覧の処理。
    #
    
    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()
    
[/code]

しかし、上記のコードの同じスコープ上で変数tを再定義してしまうと、当然ですが同じ変数が利用されて、イベントが発生したときにtの対象が異なるために予期せぬ動きをしてしまいます。

そのための解決策として単純に変数名を`t2`、`t3`...のように変えていけば良いのですが、なんとなく好ましくありません。

[code]

    ここに上記のコード...
    
    #
    # メインコンテンツのお気に入りタブのお気に入り一覧の処理。
    #
    
    t = $('.maincontent .tab.favorite .favorite-list') # これだと衝突するのでt2にして回避
    $('.elem',t).each ->
      ...
    
[/code]

変数を`t`を`maincontent_usertab_userlist`のような名前に変更するという手法も考えられます。検索済みの要素を使いまわすため、こちらのほうが処理も高速になるはずです。しかし、これだと結局変数名が長ったらしくなってしまい、コードの可視性はあまり良いとは言えません。個人的に長い名前はよりスコープの範囲の広い変数のみに付けたいものです。一度だけ使う変数に長い名前をつけるのは好ましくありません（あと個人的に変数名を考えるのが面倒です）

そこで`each`を利用します。すると変数`t`はthisでアクセスできるため定義しなくてよくなり、スコープも分かれるため、変数が衝突することもなくなって良い感じになります。（無駄に行を食う3行のコメントもコードが入れ子になるために必要性がなくなり、どこまでが`メインコンテンツのユーザタブのユーザ一覧処理`なのかもハッキリするため個人的に好きです）

[code]

    # メインコンテンツのユーザタブのユーザ一覧の処理。
    $('.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()
    
[/code]

しかし、eachが利用できるのは、あくまで配列オブジェクトであり（jQueryオブジェクトですが）要素の数がたまたま一つだけだったために、このような処理ができているだけです。

というわけで、スコープを分けthisに好きな値を設定する方法として以下が考えられます。

[code]

    (->
      # ここのthisはtarget
    ).call(target)
    
[/code]

thisはCoffeeScriptでは@で表記できるため、この書き方はjsで非推奨になったwith文に近い書き方ができているような気がします。が、これではせっかくCoffeeScriptがインデントによる入れ子の表現ができるのにJavascriptのようなカッコで閉じる書き方になってしまっています。末尾の`).call(target)`も気持ち悪く、できれば先頭に持って行きたいものです。

ということで、これを関数にして定義してしまえば理想的な書き方ができるので定義しよう！と思い立ちました。

[code]

    window.scope = (thisArg, func)->
      func.call(thisArg)
    
    scope target, ->
      @text = 'ABCDE'
    
[/code]

しかし、こういった独自実装の汎用的な関数はあまり利用したくありません。自分が知らないだけでこういったものは既にあるのでは…？と思いました。そこでlodash.jsのドキュメントを漁ったり、標準で用意されてないかざっくりと検索してみましたが、とくにそれっぽいものは見つからず、どのように検索すればいいかもよくわからないので質問させていただきました。

変数tの衝突を避けるためにスコープを定義し、かつthisを利用するベストな方法にはどんなものがあるでしょうか。


CC BY-SA 3.0
2015-03-07T01:15:54.460
7569
2015-04-22T19:35:31.170
2015-04-22T19:35:31.170
8351
8351
question
coffeescript
CoffeeScriptで変数の衝突を避けるためにスコープのthisを利用する方法は？


あまり薦められた方法ではありませんが、無理矢理 JavaScript の [`with`
ステートメント](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/with)
を使うという方法があります。CoffeeScript から `with` を用いるには、以下のように [JavaScript
の埋め込み](http://coffeescript.org/#embedded) を使うことになるでしょう。

[code]

    `with ({t: $('.maincontent .tab.user .user-list')}) {`
    
    $('.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()
    
    `}`
    
[/code]

こうすると、スコープチェーンに `t` を含むオブジェクトがぶら下がるので、`with` 内という非常に短いスコープからのみ `t` を利用できます。

この方法は JavaScript (ECMAScript5) の strict モードを使っていると動作しません。最初に書いた非推奨である理由の1つです。

なお個人的には、 **程々に短い一意の名前** の変数を利用することをおすすめしておきます。私ならば、以下のように `userlist`
という名前を使います。

[code]

    userlist = $('.maincontent .tab.user .user-list')
    
    $('.elem:last > .del', userlist).on 'click', ->
      @parent().remove()
    
    $('.elem > .edit', userlist).on 'click', ->
      $('.elem', userlist).css(opacity: 0.5)
      @parent().css(opacity: 1.0).find('textarea').show()
    
[/code]


CC BY-SA 3.0
2015-03-07T06:50:50.400
7577
2015-03-07T10:14:57.503
2015-03-07T10:14:57.503
208
208
7569
answer


CoffeeScriptでは新しいスコープを作りたい時は`do`キーワードを使うことが一般的だと思います。これはJavaScriptの即時関数とよく似た方法で、コンパイルしたらまさに即時関数になります。

これを規定値と組み合わせたら、なおさら使いやすいです。

[code]

    t = "hello!"
    
    do (t = $('.maincontent .tab.user .user-list')) ->
        t.find('.elem:last > .del').on 'click', ->
            @parent().remove()
    
        t.find('.elem > .edit').on 'click', ->
            t.find('.elem').css(opacity: 0.5)
            @parent().css(opacity: 1.0).find('textarea').show()
    
    console.log t
    
[/code]

こうすれば、何があっても`addEvents`内の`t`は他の`t`と衝突しません。

tutinocoさんは`this`を使う方法を探していると仰っていますが、`this`を使った場合、一度に一つの値のスコープしか限定できません。上記の方法ではいくつもの変数を同時に限定できるので、より良い解決策だと思います。

ご参考まで（英語）

<https://github.com/raganwald-
deprecated/homoiconic/blob/master/2012/09/lexical-scope-in-coffeescript.md>

<https://github.com/raganwald-
deprecated/homoiconic/blob/master/2012/09/actually-YOU-dont-understand-
lexical-scope.md>


CC BY-SA 3.0
2015-03-07T17:59:38.020
7588
2015-03-13T06:34:41.223
2015-03-13T06:34:41.223
4788
4788
7569
answer