スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
JavaScriptのすごくつまづきやすいところ
setTimeoutにthisを渡せない

ダメな例:
var c = {
         name : 'bingo',
         func : function(){ alert(this.name) }
        };

setTimeout(c.func, 100);


いける例:
var c = {
         name : 'bingo',
         func : function(){ alert(this.name) }
        };

setTimeout(function(){c.func()}, 100);

ダメな理由:
function(){ alert(this.name) }だけが、setTimeoutに切り離されて渡されるため。
setTimeout(function(){ alert(this.name) }, 100);
と書いたのと同じことになってしまう。


setTimeoutに引数がわたせない

ダメな例:
var c = {
         name : 'bingo',
         func : function(age){ alert(this.name + ':' + age) }
        };

setTimeout(c.func(14), 100);


いける例:
var c = {
         name : 'bingo',
         func : function(age){ alert(this.name + ':' + age) }
        };

setTimeout(function(){c.func(14)}, 100);

ダメな理由:
setTimeoutには無名関数というか関数オブジェクトを渡さないといけないため

参考:
http://d.hatena.ne.jp/bingo_nakanishi_perl/20090207/1233969698
http://d.hatena.ne.jp/cloned/20070301
http://labs.cybozu.co.jp/blog/kazuho/archives/2006/12/oo-settimeout.php
まるごとJavaScript&Ajax!
スポンサーサイト
JavaScriptの秘密
for( i in window ){
  console.log(i);
}

このソースを実行すると、
window
__scope__
console
document
addEventListener
__firebug__
location
navigator
Packages
sun
java
netscape
XPCNativeWrapper
Components
parent
removeEventListener
frames
length
top
scrollbars
name
scrollX
scrollY
scrollTo
scrollBy
getSelection
scrollByLines
scrollByPages
sizeToContent
dump
setTimeout
setInterval
clearTimeout
clearInterval
setResizable
captureEvents
releaseEvents
routeEvent
enableExternalCapture
disableExternalCapture
prompt
open
openDialog
find
self
screen
history
content
menubar
toolbar
locationbar
personalbar
statusbar
directories
closed
crypto
pkcs11
controllers
opener
status
defaultStatus
innerWidth
innerHeight
outerWidth
outerHeight
screenX
screenY
pageXOffset
pageYOffset
scrollMaxX
scrollMaxY
fullScreen
alert
confirm
focus
blur
back
forward
home
stop
print
moveTo
moveBy
resizeTo
resizeBy
scroll
close
updateCommands
atob
btoa
frameElement
dispatchEvent
getComputedStyle
sessionStorage
globalStorage

このように、windowのキー(つまり、プロパティまたはメソッド)が表示される。

では、つぎのようにしてみよう。
function hoge(){ }

for( i in window ){
  console.log(i);
}
hogeという関数を用意してみた。

結果はこうだ
__scope__
hoge
i
window
console
document
addEventListener
__firebug__
location
navigator
Packages
sun
java
netscape
XPCNativeWrapper
Components
parent
removeEventListener
frames
length
top
scrollbars
name
scrollX
scrollY
scrollTo
scrollBy
getSelection
scrollByLines
scrollByPages
sizeToContent
dump
setTimeout
setInterval
clearTimeout
clearInterval
setResizable
captureEvents
releaseEvents
routeEvent
enableExternalCapture
disableExternalCapture
prompt
open
openDialog
find
self
screen
history
content
menubar
toolbar
locationbar
personalbar
statusbar
directories
closed
crypto
pkcs11
controllers
opener
status
defaultStatus
innerWidth
innerHeight
outerWidth
outerHeight
screenX
screenY
pageXOffset
pageYOffset
scrollMaxX
scrollMaxY
fullScreen
alert
confirm
focus
blur
back
forward
home
stop
print
moveTo
moveBy
resizeTo
resizeBy
scroll
close
updateCommands
atob
btoa
frameElement
dispatchEvent
getComputedStyle
sessionStorage
globalStorage

上から2つ目に注目である!
なんと、 hoge がいるではないか!!!!
つまり、 hogeという関数を用意したら、
あなたの知らないうちに、 windowオブジェクトが書き換えられていたのだぁああああ!!!

ということは、
hoge()
と呼び出すかわりに、
window.hoge()
とも書けるというわけである。


あれ、一回目の結果に i がいないなぁ......... なぜ....
JavaScriptいろいろ試す
あけまして、おめでとうございます。
今年もよろしくお願いします。

「 DOM Scripting 標準ガイドブック ~やさしく学ぶ、JavaScriptとDOMによるWebデザイン~ (Web Designing BOOKS) (単行本(ソフトカバー)) 」
という本がすこぶるわかりやすかったので、いろいろ試したくなった。
DOMを扱う方法や、windowを扱う方法などが、非常にうまく整理されており、いままで疑問に思っていた箇所を体系的に学ぶことができた。

つぎのHTMLを使っていろいろやってみる。
<html>
  <head><title>実験1</title></head>
  <body>
  あああああ
  <p>
  PPPPPPP
  </p>
  いいいいい
  <div id="my1" style="width:30px; background-color: red; position:absolute;">
  my1
  </div>
  </body>
</html>


getElementsByTagName()
で取得したタグがどのように配列に入っているか知る方法。(firebugsを用いる ※先ほどの本では、firebugsは紹介されていない)
var f = document.getElementsByTagName('*');

for(var i=0; i<f.length; i++){
   // 番号 : [objct HTML ○○ Element] の形式で表示
  console.log(i + ' : ' + f[i]); 

  // 開始タグを表示  
  console.log(f[i]);                  
}

結果:
0 : [object HTMLHtmlElement]
<html>
1 : [object HTMLHeadElement]
<head>
2 : [object HTMLTitleElement]
<title>
3 : [object HTMLBodyElement]
<body>
4 : [object HTMLParagraphElement]
<p>
5 : [object HTMLDivElement]
<div id="my1" style="width: 30px; background-color: red; position: absolute;">

よって、bodyが配列のインデックス3番目であるのがわかるので、
var f = document.getElementsByTagName('*')[3]
                .getElementsByTagName('*');

for(var i=0; i<f.length; i++){
  console.log(i + ' : ' + f[i]);
  console.log(f[i]);
}
とすれば、 bodyタグの持つタグを取得できる。

タグの要素に限らず、bodyの要素を調べるには次のようにすればよい。
var body_childs =
     document.getElementsByTagName('body')[0]
             .childNodes;

for(var i=0; i<body_childs.length; i++){
  console.log(i + ' : ' + body_childs[i] );
  console.log(body_childs[i]);
} 

結果:
0 : [object Text]
"\n あああああ\n "
1 : [object HTMLParagraphElement]
<p>
2 : [object Text]
"\n いいいいい\n "
3 : [object HTMLDivElement]
<div id="my1" style="width: 30px; background-color: red; position: absolute;">
4 : [object Text]
"\n \n"

これにより、「あああああ」は、インデックス0 「いいいいい」は、インデックス2であることが、わかったので、
document.getElementsByTagName('body')[0]
        .childNodes[0].nodeValue = 'AAAA';

document.getElementsByTagName('body')[0]
        .childNodes[2].nodeValue = 'BBBB';
で、それぞれ、「AAAA」と「BBBB」に置き換えることができる。




お次は、次のHTMLを使って、クリックされた要素自体を知る方法を試す。
<html>
  <head><title>実験1</title></head>
  <body>
  <p onclick="(function(self){ alert(self); })(this)"> PPPPPPP </p>
  <p onclick="function hoge(self){ alert(self); }; hoge(this);"> QQQQQ </p>
  </body>
</html>
簡単な話だ。 onclickの中で、thisを引数にして、関数を呼べばよい。
しかし次のように、
<p onclick="(function(){ alert(this); })()"> PPPPPPP </p>
では、 thisは、windowになってしまう。
だが、次のようにすれば、 this は 自分自身だ。
<p onclick="alert(this);"> PPPPPPP </p>
要は、地の文だと、自分自身になるが、呼び出す関数の中のthisはwindowになるため、関数呼び出しに自分自身を伝えたい場合は、引数として、thisを与えてやらねばならないし、関数の方は受けっとた thisをなんらかの変数(今回はselfという名前にした)に入れて処理していかねばならない。



今度は、次のHTMLを使って、onclickなどのイベントのために、HTMLタグを使うのではなく、JSから、イベントをセットする方法を試す。
<html>
  <head><title>実験1</title></head>
  <body>
  <p> PPPPPP </p>
  </body>
</html>

pタグにonclickイベントを付けるなら、次のようになる。
document.getElementsByTagName('p')[0].onclick 
  =
function() {
  alert(this);
}

関数呼び出しをしたいなら、
document.getElementsByTagName('p')[0].onclick 
  =
function() {
  (function(self){
    alert(self)
  })(this);
} 
とやはり、こんな感じで、引数に、thisを与えてやる必要がある。
次のようなthisは、やはりwindowになってしまう。
document.getElementsByTagName('p')[0].onclick 
  =
function() {
  (function(){
    alert(this)
  })();
}


つまり、HTMLタグに直にJSを書き込まないこちらの方法は、
onclick=" なんか処理 "    // HTML直書きバージョン
のところが、
onclick=function(){ なんか処理  }  //JSによるセットバージョン
に置き換えられたと考えられることがわかる。



今度は、つぎのHTMLをJSで作成する方法を試す。
なお、JavaScriptを埋め込むHTMLには既に、htmlタグとheadタグとbodyタグ(※すべてのタグは空タグ)を用意してあるものとする。
<html>
  <head><title>実験1</title></head>
  <body>
  あああああ
  <p>
  PPPPPPP
  </p>
  いいいいい
  <div id="my1" style="width:30px; background-color: red; position:absolute;">
  my1
  </div>
  </body>
</html>

JSは、
// titleを作成して追加
var element_html  = document.getElementsByTagName('html')[0];
var element_head  = document.getElementsByTagName('head')[0];
var element_body  = document.getElementsByTagName('body')[0];

var element_title = document.createElement('title');
element_title.appendChild( document.createTextNode('実験1') );
element_head.appendChild( element_title );
element_html.insertBefore( element_head, element_body );

//pとdivを作成して追加
element_body.appendChild(
  document.createTextNode('あああああ')
);  

var element_p     = document.createElement('p');
element_p.appendChild( document.createTextNode('PPPPPPP') );
element_body.appendChild( element_p );

element_body.appendChild(
  document.createTextNode('いいいいい')
);

var element_my1   = document.createElement('div');
element_my1.setAttribute('id', 'my1');
element_my1.setAttribute('style',
                         'width: 30px; background-color: red; position: absolute;');
element_my1.appendChild( document.createTextNode('my1') );
element_body.appendChild(element_my1);

となる。

以上の、JSを作成中に気づいたが、
console.log(
 document.getElementById('my1')
         .appendChild( document.createTextNode('hoge') )
);
を試すと結果は、
"hoge"
になってしまう。
つまり、appendChildの返り値は、appendChildを呼び出すそれ自身ではなく、append(追加)した要素なのだ。だから、次のように、createしてappendしたものをいきなり、親の要素にappendしようとしても思ったようにはいかない。
document.getElementById('my1')
         .appendChild( document.createElement('div')
                               .appendChild(document.createTextNode('foo'))
                     )

これは、結局つぎのように解釈されてしまう。
document.getElementById('my1')
         .appendChild( document.createTextNode('foo') )
なぜなら、
document.createElement('div').appendChild(document.createTextNode('foo')
の部分で最後に評価されるのは、 appendChildのところであるが、
これが返すのは、「付け加えた要素」だからである。
だから、document.createElement('div') の部分は、divタグは作られはするものの結局どこにも加えられないことになってしまう。

思ったようにするには、次のように丁寧に変数に代入していかねばならない。
var element_div = document.createElement('div');
element_div.appendChild( document.createTextNode('foo') );

document.getElementById('my1')
        .appendChild( element_div )




HTMLの見た目を変えたければ、styleプロパティを使えばよい。
さきほどのHTMLを使って試してみる。
console.log(
 document.getElementById('my1')
         .style
)

結果:
["width", "background-color", "position"]
この、 width, background-color, positionによって値を読み込めたり、書き込めたりできる(と思いきや、background-colorは、backgroundColorでアクセスしなければならない Firebugsはいったいなにを表示したのだろうか..........)

こんな感じで値を取得できる。
console.log(
 document.getElementById('my1')
         .style.width
)

console.log(
 document.getElementById('my1')
         .style.backgroundColor
)

console.log(
 document.getElementById('my1')
         .style.position
)
結果:
30px
red
absolute

この、style以下のプロパティを使って値を書き換えれる。
document.getElementById('my1').style.width = '50px';
document.getElementById('my1').style.backgroundColor = 'blue';




お次は、バックが赤色のAという文字をクリックすると、進みだし、
進んでいるAをクリックするとストップするというJSである。
HTMLは以下のを使う。
<html>
  <head><title>実験1</title></head>
  <body>
  <div id="my1" style="left: 5px; width:30px; background-color: red; position:absolute;">A</div>
  </body>
</html>

JSは以下の通りとなる。(どなたが、moveにthisを渡す方法をご存知ないだろうか?)
var my1 = document.getElementById('my1');
my1.flag_click = 0;

function move(id){
  var f = 'move(' + "'" + id + "'" + ')';
  self = document.getElementById(id);
  self.style.left = parseInt(self.style.left) + 2 + 'px';
  self.t_id = setTimeout(f, 50);
}

my1.onclick = function(){
  var f = 'move(' + "'my1'" + ')';
  if ( this.flag_click == 0 ){
    this.flag_click = 1;
    this.t_id = setTimeout(f, 50);
  } else {
    this.flag_click = 0;
    clearTimeout(this.t_id);
    alert('You Stoped!');
  }
}


次のようにこのJSは試してもらうことができる。(ただし、クリック以外に止める方法を実装していないので注意してほしい)
A






fromタグは、inputタグに値を入れれたりと結構特殊なので、Formオブジェクトが存在する。以下は、Formオブジェクトを使う例である。
HTMLは、
<form method="POST" action="cgi-bin/xxx.cgi">
<div>名前:<input type="text" name="NAME" /></div>
<div>住所:<input type="text" name="ADDR" /></div>
<input type="submit" value="送信">
<input type="reset" value="取消">
</form>
を使う。(参考:http://www.tohoho-web.com/html/form.htm)

JS
console.log(
 document.forms
);
結果:
[form xxx.cgi]


console.log(
 document.forms[0].elements
);
結果:
[input, input, input 送信, input 取消]


var form_in = document.forms[0].elements;

for(var i=0; i
結果:
送信
取消

「名前」と「住所」に、「hoge」と「foo」をそれぞれいれておれば、 結果は、
hoge
foo
送信
取消
となる。



おつぎは、お待ちかねのAjaxである。
↓の「ここが置き換わる」の部分をクリックして欲しい。
さすれば、アラートが出てから、私がこのブログではじめてかいた記事の内容に置き換わるはずである。(※ Firefoxのみしか対応していない)


(クリック!) ここが置き換わる (クリック!)


変わったでしょうか? どなたか brタグを改行にする方法をご存知ないだろうか?(現状では、brタグがそのまま文字として表示されてしまう)

気になるソースは次のようになっている
<div id="first_entry" onclick="hogera(this)"> (クリック!) ここが置き換わる (クリック!)</div>
<br /><br />
<script>
function hogera(self){
httpObj = new XMLHttpRequest();
httpObj.onload = function(){
 if( this.readyState == 4 ) {
   var str = this.responseText;
   str = str.replace(/\n/g, 'NNN');
   str.match(/<div class="topentry_body" id="1">(.*?)<\/div>/);
   alert(RegExp.$1);
   self.childNodes[0].nodeValue = RegExp.$1;
  }
};
httpObj.open("GET","http://bingobingobingo.blog49.fc2.com/blog-entry-1.html",true);
httpObj.send(null);
}
test
初級1
初級2
上級1
中級
上級2

ということで、
http://bingobingobingo.blog49.fc2.com/blog-entry-892.html
にボタンをつけてみました。
如何にJavaScriptとPerlは似ているか
JavaScriptとPerlは、そっくりさん。
よく似てらっしゃいます。


配列をみてみよう。
JavaScript
arr = [1,2,3];

alert(arr[2]);
Perl
$arr = [1,2,3];

print $arr->[2];


ハッシュをみてみよう。
JavaScript
h = {
      A :'a',
      B :'b',
      C :'c'
    };

alert(h.A);
Perl
$h = {
       'A'=>'a',
       'B'=>'b',
       'C'=>'c'
     };

print $h->{'A'};


関数をみてみよう。
JavaScript
f = function(){ alert("Hello") };
f();
Perl
$f = sub { print 'Hello'; };
$f->();




その場で作って、その場で実行も同じようにできるよ。

配列
JavaScript
alert(([1,2,3])[2]);
Perl
print +([1,2,3])->[2];

ハッシュ
JavaScript
alert(({
        A:'a',
        B:'b',
        C:'c'
      }).A);
Perl
print +({
         'A'=>'a',
         'B'=>'b',
         'C'=>'c'
       })->{'A'};


関数
JavaScript
(function(){ alert("Hello") })();
Perl
(sub { print 'Hello'; })->();




でも、まあ構文(書き方)で似てるやつを選んだらの話で、
JavaScriptは全部ハッシュだったりとか、いろいろ作りは違うけどね。
上記の例で同じような書き方で、同じように振る舞う場合があることが、
多いのはわかってもらえると思う。


関数を作ってその場で実行は、スコープの問題を解決するために、
JavaScriptでは最近多用されまくるけど、Perlでは、その問題はないため、
使われる箇所は全然違ってくる。

結局のところ、どのタイミングで、どの箇所にどのように書くか は、
長年、その言語を読み書きしていないと見えてこない。
構文(書き方)が似てるからといって、むこうの言語と同じようなタイミングで 書けば異端児扱いになるだろう。


Perlでは、とくに意味がなければ、関数は、
$f = sub { };
ではなく、
sub f{};
にしてほしいし、

配列は、とくに意味がなければ、
@arr = (1,2,3);
としてほしいものだ。
ブログ検索

プロフィール

ビンゴ中西
Perlが好きである。
プログラミング言語のほとんどは独学。独学の過程で多くのプログラム仲間にも色々教わりました。

FC2カウンター

カレンダー

07 | 2017/08 | 09
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -

ブロとも申請フォーム

この人とブロともになる

| ホーム | 次のページ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。