0

クロージャーとか無名関数とか

最近javascriptを触る機会が多くなり、いい機会なのでいつも適当だった無名関数とかクロージャーとかについて纏めてみた。
コードだらけ。

まさに”無名”関数といった書式。()で括るのが少し気持ち悪いけど()がない限り実行されない。
// hoge1と出力
(function(){console.log('hoge1');})();

var fc = function(){console.log(‘hoge2’);}で一旦途切れると考えれば納得の出力。
// hoge2と出力
var fc = function(){console.log('hoge2');}();

function fc()とほぼ同じ。
// hoge3と出力
var fc = function(){console.log('hoge3');}
fc();

クロージャー。
// hoge4と出力
function test4(){
return function(){
console.log('hoge4');
};
}
var fc = test4();
fc();

()で括るのを知らなかった時、出力されるものかと重いえらいハマった。
// 出力なし
function test5(){
return function(){
console.log('hoge5');
};
}
test5();

//関数の中の関数なんでこんな呼び方も一応OK
// hoge6と出力
function test6(){
return function(){
console.log('hoge6');
};
}
test6()();

test5で出力できないけど、こっちは()で括ってるので内側の関数が実行され出力される。
// hoge7と出力
function test7(){
return (function(){
console.log('hoge7');
})();
}
test7();

test5と同じく出力しない。
// 出力なし
var test8 = function(){
return function(){
console.log('hoge8');
};
}
test8();

引数のテスト。無理やり内側の関数に渡すことは可能。
// fuga9 hoge9と出力
function test9(i){
return sub(i);
function sub (i){
console.log(i);
console.log('hoge9');
}
}
test9('fuga9');

一見混乱しそうだけど、fcは内側の関数を呼びだそうとしてるので問題なし。
// fuga9 hoge10と出力
function test10(){
return function (i){
console.log(i);
console.log('hoge10');
};
}
var fc = test10();
fc('fuga10');

クロージャーの良さはグローバル変数を多用してると気づかない。
漠然とグローバル変数は遅いという認識だったけど、実際ベンチマークをとってる記事とかをみると確かに遅い。
本題と関係ないけど、重いとか遅いとかってのは主観とかスペックとかにもよるし、そもそも多少の遅さより開発の速度を優先せんとダメな場合もあるのでどれくらい重いとか遅いというのを数値化するのは重要。

0

jQuery1.4系のあれこれ

PCサイトから長らく離れている間にjQueryが1.4になりました。
何が変更されて何が追加されたっていうのは「jQuery 1.4 API Cheat Sheet — Future Colors」というサイトでうまくまとめられています。
bind()で複数イベントが追加できたり、処理を遅らせるdelay()等があったりします。

基本的に互換性は保ってるとのことだけど、そのままのコードだと動かないところもあります。

例えば1.3だとこれでtitleというnameのinputの値がとれたけど1.4ではエラーになります。
$('input[@name=title]').val();

1.4では下記のようにtypeを明示する必要があります。
$('input:text[@name=title]').val();

jqueryのfunction名は直感的でわかりやすいなぁ。

0

mixiアプリで情報をjsonで永続的に保存する方法

opensocialのデータの保存方法は、Key-Valueペアの文字列情報になります。
非常に高速に単純明快で扱いやすいのですが、とたんに扱いにくくなりコードが冗長化しがちです。

mixiアプリはjavascriptなんでそんな時jsonで保存しとくとラクチンです。
幸いな事にopensocialにはjson化するメソッドが既に備わっています。
下記がarrと言う名前のJSONデータが作成を保存する方法です。

情報の永続化

var user_id = opensocial.IdSpec.PersonId.VIEWER;
 
var arr = {};
arr['title'] = "hoge";
arr['rank1'] = "rank1";
arr['rank2'] = "rank2";
arr['rank3'] = "rank3";
 
// エンコードしてjson化
var jsonvalue = gadgets.json.stringify(arr);
  
var req = opensocial.newDataRequest();
req.add(req.newUpdatePersonAppDataRequest(user_id, "jsonvalue", jsonvalue));

永続化された情報の取得

var user_id = opensocial.IdSpec.PersonId.VIEWER;
 
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(user_id), "viewer");
var fields = [ "jsonvalue" ];
 
req.send(function(response) {
if (response.hadError()) {
alert(response.getErrorMessage());
} else {
var myId = response.get("viewer").getData().getId();
var data = response.get("viewer_data").getData();
 
var str = data[myId]["jsonvalue"];
str = gadgets.util.unescapeString(str);
var arr = gadgets.json.parse(str);
 
// hogeが出力される
alert(arr.title);
}
});

0

mixiアプリでのキャッシュの無効処理

無効処理と言う程でもないけど、mixiアプリはアプリ自体は自前のサーバーにアップします。
そのアプリをmixiから呼び出して実行する仕組みです。
毎回mixiから自前サーバーへの接続があるのも面倒な話なんで、アプリはmixiのキャッシュサーバーに取り込まれます。
これはこれでいいんだけど、開発時やデバッグ時にはかなり厄介です。
で、このキャッシュを除去するには、URLの後に下記のパラメーターを付与します。

&nocache=1

これで毎回修正を加えたアプリを取得しにいきます。

ただこれでもxmlの中に
<script type="text/javascript" src="http://hoge.com/mixiapp/opensocial-jquery.js"></script>
のように外部ファイルを読み込む記述があるとこれは引き続きキャッシュを読み込みます。

これを除去するのは、使い古されたやり方だけどURLの後ランダムな文字列(Math.random())を付与します。

0

mixiアプリでownerがviewerであるかの判別

var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest('OWNER'), 'owner');
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer');
req.send(function(data) {
var owner = data.get('owner').getData();
var oid = owner.getField(opensocial.Person.Field.ID);
var viewer = data.get('viewer').getData();
var vid = viewer.getField(opensocial.Person.Field.ID);
var viewer = data.get("viewer").getData();
if(vid == oid){
//viewerがownerである時
$("#owner").show();
$("#viewer").hide();
m.getContents();
}else{
//viewerがowner以外の時
$("#owner").hide();
$("#viewer").show();
v.getContents();
}
});

0

mixiアプリでユーザー情報を取得

mixiアプリを仕事ですることになったのでメモ。
ユーザー情報をとってくるサンプルです。
特定のユーザー(243944402)からプロフィールURLを取得します。

profile URL is <span id="target"></span>
<script type="text/javascript">
function init() {
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [
opensocial.Person.Field.PROFILE_URL,
];
// ユーザーIDを指定
var uid = "243944402";
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(uid, params), "spec");
req.send(function(data) {
var viewer = data.get("spec").getData();
var profileUrl = viewer.getField(opensocial.Person.Field.PROFILE_URL);
document.getElementById("target").innerHTML = profileUrl;
});
} 
gadgets.util.registerOnLoadHandler(init);
</script>

特定のユーザー(243944402)のマイミクのプロフィールURLを取得します。
複数のデータがとれるのでeachでまわす。

<div>profile URL is <span id="target"></span></div>
<script type="text/javascript">
function init() {
// ユーザーIDを指定
var uid = "243944402";
var user_info = {};
user_info[opensocial.IdSpec.Field.USER_ID] = uid;
user_info[opensocial.IdSpec.Field.GROUP_ID] = "FRIENDS";
var idSpec = opensocial.newIdSpec(user_info);
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [
opensocial.Person.Field.PROFILE_URL,
];
var req = opensocial.newDataRequest();
req.add(req.newFetchPeopleRequest(idSpec, params), "friends");
req.send(function(data) {
var friends = data.get("friends").getData();
friends.each(function(friend) {
var prof = friend.getField(opensocial.Person.Field.PROFILE_URL);
document.getElementById("target").innerHTML += prof + '<br>';
});
});
}
gadgets.util.registerOnLoadHandler(init);
</script>

このアプリをインストールした人のプロフィールを取得したい場合は、opensocial.IdSpec.PersonId.OWNER、
このアプリを今使ってる人(ユーザー自身)のプロフィールを取得したい場合は、opensocial.IdSpec.PersonId.VIEWER
をそれぞれuidに指定。

uidのユーザーIDはintとして扱うとエラー吐くので注意。

0

javascriptで桁合わせ

javascriptには他言語と違い、数字の桁合わせ(ゼロ詰め)がありません。
いつも代用する手段はこれ。
2桁に合わす。桁数に応じて適宜数字を変更。
function test() {
var date = new Date();
var month = "0" + (date.getMonth() + 1);
var zero_month = month.substr(month.length - 2);
alert(zero_month);
}

ポイントとしては、4行目の(month.length – 2)。
ここをマイナス指定してしまうとIEで動かなくなります。

0

jqueryのform.js

jqueryのプラグインといえば、エフェクト系が多いのですがform.jsはその名の通りformの値を扱うの便利なプラグインです。

例えばこんなフォームがあったとして、
<form action="/hoge.php" name="search" method="GET" id="comSearchForm">
<table>
<tr>
<th>
フリーワード
</th>
<td>
<input name="word" type="text" value="">
</td>
</tr>
<tr>
<th>
生年月
</th>
<td>
<select name="start_year">
<option value="">----</option>
<option value="2006">2006</option>
<option value="2007">2007</option>
<option value="2008">2008</option>
</select>年
<select name="start_month">
<option value="">--</option>
<option value="01">01</option>
<option value="02">02</option>
<option value="03">03</option>
<option value="04">04</option>
<option value="05">05</option>
<option value="06">06</option>
<option value="07">07</option>
<option value="08">08</option>
<option value="09">09</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>月
</td>
</tr>
</table>
<div class="submit">
<input type="hidden" name="action" value="search" />
<input type="button" value=" 検 索 " onclick="searchCom();"/>
</div>
</form>

それぞれの値をajaxでphpに渡したい場合(検索なんかの時)に、form.jsを使えばフォームの値を一発でパラメータに変換してくれます。
queryString = $('#comSearchForm').formSerialize();

queryStringはこんな感じになります。
word=%E3%81%BB%E3%81%92&start_year=2006&start_month=02&action=search

0

jqueryでフォームの値を扱う

jqueryでフォームの値を取得する時、毎回調べてしまうのでまとめとく。
ID指定してattrとかでもいけるんだろうけど、checkboxの時に統一できないんで。

HTML
<form name="form">
タイトル
<input type="text" name="title" id="title">
<br />
コメント
<input type="textarea" name="com" id="com"></textarea>
<br />
血液
<input type="radio" name="blood" id="blood_A" value="A" />A型
<input type="radio" name="blood" id="blood_B" value="B" />B型
<br />
趣味
<input type="checkbox" name="hobby" id="hobby_A" value="サッカー" />サッカー
<input type="checkbox" name="hobby" id="hobby_B" value="野球" />野球
<br />
都道府県
<select name="pref">
<option value="東京">東京</option>
<option value="大阪">大阪</option>
</select>
<input type="button" value=" クリック " onclick="show()" />
</form>

javascript
<script type="text/javascript"><!--
function show() {
$("#result").html('');
//name
$("#result").append($('input[@name=title]').val());
// textarea
$("#result").append($('input[@name=com]').val());
// radio
$("#result").append($('input[@name=blood]:checked').val());
// checkbox
$('input[@name=hobby]:checked').each(function(){
$("#result").append($(this).val());
});
// select
$("#result").append($('select[@name=pref]').val());
}
// --></script>

0

jqueryのeach

使いそうで使ってなかったjqueyのeach関数。
下記のようにDOMをループしてリンク先を全て変更したい時等に使います。

<ul class="clearfix">
<li><a href="link1.html">リンク1</a></li>
<li><a href="link2.html">リンク2</a></li>
<li><a href="link3.html">リンク3</a></li>
<li><a href="link4.html">リンク4</a></li>
<li><a href="link5.html">リンク5</a></li>
</ul>

$(document).ready(function(){
j$("ul li a").each(function(i){
var url = j$(this).attr('href');
url = url + '\?ref=blog';
j$(this).attr('href', url);
});
});

するとページがロードされるとこんな感じに代わります。
<ul class="clearfix">
<li><a href="link1.html?ref=blog">リンク1</a></li>
<li><a href="link2.html?ref=blog">リンク2</a></li>
<li><a href="link3.html?ref=blog">リンク3</a></li>
<li><a href="link4.html?ref=blog">リンク4</a></li>
<li><a href="link5.html?ref=blog">リンク5</a></li>
</ul>

実際jsでリンクを変更って余り使わないだろうけど、classを変更したい時はテキストを変更したい時に使えます。

jsでインターフェイスの幅が広がったと思うけど、本当のユーザビリティーってなんだろうと思う。
「直感」で分かるインターフェイスはもちろん重要。
しかし、

  • ユーザーは「次へ」をクリックするとページ遷移することに慣れてしてまってる。
  • 開発者はユーザービリティを考慮してページ遷移させずにajaxで次ページを表示する。
    で、ブラウザの戻るをクリックするとユーザーが望んでるページに戻らない。

開発者や制作会社なんかは後者を意識しがちだけど、ユーザーの慣れとか体験っていうのを無視してしまうのは、本来のユーザビリティっての無視してる気がする。
後進的と言われようと、そこは重要なところ。