Blogger:ページ番号付ページナビ(5)グローバル変数の除去など

前回作成したpagenavi.jsはグローバル変数ばかりなのでこれらを除去します。feedを受け取るコールバック関数に含まれるグローバル変数をどう除去するのかちょっと悩みましたが、関数に値を持たせるといえばクロージャと思いついてうまくいきました。

前の関連記事:Blogger:ページ番号付ページナビ(4)投稿数が150個を超えても使えるように修正する


フィードを受け取るコールバック関数のグローバル変数を除去する


グローバル変数を除去するにはすべてローカル変数にして関数の引数で引き継いでいけばよいわけです。
var code = home_page + "feeds/posts/summary?start-index=" + jsonstart + "&max-results=1&alt=json-in-script&callback=finddatepost";
これはBlogger:JSONPを使ってJSON形式でブログの情報を引き出すでやったJSOPでBloggerのフィードをコールバック関数finddatepost()の引数で受け取るコードの一部です。
function finddatepost(root) {  // フィードから表示させるページのURL得てそこに移動させる。
    var post = root.feed.entry[0];
    var timestamp = encodeURIComponent( post.published.$t.substring(0, 19) + post.published.$t.substring(23, 29));
    var addr_label = "/search/label/" + postLabel + "?updated-max=" + timestamp + "&max-results=" + perPage + "#PageNo=" + noPage;
    var addr_page = "/search?updated-max=" + timestamp + "&max-results=" + perPage + "#PageNo=" + noPage; 
    location.href =(currentPage == "label")?addr_label:addr_page
}   
関数finddatepost()の引数はフィードのrootしかわたせません。

しかしこのfinddatepost()にはpostLabel、perPage、noPage、currentPageの4つもグローバル変数があります。

この4つの変数をどうやってこのfinddatepost()に渡すかが問題です。

それで思いついたのはPython(5)クロージャ(Closure)のお勉強ででてきたクロージャです。

WSH JScriptでJavaScriptのお勉強(関数定義、クロージャ、this)でJavaScriptでもPythonと同様にできることを学習しました。

関数と関数スコープ - JavaScript | MDNに入れ子になった関数もそれを含む関数もともに引数をとる例が参考になりました。
function paginavi_finddatepost_outside(noPage, perPage, postLabel) {  // フィードから表示させるページのURL得てそこに移動させる関数を返す関数。
    function inside(root) {
        var post = root.feed.entry[0];  // フィードから先頭の投稿を取得。
        var timestamp = encodeURIComponent( post.published.$t.substring(0, 19) + post.published.$t.substring(23, 29));  // 先頭の投稿からタイプスタンプを取得。
        var addr_label = "/search/label/" + postLabel + "?updated-max=" + timestamp + "&max-results=" + perPage + "#PageNo=" + noPage;
        var addr_page = "/search?updated-max=" + timestamp + "&max-results=" + perPage + "#PageNo=" + noPage; 
        location.href =(postLabel)?addr_label:addr_page  // ラベルインデックスページとそれ以外でURLが異なる。
    } 
    return inside
}
行番号はグローバル変数を除去したpagenavi.jsのものです。

finddatepost()に相当する関数inside()を返す関数paginavi_finddatepost_outside()を作成します。

グローバル変数だったものは外側の関数paginavi_finddatepost_outside()の引数としてあらかじめ渡しておきます。

変数currentPageは変数postLabelから判断できるので削除しています。

関数inside() は関数paginavi_finddatepost_outside()のローカルスコープ(これが関数inside()の静的スコープになる)に収まっているのでこのペアでクロージャを形成することになります。

クロージャの変数は保持されるのでnoPage, perPage, postLabelはそのまま関数inside()で使えます。
finddatepost = paginavi_finddatepost_outside(noPage, perPage, postLabel)  // コールバック関数に渡しておきたい変数をクロージャで渡しておく。
あとはこのように静的スコープに変数を渡して関数inside() をfinddatepostで受け取ればfinddatepost()に複数の変数を渡したのと同じことになります。

もう一つのコールバック関数totalcountdata()も同様に処理しました。

あとはグローバル変数だったのを呼び出す関数の引数に順に引き継いでいくようにすればすべてのグローバル変数を除去できました。

pagenavi.js · GitHub

グローバル変数の除去以外にもいろいろわかりにくかったところを整理しました。

このコールバック関数の引数問題以外にもう一つ悩んだことを次のことです。

関数を起動するボタンに書く引数が文字列の場合の処理に悩む


function paginavi_button(noPage, perPage, postLabel, text) {  // redirectするボタンの作成。
    return '<span class="displaypageNum"><a href="#" onclick="paginavi_redirect(' + noPage + ',' + perPage + ',\'' + postLabel + '\');return false">' + text + '</a></span>';  // postLabelは文字列なので、クオーテーションが必要。undefinedも文字列として解釈される。
} 
クリックすると関数paginavi_redirect()を呼び出すボタンをhtmlに書き込むところです。

関数paginavi_redirect()はnoPage, perPage, postLabelの3つの引数を受けます。

noPageとperPageは数値なので問題は起きませんが、文字列のpostLabelが問題です。
<a href="#" onclick="paginavi_redirect(2,5,undefined);return false">2</a>
文字列と同様にpostLabelを書き出すと文字列がそのままhtmlに書き出されます。

undefinedの場合は数値と同じように問題なく処理されます。

ところがundefined以外の場合は変数名と解釈されてしまいます。
<a href="#" onclick="paginavi_redirect(2,5,windows);return false">2</a>
Uncaught ReferenceError: windows is not defined

変数windowsが定義されていませんといわれてしまいます。

そこでpostLabelはクォーテーションをつけて書き出すようにします。
<a href="#" onclick="paginavi_redirect(2,5,'windows');return false">2</a>
これでpostLabelが文字列のときはうまく動きました。

ところが今度はundefinedがfalseと解釈されずundefinedという文字列に解釈されてしまいます。
    if (postLabel == "undefined") {postLabel = false}  // undefinedが文字列と解釈されているのを修正。
これは結局引数を受け取った後postLabelがundefinedという文字列の時はfalseを代入し直すことで解決しました。

参考にしたサイト


関数と関数スコープ - JavaScript | MDN
入れ子の関数とクロージャをコールバック関数の作成に使いました。

次の関連記事:Blogger:ページ番号付ページナビ(6)モジュール化してグローバル関数を除去する

PR

0 件のコメント:

コメントを投稿