白猫のメモ帳

JavaとかJavaScriptとかHTMLとか機械学習とか。

closestメソッドは自身を含むよ

こんばんは。

ずいぶん久しぶりになってしまいました。
台風が近づく三連休初日、いかがお過ごしでしょうか。

さて、もうタイトルがすべてっていえばそれまでなのですが、
久しぶりにまじめにJavaScript(jQuery)を書いたら罠にはまったのでメモ。

.closest()ってどんなメソッド

.closest() | jQuery API Documentation

リンク先に

For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.

とあるように、DOMツリー内の祖先をトラバース、
つまり自身の要素をもとに外側に辿って行き、一番最初に見つかった要素を返すメソッドです。

<ul>
    <li id="item1">その1</li>
    <li id="item2">その2</li>
</ul>

というDOMに対して、

$("#item1").closest("ul");

と書くと直上のul要素を取得できます。
これは「.parent()」メソッドを使っても変わりません。

<ul>
    <li class="item"><a href="#">その1</a></li>
    <li class="item"><a href="#">その1</a>その2</li>
</ul>

というDOMに対して、

$("#item1 a").closest("ul");

としても、セレクタに一致するまで遡ってくれるのでul要素を取得できます。
parentを使っている場合よりも柔軟な構造変更が可能なわけです。

で、罠

DOMをもうちょっと複雑にしてみます。いわゆる入れ子構造。

<ul id="list1">
    <li id="item1">
        <ul id="item1-list1">
            <li id="item1-1">その1の1</li>
            <li id="item1-2">その1の1</li>
        </ul>
    </li>
    <li id="item2">その2</li>
</ul>

ディレクトリとか表現するときにはよくあるパターンですね。

$("#item1-1").closest("ul");

とすると当然直上のul、つまり#item1-list1を取得することができます。
されにその外側のulまで遡ろうとして、こう書くと、

$("#item1-1").closest("ul").closest("ul");

#list1が取れません
取れている要素を確認してみると#item1-list1になっています。

要するに先祖と言っている中には自分自身を含んでいるってことですね。

$("#item1-1").parents("ul").eq(1);

とかがいいのかな。もっといい方法あるのかな。

そんな罠に掛ったよっていうお話でしたとさ。

無限subst地獄

こんばんは。

突然の豪雨に窓の周りがびしょ濡れになりました。
ちゃんと窓を閉めて出かけましょう。

さて、Windowsにはsubstという便利なコマンドがあります。
このコマンドは任意のディレクトリをドライブに割り当てることができます。

subst F: C:\Temp

とすれば、「C:\Temp」ディレクトリがFドライブに割り当てられるわけです。

便利なのでバッチにします。
名前はわかりやすく「subst.bat」でいいでしょう。

で、実行すると…。

f:id:Shiro-Neko:20170729184810p:plain

うぁぁぁぁぁぁ。

恐ろしい勢いでコンソールが流れていきます。

なんじゃこりゃ一体って感じですが、実はこれ「subst」が自身のバッチファイルを指してしまっていて、
自分自身をひたすらコールし続けているみたいです。

バッチファイルを作る際には名称をコマンド名と一緒にしないように気をつけましょう。
それでは。

staticでないネストクラス

こんばんは。

昔は海の日から夏休みが始まった気がするのですが、
最近はハッピーマンデーの関係でそうでもないんですよね。

さて、前回のつづきです。
前回はstaticなネストクラスについて書きましたが、
今回は「でない」方です。

Java

Javaにおけるstaticでないネストクラスは、
外側のクラスのインスタンスなしにはアクセスできないクラスです。

class Outer {
    
    private static int field1;
    
    private int field2;
    
    public class Inner {
        
        void notStaticMethod() {
            System.out.println(field1);
            System.out.println(field2);
            System.out.println(Outer.this.field2);
        }
        
        // error!
        // static void staticMethod() {
        // }
    }
}

このネストクラスにはstaticアクセスできないため、当然staticメソッドを定義することはできません。

外側のクラスのインスタンスを「エンクロージングインスタンス」といい、
ネストクラスはstaticでないフィールドにもアクセスすることができます。
(「エンクロージング」は「囲い込む」みたいな意味)

this修飾する場合には「Outer.this.field2」というちょっと不思議な文法を使います。
「this.Outer.field2」ではないことに注意です。

class Test {
    void test() {
        // Outer.Inner inner = new Outer.Inner(); error!
        
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        
        Outer.Inner inner2 = new Outer().new Inner();
    }
}

newキーワードによってインスタンスを生成する場合には、エンクロージングインスタンスが必要です。
「outer.new Inner()」という書き方は慣れないと少し変な感じがしますね。

C#

C#のstaticでないネストクラスは前回の記事で触れたとおり、
Javaでのstaticなネストクラスと全く同じ挙動になります。

class Outer
{
    private static int field1;

    private int field2;

    public class Inner
    {
        public void notStaticMethod()
        {
            Console.WriteLine(field1);
            // Console.WriteLine(field2); error!
        }

        public static void staticMethod()
        {
        }
    }
}

class Test
{
    void test()
    {
        Outer.Inner inner = new Outer.Inner();
        Outer.Inner.staticMethod();
    }
}

C#にはエンクロージングインスタンスという概念はありません。

じゃあ外側のインスタンスのメンバには絶対にアクセスできないかというと、
当然そんなことはなくて、単純に外側のクラスのインスタンスを内部に保持してしまえば良いのです。

class Outer
{
    private int field;

    public class Inner
    {
        private readonly Outer outer;
            
        public Inner(Outer outer)
        {
            this.outer = outer;
        }

        public void notStaticMethod()
        {
            Console.WriteLine(this.outer.field);
        }
    }
}

class Test
{
    void test()
    {
        Outer.Inner inner = new Outer.Inner(new Outer());
    }
}

こんな感じ。
自分で作るの意外と面倒・・・。

Javaのインナークラスはいろいろあるよ

Javaのインナークラスには今回紹介した「staticネストクラス」「非staticネストクラス」の他に、
「無名クラス」と「ローカルクラス」というものもあります。
せっかくなのでちょっと見てみましょうか。

「無名クラス」はインタフェースのインスタンスを直接生成したり、
派生クラスをインスタンス生成時に作成する仕組みです。
Java8でラムダ構文ができたので、今は使うことはあまりないですね。

interface Runnable {
    void run();
}

void test() {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {}
    };
}

「ローカルクラス」はメソッド内で定義するクラスです。
いやほんとこれいつ使うの?

void test() {

    class Local {
    }

    Local local = new Local();
}

上手に使い分けるべし

えーと、みんな違ってみんないいってことでいいですかね?

わざわざ一生懸命使う必要はないですが、
こんな選択肢もあるんだなくらいに心の片隅に置いておくといいことがあるかもしれませんね。

それでは。