はてブのようにTweetする機能作ったよ!

はてブでブックマークするときに押すボタンってかなり便利だよね。

これがなかったら、わざわざURLをコピーして、はてブのサイトに行って、書き込みっていう作業をしないといけない。

Twitterでもそれは同じで、あるサイトに対してつぶやきたい場合は、URLをコピー、Twitterのサイトに行く、書き込みっていう作業が必要だよね。


じゃあはてブのボタンの用にTweetするボタン作ればよくね?

作った

10分とかからなかった。
Google Chorome、FirefoxOperaで動作確認済み。

javascript:(function(){window.open("http://twitter.com/?status="+location.href);})();


上のJavaScriptをブラウザのブックマークに追加すれば完成!




あとは、サイトに対して突っ込みたいときにTweet!を押せば、Twitterのページに飛べる。
もとのサイトのURLがすでに入っているので、それに突っ込みを追加して投稿!


これでより、tweetしやすくなったね。

より幸せなTwitterライフを送ってください。では!

追記(タイトル有バージョン)

"twitter"にてタイトルもあった方がさらにいいのでは?とのコメントをいただいたので、タイトル有バージョンも作った。

javascript:(function(){window.open("http://twitter.com/?status=+"+encodeURI(document.title)+location.href);})();


場合によって、好きな方を使えばいいと思うよ。

さらに追記(タイトル判別バージョン)

もっと便利なの作ってもらった。やったー。
http://skdontl.blogspot.com/2009/09/tweetlet.html
タイトルが有るか無いかを自動的に判別してくれる。みんな、これ使えばいいよ!

try catchでスコープが可能!?

AS3ではブロック単位のスコープは無くて、スコープが欲しい場合は、関数クロージャでくくったりする。でもそれ以外にも方法があったらしいね。
偶然にもtry catchでもスコープがあることを発見した。
なんだこれ?

package 
{
	import flash.display.Sprite;
	import flash.text.TextField;	
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			var xs:Array = [];
			try { throw 0; } catch (i:int) {
				for (; i < 5; i++ )
				{
					xs.push(i);
				}
			}
			var sum:int = 0;
			try { throw 0; } catch (i:int) {
				for (; i < xs.length; i++ )
				{
					sum += xs[i];
				}
			}
			var tf:TextField = new TextField();
			addChild(tf);
			tf.text = "sum" + sum;
			//trace(i);//コンパイルエラー
		}
	}
}

http://wonderfl.net/code/47bb13e129e7eee92a0a556bbe430b78bb8cb29d#251374
iに注目。
catch(i:int)で宣言しているiのスコープはcatchの中になっているっぽい。
つまりcatchの外からはiは見えない。
何でこうなるかはわかんない。仕様なのか?バグなのか?


ひとまずこれで、これで関数内でスコープを自由自在に使いこなすことが出来るね!
...ってことにはならない。


そもそも例外を例外以外で使おうとするなんて、問題外。
可読性めっちゃ悪いし。


例えば、スコープの付いた変数を2個定義しただけでも、

try { throw 0; } catch (i:int) {
	try { throw 0; } catch (j:int) {
		for (i=0; i < 5; i++ ) {
			for (j=0; j < 5; j++ ) {
				trace(i, j);
			}
		}
	}
}

と、ネストがひどい。
面白い機能だけど、くれぐれも遊び以外では使わないように!

追記

どうやらこれはECMAScripの仕様らしいです。

ECMA262規格で決められた仕様です。AS3はECMA262の拡張言語なのでECMAの仕様は踏襲していると思います。JavaScriptも同様だけど、これが正しく実装されていない処理系もあったりします(特にJScript)。

関数クロージャ使えばprivateなメンバが消えてなくなるよ!

クロージャネタでいつまで引っ張る気だよ!って声がそろそろ聞こえてきそう・・・。
俺のクロージャネタは108式まであるぞ!


ActionScript書いてて余計なメンバが増えることって無い?
数個なら気にならないけど、それが増えてきたら気持ち悪いよね。
関数クロージャを使いこなせば余計なメンバも定義する必要もなくなるぜ!

関数クロージャを使わなかった場合

例えば60〜0までのカウントをTextFieldで表示するものを作ろうとした場合。

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.text.TextField;
	
	public class Main extends Sprite 
	{
		private var _tf:TextField;
		private var _count:int;
		public function Main():void 
		{
			_count = 60;
			_tf = new TextField();
			addChild(_tf);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onEnterFrame(e:Event):void 
		{
			_tf.text = "count" + _count--;
			
			if (_count < 0)
			{
				removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			}
		}
	}
}

wonderfl build flash online | 面白法人カヤック


このように書くのがふつー。
ただ、_tfと_countはonEnterFrame()の中でしか使ってないのに、
メンバとして宣言しないといけないのはちょっと微妙。

関数クロージャを使った場合

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.text.TextField;
	
	public class Main extends Sprite
	{
		public function Main()
		{
			var tf:TextField = new TextField();
			addChild(tf);
			var count:int = 60;
			addEventListener(Event.ENTER_FRAME, function(e:Event):void {
				tf.text = "count" + count--;
				if (count < 0)
				{
					removeEventListener(Event.ENTER_FRAME, arguments.callee);
				}
			});
		}
	}
}

関数クロージャを使った場合 | wonderfl build flash online


あれ?tfとcountがメンバじゃなくなったぞ!
ついでにonEnterFrame()も消えちゃった!


コードを見ると、関数クロージャの中からtfやcountにアクセスしている。
これは、レキシカルスコープという関数クロージャ特有のスコープのおかげです。
関数クロージャの中から外の変数が見える。


このレキシカルスコープと関数クロージャを使えば、コンストラクタ以外のすべてのメンバがprivateならば、
メンバをすべて無くすことも可能です。


関数クロージャすっげ!

関数クロージャの中でthisを使ってはならない

そーいえば、前twitter上でFlasherたちが、メンバを呼ぶときにthis付ける派か付けない派で議論してたね。
私の場合、thisを付けいないとわかりにくくなる場合は付ける。


じゃあ、関数クロージャの中では、this付けてメンバ呼び出す?
いやいや、関数クロージャの中では、thisがどれを指すかは実行時に決まるので基本的にthisは使わないよ。

関数クロージャの中ではthisが変わってしまう

メソッドの内部ではthisを付けようが付けまいが、変数名が被ってない限り、動作は変わらない。
なので、thisは読みやすさを重視するために付けるようなもんだ。


しかし、これが関数クロージャとなると話は別だ。
下の例で何が起こるのかわかるだろうか?

package 
{
	import flash.display.Sprite;
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			(function():void {
				this.f();
			})();
		}
		private function f():void
		{
			trace("f() called!");
		}
	}
}

きっと、「f() called!」って出力が出るんだよね!
残念ハズレ。
「thisにfっていう関数はねーよ!」というランタイムエラーが出る。
これはthisが指している対象が違うため。

じゃあthisって何を指しているの?

次の例で、traceで何が出力されるのか予想してみよう。

package 
{
	import flash.display.Sprite;
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			trace("1 " + this);
			
			(function():void {
				trace("2 " + this);
			})();
			
			var o:Object = new Object();
			o.f = function():void { trace("3 " + this); }
			o.f();
		}
	}
}

答え
1 [object Main]
2 [object global]
3 [object Object]


1はメソッド内に書かれているので、ふつーにMainのオブジェクトを指している。
2,3が関数クロージャ内でthisが使われているので、挙動が違う。
2がglobalというよくわからない場所を指していて、
3が、oを指している。

thisが変わる理由

thisが指している対象が変わってしまうのかは、一応理由がある。
ASも昔はJavaScriptのようなプロトタイプベースのオブジェクト指向言語だった。
それが、AS2.0、AS3.0になり、クラスベースのオブジェクト指向プログラミング(OOP)もサポートするようになった。
メソッドクロージャの内部でthisが変わるのは、そのプロトタイプベースのOOPの名残とも言える機能だ。
なので、AS3でもプロトタイプベースのOOPをやろうと思ったら出来なくはないが、
クラスベースのOOPの方が優れているので誰もやらない。


なので、この関数クロージャの内部でthisが変わるというのは、クラスベースのOOPをしている限り、
うっとうしいだけであり、邪魔で、意味のない機能だ。
AS3で誰もプロトタイプベースのOOPをしてないんだから、この機能は消えるべきだと思う。


であるからして、AS3は基本的にメソッドクロージャ内部ではthisを付けないこと推奨する。
ちなみにAS2までは、メソッドもthisの対象が変わってしまうというやっかいな問題があった。
Delegate.create()という関数を使ってその問題に対処していた。

それでもthisを使いたいです!

それでも、ローカル変数とメンバ名が被った場合など、thisを使いたい場合があるだろう。
何通りか対処の方法はある。

一番簡単で一般的な方法が、thisを関数クロージャの外で変数に入れておく方法。

package 
{
	import flash.display.Sprite;
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			var f:Function = function():void {
				trace("local f() called!");
			}
			
			var self:Main = this;
			
			(function():void {
				f();
				self.f();
			})();
			
		}
		private function f():void
		{
			trace("member f() called!");
		}
	}
}

上の例ではselfという変数を使って代わりに使用している。
これなら、指している対象が変わってしまうということにはならない。
一安心だ。


他にもFunction#call()とかを使えば出来るけど、あんまり使わないので説明は省略。

クロージャをイベントに登録したら、どうやってイベントから削除するの?

無名関数使うと、removeEventListener()出来ないんじゃないの?
っていうコメントがあったので。

それぐらい出来るよ!

その1 一時変数に入れておく

誰でも思いつく方法。
変数に入れて変数名でアクセスすれば解決。

public function Main():void
{
	var f:Function = function(e:Event):void {
		trace("hello");
		removeEventListener(Event.ENTER_FRAME, f);
	}
	addEventListener(Event.ENTER_FRAME, f);
}

実行してみると、"hello"、と一回出るだけ。
え?いちいち変数作るのめんどくさいって?

その2 arguments.calleを使う

無名関数自身にアクセスする方法はないだろうか?
もちろんあるよ!arguments.calleつかえばいいよ!

public function Main():void
{
	addEventListener(Event.ENTER_FRAME, function(e:Event):void {
		trace("hello");
		removeEventListener(Event.ENTER_FRAME, arguments.callee);
	});
}


arguments.calleというのは、現在実行している関数への参照。
上の例では、arguments.calleの中に無名関数が入っている。
ちなみに、メソッドでもクロージャでもどちらの場合でも使える。


arguments.calle使えば無名関数で再起も出来るよ!

(function(i:int):void {
	trace(i);
	if (i > 0)
	{
		arguments.callee(i - 1);
	}
})(5); // => 5 4 3 2 1 0

メソッド、メソッドクロージャ、関数クロージャ

前の記事、ミスったああああ。
メソッドクロージャじゃなくて、関数クロージャだった!
修正しといた。


メソッドとメソッドクロージャと関数クロージャって紛らわしいよね!
ということでまとめ。

メソッドとは?

いつも使ってるやつ。
インスタンス関数とかstatic関数とかがそれに当たる。

public function foo():void {}

とか。

関数クロージャとは?

クラスに属していない関数のこと。
無名関数で作られたり、関数の中で宣言したやつがこれにあたる。
様は動的に作られる関数。

public function foo():void
{
	var f:Function = function():void { trace("f()");}
	
	function g():void
	{
		trace("g()");
	}
	f();
	g();
}

上の例では、fに束縛(代入)された関数や、gが関数クロージャにあたる。
当然、fとかgとかはfoo()の外からはアクセスできない。
ちなみにtrace()や、getTimer()も、クラスに属していないので、これも関数クロージャである。
(TODO:あとで補足書く)

メソッドクロージャとは?

メソッドをFunctionオブジェクトとして扱う場合、それをメソッドクロージャっていう。

public function Main():void{
	addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
private function onEnterFrame(e:Event):void
{
	trace("enter framce");
}

addEventListener()の引数に入れているonEnterFrameがそれにあたる。


というのが
AS3のクロージャ | www.imajuk.swf
に書いてあったよ!

ActionScript3.0で関数クロージャ使えると便利だよ!

ActionScriptでは、関数クロージャ使う場合と、使わない場合では、だいぶ書き方に差が出る。
関数クロージャ使った方が、綺麗に、短く書けるようになる。


ということで、メソッドクロージャ使ったパターンを紹介するよ!


関数クロージャってなに?っていう人は以下のサイトを読めばわかるかも。
Adobe ActionScript 3.0 * 関数のスコープ
メソッドクロージャとバインドメソッド at AS3S.ORG

その1 イベントのリスナーとして使う

イベントリスナーを作るとき、わざわざメンバとして作ってやるのはめんどくさい。
そんなときは関数クロージャ使えばいいよ!

普通に書いた場合

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(e:Event):void
		{
			trace("ENTER FRAME !");
		}
	}
}


関数クロージャを使用した場合

package 
{
	import flash.display.Sprite;
	import flash.events.Event;
	public class Main extends Sprite 
	{
		public function Main():void 
		{
			addEventListener(Event.ENTER_FRAME, function(e:Event):void {
				trace("ENTER FRAME !");
			});
		}
	}
}


関数クロージャを使えば結構コードがすっきりして、可読性が上がる。

その2 スコープとして使う

ASのスコープは関数単位。
Javaとは違ってブロック単位でのスコープはないよ。
つまり関数内では同じ変数を二個作ることは出来ないわけだ。
例えば関数内でfor文を二回以上使いたい場合、はループのための変数名を二個作るか、
変数を使いまわさないといけない。

public function Main():void 
{
	var xs:/*int*/Array = [];
	for (var i:int = 0; i < 5; i++ )
	{
		xs.push(i);
	}
	var sum:int = 0;
	for (var j:int = 0; j < xs.length; j++ )
	{
		sum += xs[j];
	}
	trace(sum);
}


この例だとiとjという2つの変数を使っている。
ただ、この書き方は、下のfor文で間違ってiを使ったりして、間違えやすい。


これを関数クロージャでくくってやると、スコープが働くので、
別々の関数クロージャなら同じ変数を宣言しても問題なくなる。

public function Main():void 
{
	var xs:/*int*/Array = [];
	(function():void{
		for (var i:int = 0; i < 5; i++ )
		{
			xs.push(i);
		}
	})();
	var sum:int = 0;
	(function():void{
		for (var i:int = 0; i < xs.length; i++ )
		{
			sum += xs[i];
		}
	})();
	trace(sum);
}


iを別々の場所で二回宣言しても、コンパイラに怒られることはない。

その3 配列の高階関数の引数として使う

例えば、配列の要素すべてに5を足した配列を作りたいとしよう。
普通にやるなら、for文で回すのが一般解だろう。

var xs:/*int*/Array = [0, 1, 2, 3, 4, 5];
var outs:/*int*/Array = [];
for (var i:int = 0; i < xs.length; i++)
{
	outs.push(xs[i] +5);
}
trace(outs);


しかし、配列にはそれにぴったりな関数が用意されてる。map()だ。

var xs:/*int*/Array = [0,1,2,3,4,5];
var outs:/*int*/Array = xs.map(
	function(x:int, index:int, xs:/*int*/Array):int { return x + 5; }
);
trace(outs);


ちなみにこれはもっと短く書ける。

trace([0, 1, 2, 3, 4, 5].map(function(x:int, ...rest):int { return x + 5; } ));


すっごい短く書けるよ!


配列には他にも、便利な関数が何個もある。
filter()とか、every()とか、some()とか。

ちなみに、map()とか、filter()とか、引数に関数をとる関数を高階関数っていうらしいよ。



関数クロージャ便利だからどんどん使っていこうぜ!


※追記
メソッド、メソッドクロージャ、関数クロージャ - 妄想宝箱