タイトル/名前 | 更新者 | 更新日 |
---|---|---|
画像/JavaScript/js_prototype_memo2_02.png | msakamoto-sf | 2009-02-14 13:35:27 |
JavaScript/constructor | msakamoto-sf | 2009-02-14 12:50:02 |
JavaScript/prototypeメモ1 | msakamoto-sf | 2009-02-11 22:56:04 |
日記/2009/02/10/眠れない。 | msakamoto-sf | 2009-02-10 02:59:26 |
日記/2009/02/08/明日も中央線か・・・。 | msakamoto-sf | 2009-02-09 00:01:00 |
技術/Apache/Cookieログ(1) | msakamoto-sf | 2009-02-08 21:29:50 |
YakiBiki | msakamoto-sf | 2009-02-08 20:30:21 |
JavaScript/PageNavi(1) | msakamoto-sf | 2009-02-08 18:11:07 |
JavaScript/var_scope | msakamoto-sf | 2009-02-08 16:12:34 |
日記/2009/01/27/Python勉強中 | msakamoto-sf | 2009-01-27 09:35:34 |
まず、関数を使ってオブジェクトを生成する典型的なサンプルを以下に示します。
function Klass(prop) { this.prop = prop; this.func = function() { return 'prop is ' + this.prop; }; } obj = new Klass('abc'); alert(obj.func()); // alerts 'prop is abc' for (i in obj) { alert(i); // alerts 'prop', 'func' (順不同) }
で、気になったのが「関数の中のthisが何でオブジェクトになるんだろう?」とか、「なんで只の関数呼び出しに newを付けると、オブジェクト生成になるんだろう?」とか、そんな感じ。
と言うわけで例によりECMA262を見てみます。
11.2.2 The new Operator ... The production NewExpression : new NewExpression is evaluated as follows: 1. Evaluate NewExpression. 2. Call GetValue(Result(1)). 3. If Type(Result(2)) is not Object, throw a TypeError exception. 4. If Result(2) does not implement the internal [[Construct]] method, throw a TypeError exception. 5. Call the [[Construct]] method on Result(2), providing no arguments (that is, an empty list of arguments). 6. Return Result(5).
頑張って訳してみます。
言語実装の詳細っぽくて難しそうですが、JavaScriptでいう「オブジェクト」って、裏側では色々な内部メソッド(プロパティ?)を持っているらしくって、その内の"Construct"というメソッドを実行しているようです。
"Construct"のようにECMA262の仕様書で二重角括弧で囲まれた語句は、正確には、説明用の便宜上のラベルとなっています。つまり実際に"Construct"という名前のメソッドが内部実装で存在するわけではなくて、仕様的に、このような動作をする機能を作って下さいね、という意味合いになります。
二重角括弧はWiki書式と衝突するので、今後は " (ダブルクォート)で囲って表記します。
では"Construct"が呼ばれる時は何が起こるのでしょうか?
ECMA262の"13 Function Definition"の中に、ズバリ、関数オブジェクトの"Construct"が呼ばれた時のステップが記述されています。
13.2.2 [[Construct]] ... When the [[Construct]] property for a Function object F is called, the following steps are taken: 1. Create a new native ECMAScript object. 2. Set the [[Class]] property of Result(1) to "Object". 3. Get the value of the prototype property of the F. 4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3). 5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the original Object prototype object as described in 15.2.3.1. 6. Invoke the [[Call]] property of F, providing Result(1) as the this value and providing the argument list passed into [[Construct]] as the argument values. 7. If Type(Result(6)) is Object then return Result(6). 8. Return Result(1).
頑張って訳してみます。
ようやく突き止めました。関数をコンストラクタとしてnewで使う時に"this"が使えるのは、言語実装が裏側でECMAscriptオブジェクトというオブジェクトを生成して、それをthisに割り当ててくれているからでした。また、newの結果として返されるのはそのECMAscriptオブジェクトでした。(コンストラクタがObjectを返す場合はそちらが優先)
なんでこんな疑問を持ったかというと、自分、てっきり「this」がprototypeを指すものとどこかで勘違いしていたからです。それでちょっと実験中に「アレ?」となって、確かめた次第です。
なお今回注目した "this" はnew演算子を付けた関数呼び出し中での "this" で、その他のケースは一切考慮してません。そこまで追いつめてないです、スミマセン。
※jdk1.6.0_01のjrunscriptを使ってます。
素朴な疑問です。
JavaScriptでオブジェクトを継承するのに、以下のように関数オブジェクトのprototypeプロパティに継承元オブジェクトを参照させる手法はよく見かけます。
Base = { prop1 : 'abc' }; function Klass() {}; Klass.prototype = Base; o1 = new Klass(); print(o1.prop1); // abc
一方、単純に「じゃぁprototypeというプロパティに継承元を設定させれば見に行くのだから、オブジェクトリテラルで生成したオブジェクトのprototypeプロパティに指定してもOK?」と思って試すと、上手く行きません。
Base = { prop1 : 'abc' }; o1 = {}; o1.prototype = Base; print(o1.prop1); // 'undefined' 'prop1' in o1; // false 'prototype' in o1; // true
何でなんだろう?ということでECMA262の仕様書をぽちぽちと拾い読みしてみました。
適当に読んでると混乱しますが、ECMA262の仕様書を読んでいると同じ"prototype"という語が二種類の表記で出てきます。
(1) "[[Prototype]] property" という表記(二重Bracket) (2) "prototype property" という表記(prototypeのフォントが異なる)
(1)の表記の場合、内部で管理している構造体の"Prototype"というプロパティを指します。JavaScriptのソースコード上でのprototypeとは別物になります。
(2)の表記の場合が、通常JavaScriptコードで出てくる"prototype"プロパティになります。大概"オブジェクト.prototype"という形でアクセスされます。
今回は(1)を "Prototype" とダブルクォート + 頭大文字で表記し、(2)を prototype と小文字だけで表記します。(二重BracketがWiki書式に引っ掛かってしまうため)
15.2.5 Properties of Object Instances
Object instances have no special properties beyond those
inherited from the Object prototype object.
オブジェクトリテラルで生成したオブジェクトは "new Object()" された結果と同様で、特別なプロパティは存在しません。つまり、冒頭の疑問で示した
o1.prototype = Base;
というコードは、継承元を示す意味のではなく、通常のプロパティと同じ意味でのprototypeプロパティに値を設定していたわけです。
Object o1 extends Base
じゃなくて
o1['prototype'] = Base
だった、みたいな。
ちなみにオブジェクトリテラルでの生成が "new Object()" であるのは以下の記述で知りました。
11.1.5 Object Initialiser Syntax ObjectLiteral : { } { PropertyNameAndValueList } ... The production ObjectLiteral : { } is evaluated as follows: 1. Create a new object as if by the expression new Object(). 2. Return Result(1).
JavaScriptのオブジェクトのプロパティには、内部的に幾つかの「属性」を持つようです。ECMA262の「8.6.1 Property Attributes」にその一覧があります。以下に簡単にまとめます。
例えばJavaScriptのオブジェクトの基本であるObjectオブジェクト自身はprototypeプロパティを持っていますが、しかしReadOnly属性が設定されるため、prototypeをJavaScriptのソースコードから変更することは出来ません。
15.2.3.1 Object.prototype
The initial value of Object.prototype is the Object prototype object (15.2.4).
This property has the attributes { DontEnum, DontDelete, ReadOnly }.
もちろん prototype プロパティが参照している先のオブジェクトにはアクセスできますので、
Object.prototype = YourKlass;
はNG(jrunscript上では特に異常は出ないが、printしてみると無視されている事がわかる)でも、
Object.prototype.your_func = function() {...};
はOKで、Prototype.jsなどはこれを活用しています・・・というか、していたと記憶しています。(このせいで、Prototype.jsはグローバルオブジェクトを"汚染"するから云々、という話をPrototype.jsが出た当初、見かけました。)
話を元に戻しますと、関数オブジェクトの prototype プロパティはJavaScriptのソースコードから変更可能です。というのは、DontDelete属性しか設定されないからです。(*1)
15.3.5.2 prototype The value of the prototype property is used to initialise the internal [[Prototype]] property of a newly created object before the Function object is invoked as a constructor for that newly created object. This property has the attribute { DontDelete }.
関数オブジェクトがコンストラクタとして実行される時、newされたオブジェクトの "Prototype" プロパティを初期化する為に使われる、と書いてあります。
ここで JavaScript/constructor を見返してみますと、コンストラクタ起動時の挙動で次のようにECMA262では説明されていました。
13.2.2 [[Construct]] ... When the [[Construct]] property for a Function object F is called, the following steps are taken: 1. Create a new native ECMAScript object. 2. Set the [[Class]] property of Result(1) to "Object". 3. Get the value of the prototype property of the F. ^^^^^^^^^^^^^^^^^^ 4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3). ^^^^^^^^^^^^^^^^^^^^^^
まさにその通りで、関数オブジェクトのprototypeプロパティがオブジェクトの時は、新規作成したオブジェクトの "Prototype" 属性にセットすると書かれています。
これでようやく、ECMA262の記述上で、冒頭の疑問が解決されかつ、関数オブジェクトのprototypeプロパティがどのように扱われるのか追えるようになりました。
朝8:00 - 8:30の中央線で、豊田 - 新宿、さらに大江戸線で新宿 - のぎゅうぎゅう詰め。
今から気分悪い。
現場は22:00 - 23:00上がりなので、自室に戻る頃には日が変わってて、お風呂入ったりしてたら2時とか。
4時間とか5時間睡眠が耐えられない体っぽくて、最低6時間は寝てないと不味い。
(ここ数年はその傾向。先日健康診断で朝7時とかに無理矢理起きたのだけれど、午後一杯まるで使い物にならなくて、その次の次の日には体調崩して休んでしまった。もう体がだるくて溜まらなくて、朝8:00とかに起きられたのだけれど胸焼けがひどくて頭がぐるぐるしてて、なんかこのまま電車乗ったら確実にヤバイ雰囲気がしてそのまま倒れ込んだ。その前の晩は気分が沈みまくってて、24:00を回ったcocosでヤケ食いして自室に戻り、風呂にも入らずそのまま布団の中で仕事のこととかで悶々と暗いことばかり考えてた。結局ヤケ食いで毎日平均2,000 - 3,000円は浪費してる。)
どうにも、困った。
というか、何でみんな耐えていられるのか、そちらの方が不思議でならない。中央線の朝の満員電車で時々急ブレーキがかかると、マジで通路中央の人で一人が倒れて、将棋倒しでどさどさって行くんだけど、よく圧死とかでの死者が出ないなと。そっちの方に感心する。あとは朝9:30 - 9:45台の大江戸線の新宿 - 六本木間とか。
うーぬ、本当、どうにも弱った。
(Apache 2.0, WindowsXP)
ApacheのログでCookieを出力したい場合は、mod_log_configの機能として
%{Cookie名}C
というフォーマット文字列が使える。
クライアントが
Cookie: cookie1=123, cookie2=456
というCookieヘッダーを送ってきたら、
LogFormat "... %{cookie1}C ..." xxxx
というLogFormatを使えば "123" がログに記録される。
該当するCookie名が送られてこなければ、単に "-" として記録される。
LogFormat "%{sesstest}C,%t,%h,%>s,%D,%m,%U%q" cookielog
とかすると
9fe78323861e33bc4cac7a2142655d72,[05/Jan/2009:11:00:53 +0900], \ 127.0.0.1,200,31250,GET,/sess/test1.php?a=b
という感じになり、CSVとしてExcelとかに読み込めばソートやフィルタがかけやすくて良いかも。("\"で折り返してます)
注意すべきは、あくまでもクライアントが送信したCookieヘッダーが記録されるため、Cookieをクリアしたクライアント乃至は初めてアクセスするクライアントについては "-" として出力されてしまう。
サーバーが送出した "Set-Cookie" までは取り出してくれない。
ちなみに
・「第13回 Apacheのログを活用しよう」
http://www.atmarkit.co.jp/flinux/rensai/apache13/apache13b.html
で紹介されている mod_usertrack は、静的コンテンツメインのサイトの場合にも「Apache側で」ユーザ追跡に使えるCookieを送信してしまおうというモジュール。
なので、コンテンツのほとんどがWebアプリの場合(CMSやSNSなど)は、不要かもしれない。CMSやSNSの基盤側で常にsession cookieをチェックしていたりするのであれば。(んー、でも、Set-Cookieされていない「未ログイン」ユーザーというのがありえる場合は、ユーザ追跡用のCookieを別立てでmod_usertrackで仕込んでおいたほうが良いかも・・・。)
YakiBiki : Yet another Blog like Wiki
個人サイトで「荒らし」を気にせずにWikiを使いたい、あるいは数十人程度までの社内イントラでアクセス制御に注意して使いたい、みたいな場合にお奨めできます。
お仕事で、JavaScriptで「前へ」「次へ」を実装することになりました。まぁ、jQuery使ってOKで、ちゃんとレビューしてくれる技術者も居ますので勉強がてら試作してみます。
横方向に滑らかにスクロールするのは「カルーセル」(carousel)と呼ばれてるらしく、こんなのもあるようです。
今回は単純にページ遷移無しで表示が切り替われば良いので、ページの単位となるdivタグブロックを"display: none"でページ数分出力しておき(実際はプログラムが動的に生成すると思われます)、「前へ」「次へ」クリックされた時にdisplayを順繰りに"block"にしていけばOKっぽいです。というか動作原理として。
というわけで、ちょっと長いですが試作品第一号。
"var"の機能について、以下の"ディノオープンラボ"でサンプルと共に解説されていたので、試してみる。
無名関数と関数リテラルについては置いておいて、単純なStringオブジェクトで試してみます。
baz = 'abc'; function(){ alert(baz); // (1) alerts 'undefined' var baz = 'def'; alert(baz); // (2) alerts 'def' }(); alert(baz); // (3) alerts 'abc'
関数コードの中でvarを使うと、実行スコープにundefinedでbazという識別子が登録されます。
ですので(1)のalert()ではundefinedになります。(2)のalert()時点で、関数の実行スコープのbazには 'def' という値が入ってますので、それがalert()で表示されます。
(3)のalert()ですが、(1) - (2) で参照されているbazはあくまでも無名関数による実行スコープに登録されたものであって、グローバルスコープのbazは弄ってません。なので、最初に設定していた 'abc' がそのままalert()で表示されます。
で、試しに関数の中の var を取ってみます。
baz = 'abc'; function(){ alert(baz); // (1) alerts 'abc' baz = 'def'; alert(baz); // (2) alerts 'def' }(); alert(baz); // (3) alerts 'def'
こうすると関数コード中もグローバルスコープのbazを参照するようになるので、(3)でも変更後の値 'def' がalert()表示されます。
ちなみに実行スコープにより分断されるのは関数コードだけであって、一般的なBlock構文では実行スコープは生成されません。
foo = 'ABC'; { alert(foo); // (1) alerts 'ABC' var foo = 'DEF'; alert(foo); // (2) alerts 'DEF' } alert(foo); // (3) alerts 'DEF'
これについてはECMA262に記述されています。
12.2 Variable statement
...
A Block does not define a new execution scope.
Only Program and FunctionDeclaration produce a new scope.
Variables are initialised to undefined when created.
「実践Python」でPython入門中。
最初はインデントが慣れなかったけど、"\"(バックスラッシュ)で折り返せる事も分かったし今は平気。
Rubyもそうだったけれど、行末の";"を省けるのは楽。
あと、文法として覚えることがあまり無くて(入門レベルなら当然か)、楽。
元々Tracを操る上で最低限度の教養が欲しくて始めたのだけれど、ふと思い出せばTurboLinuxServerのCUIのパッケージアップデート(turbopkgコマンド)とか、RedHat系の管理ツールとか、意外とPythonベースのツールが多い。
勉強しておいて損はなさそうな感じです。特にC言語とのBindingとか。