最強オブジェクト指向言語 JavaScript 再入門!
Upcoming SlideShare
Loading in...5
×
 

最強オブジェクト指向言語 JavaScript 再入門!

on

  • 164,618 views

この資料では、JavaScript でオブジェクト指向プログラミングを行う際に備えておくことが望ましい、基礎知識や概念について解説します。 ...

この資料では、JavaScript でオブジェクト指向プログラミングを行う際に備えておくことが望ましい、基礎知識や概念について解説します。

【対象者】
・JavaScript でアプリケーションを構築できる方
・JavaScript におけるオブジェクト指向プログラミングの
 実現手法や原理への理解を深めたい方
・Java 的なクラスベースの言語との違いに違和感や混乱を
 感じてらっしゃる方

Statistics

Views

Total Views
164,618
Views on SlideShare
138,386
Embed Views
26,232

Actions

Likes
768
Downloads
89
Comments
7

76 Embeds 26,232

http://morizyun.github.io 20164
https://twitter.com 1235
http://bikkuri.me 867
http://blog.livedoor.jp 768
http://s.deeeki.com 667
http://www.logicalyze.net 404
https://cybozulive.com 254
http://naoyashiga.hatenablog.com 219
http://kothiba.sakura.ne.jp 147
http://feedly.com 143
http://blog.everyleaf.com 131
http://www.mitsurugi.pw 128
http://kotaroito.hatenablog.com 112
http://doriven.hatenablog.com 95
http://absol-ex.hatenablog.com 94
http://m-j2a-web.nagatani.gifu.jp 72
http://a-hum.unoke.pfu.co.jp 65
http://notes.shuntak.net 63
https://sonic.qiita.com 55
http://admin.blog.fc2.com 48
http://localhost 44
http://tweetedtimes.com 42
http://wiki.onakasuita.org 36
http://www28422ue.sakura.ne.jp 34
http://cashew.hatenablog.com 32
http://www.google.co.jp 29
http://b.hatena.ne.jp 26
http://feeds.feedburner.com 21
http://mattarila.wkeya.com 20
http://slshmatome.blog.fc2.com 19
http://connpass.com 17
https://www.chatwork.com 15
http://awtcms.sakura.ne.jp 14
http://ayahito2828.tumblr.com 12
http://0.0.0.0 10
https://kcw.kddi.ne.jp 9
http://akiyoko.hatenablog.jp 9
http://espochnutri.blogspot.com 8
http://slideck.herokuapp.com 8
http://webcache.googleusercontent.com 6
http://blog.hatena.ne.jp 6
http://slideshare-download.seesaa.net 6
http://s.deeeki.stg 5
http://192.168.33.10 5
http://feedproxy.google.com 5
https://www.google.co.jp 4
http://plus.url.google.com 4
http://cloud.feedly.com 4
http://hashtagsjp.appspot.com 3
http://t.co 3
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-ShareAlike LicenseCC Attribution-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • @kaindu さん、できました! ありがとうございます! 見逃してたのかな…
    Are you sure you want to
    Your message goes here
    Processing…
  • MyUploads→対象のスライドのチェックボックスにチェック→Edit→Settings→PrivacySettingsタブ→Allow users to download→Yesを選択→Updateボタンを押下。で変更できないでしょうか?
     この操作は無料ユーザーでも可能です。
    Are you sure you want to
    Your message goes here
    Processing…
  • スライドを更新しました。誤字脱字を修正し、「余談ですが…」のページだけ少し追記しました。再読いただく程の更新ではありません。
    Are you sure you want to
    Your message goes here
    Processing…
  • 次の URL で PDF 版を配布しています。
    http://foreignkey.jp/wp-content/uploads/2013/07/js-javascript-oop-reintroduction.pdf
    Are you sure you want to
    Your message goes here
    Processing…
  • はてブ他でコメントいただいた中で、.next の class 構文や内部仕様の [[Class]]、またそもそもの「クラス」の定義についてご意見頂いたので、簡単にですが捕足します。 ◆まず、このスライドの目的が「分かり易く説明する」ことでしたので、そのために「クラスは忘れなさいと言い切る」ことが大切だと思ってこのような記述・構成になっています。プロトタイプチェインの説明を __proto__ 属性から始めたのも同様で、分かり易さを優先したためです。違和感のある方には申し訳ないのですが、概念的な理解という意味で無理の無い範囲の単純化だとは思っています。◆.next で導入される class 構文についても同様で、現時点で説明するのは適切でないと思ったことと、いずれにしても同構文はプロトタイプチェインのシンタックスシュガー的な意味合いが強いと理解しているので、本質的な理解を優先するためにも説明は不要と思いました。◆「クラス」という言葉の定義については、本稿では明らかに「Java的なクラス」をその意図として利用しています。実は本稿を書くキッカケとなったのが、そういった Java 的なクラスの概念で JavaScript の仕組みを理解しようとした記事が Web 上に多いことに気付いたことだったので、そのような意図となっています。実際のところ、この辺りで悩まれる方の多くが「Java的なクラス」の概念から離れられなくて悩まれているように思いましたもので、そのあたりを一歩進めることが出来ればという意図でまとめさせて頂きました。◆えっと、以上ですぅ。
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

最強オブジェクト指向言語 JavaScript 再入門! 最強オブジェクト指向言語 JavaScript 再入門! Presentation Transcript

  • 最強オブジェクト指向言語 JavaScript 再入門!
  • 自己紹介 ‣ ノジマユウジ @yuka2py ‣ 株式会社フォーエンキー 代表取締役 ‣ システム開発、 グラフィックデザイン、 DTPや印刷なども ‣ PythonとJavaScriptが大好 き(Dartに興味深々) ‣ 様々なWebアプリケーション 設計・構築・運用。またWP によるシステム開発、プラグ イン開発などが主なお仕事 ‣ おしゃれも大好き☆ リボンやお花が好き☆ ‣ 参加コミュニティ ● WordBench 神戸 ● HTML5-WEST.jp ● 日本Androidの会 神戸支部 絶賛 お仕事募 集中
  • 去年のボク Python 1%Design 15% iOS 4% Android 10% Web(PHP/JS) 10% Windows(C#) 20% お嫁 40% お 嫁 W i n d o w s ( C # ) W e b ( P H P / J S ) A n d r o i d i O S D e s i g n P y t h o n 2012年11月2日 株式会社フォーエンキー調べ
  • 最近のボク Python 1% Design 5% Android 5% Windows 15% Web/WordPress 25% お嫁 50% お 嫁 W e b / W o r d P r e s s W i n d o w s A n d r o i d D e s i g n P y t h o n 2013年6月1日 株式会社フォーエンキー調べ 愛 恐
  • はじめに 本セッションでは、JavaScript でオブジェクト指向プログ ラミングを行う際に備えておくことが望ましい、基礎知識 や概念について解説します。 対象者 ・JavaScript でアプリケーションを構築できる方 ・JavaScript におけるオブジェクト指向プログラミングの  実現手法や原理への理解を深めたい方 ・Java 的なクラスベースの言語との違いに違和感や混乱を  感じてらっしゃる方
  • Simple and Powerfull JavaScript はとてもシンプルな言語仕様でありながらも、 とても強力にオブジェクト指向プログラミングをサポートしています。
  • var obj = {} JavaScript はオブジェクトが基本です。
  • オブジェクトの構造は、単純なキーと値の組み合わせによるハッシュテーブルのよ うなものです。とても大切な概念です。難しく考え過ぎないでください。 var obj = { key: value }
  • 関数は、JavaScript において「第一級オブジェクト」…簡単に言うと「値」です。 値ですから、変数に代入したり、取り出したり、受け渡しが制約なく行えます。 var f = function() {...}
  • もちろん、関数をオブジェクトのプロパティに格納する事もできます。オブジェクトが 単純なハッシュテーブルで、関数も値であるならば、それはとても自然なことですね。 var obj = { key: function() {...} }
  • 言い換えると、オブジェクトのメソッドはそれをメソッドとして持つオブジェクト に束縛されているのではなく、 オブジェクトによってただ参照されているだけです。 var obj = { key: function() {...} }
  • おしながき ‣ クラスはあるか? ‣ プロトタイプチェイン ● JSでのオブジェクト指向プ ログラミングの基礎概念 ‣ オブジェクトの生成 ● newとコンストラクタ関数 ● プロトタイプチェインとの 関連 ‣ スコープチェイン ● var の意味 ● スコープチェインの正体 ‣ クロージャ ● 定義された環境を束縛する ‣ this ● thisの決定 ● thisのコントロール
  • クラスはあるか? 追記。本資料では、いわゆる「Java言語的なクラス」を「クラス」と定義してお話しています。それは、こ のスライドを書きはじめた動機が、Web上でJava言語的OOPで解釈しようとする記事が多くあり、それが JSを学ぼうとされる方の理解を混乱させているように思えたので、その混乱を解消したかったからです。 また、ES.next の class 構文も説明するには早そうなこと、かつさらに理解を難しくするものとして割愛。 なお、これらの省略や定義付けに違和感を覚えられる方は既に本資料の内容は理解済みとの認識です! (*'-'*)
  • QJavaScript で クラスは作れますか? (?_?)
  • A無理ポ。
  • Qでは、クラスっぽいものは 作れますか? (?_?)
  • Aだから、無理ポ。
  • だって「オブジェクト」しか 無いんだもん。
  • 「クラス」って何それ?
  • おいしいの?
  • (・ ・)
  • クラスっぽいものは出来るっていう方も いらっしゃると思いますが…
  • それが理解を難しくする と僕は思う クラスが無いのに、クラスの概念を持ち込んで 理解しようとしたら、メンドクサクなりそうでないっすか?
  • そもそも…
  • オブジェクト指向に
  • クラスなんて要らない。 クラスや型は、オブジェクト指向を 扱い易くするためのテクニックのひとつに過ぎません。
  • 必要なのはオブジェクト。 だって「オブジェクト指向」だしぃ∼
  • だから JavaScript は 代わりに、
  • もっと面白い方法を選んだ。 (↑あくまでも個人的主観)
  • それが…
  • プロトタイプチェイン
  •  「クラス」とは?の前に
  • 似たオブジェクトを 量産するための仕組み
  • Class (型) クラス クラスという「型」を元にして、 オブジェクトを生成するイメージ クラスから生成されたオブジェクトは クラスの特性(属性や機能)を受け継ぐ Object(実体) new Object(実体) new Object(実体) new
  • でも、 クラスを使わなくても、 同じ機能や属性(性質)を持った オブジェクトを量産できれば OKですよね。
  • では、JavaScript では?
  • Object (実体) (prototype) プロトタイプ オブジェクトは、自分自身が備えて いない特性(機能や属性)を、別の オブジェクトにお願い(委譲)する イメージ。この時、お願いする先を 「プロトタイプ」と言ったりします。 Object(実体) dele gate Object(実体) dele gate Object(実体) dele gate
  • …な、感じで実現します。
  • コードで見てみる。
  • 3つのオブジェクトが登場し ています。 ここで、3つ目の、objC は __proto__ 属性以外何も持っ ていませんが、I Love Nicole と表示されます。 I Love Nicole var objA = { name : 'Yome', say : function () { alert('I Love ' + this.name); } }; var objB = { name : 'Nikole' }; objB.__proto__ = objA; var objC = { }; objC.__proto__ = objB; objC.say();
  • ここでポイントになるのは、 __proto__という属性。 この__proto__を介して、 objC が objB を経て objA ま での参照を保持できているこ とを確認してください。 所有 所有 var objA = { name : 'Yome', say : function () { alert('I Love ' + this.name); } }; var objB = { name : 'Nikole' }; objB.__proto__ = objA; var objC = { }; objC.__proto__ = objB; objC.say();
  • I Love Nicole が表示される仕組み
  • objC.say() メソッドのコール (objB) objC.say 無い アッタ! 無い objC.__proto__.say (objA)(objB) objC.__proto__.__proto__.say objC に対して say() がコールされた時、JavaScript は、まず objC 自身が「say」というキーの 値を持っているか調べます。しかし、見つからないので、次にその__proto__ …つまり objB を検 索します。また見つからないので、objB の __proto__ = objA を検索し、say を見つけます。
  • objC.name 属性の参照 objC.name アッタ! 無い (objB) objC.__proto__.name (objA)(objB) objC.__proto__.__proto__.say 使われ ない 同様に、objC の name を参照した時、JavaScript は objC 自身が name を持っているか確認し ます。しかし、見つからないので、次にその__proto__ …つまり objB を検索し、name を見つ けました。必要な名前が見つかった時点で、検索は終了します。
  • このように、自分に無い機能や属性を、 __proto__ に入っている別のオブジェクトから 連鎖的に検索して探す仕組みが… obj.__proto__.__proto__.property
  • obj.__proto__.__proto__.property プロトタイプチェイン
  • では、同じ特性を持った オブジェクトを量産するには?
  • 同じプロト タイプを所有 すればOK var objA = { name : 'Yome', say : function () { alert('I Love ' + this.name); } }; var objB = { name : 'Nikole' }; objB.__proto__ = objA; var objC = { name : 'Gyu-Ri' }; objC.__proto__ = objA; objB.say(); // I Love Nikole objC.say(); // I Love Gyu-Ri
  • クラスベースと 較べてみる。
  • ‣ クラスベース ‣ プロトタイプベース class A class B class C obj A obj B obj C obj A of B obj B of C obj C of C obj E obj F extends extends (delegete) new new (delegete) オブジェクト だけの世界 クラスから オブジェクト を作成 obj D (delegete) new (delegete) (delegete) クラスベースではクラスという型を拡張し、そこからインスタンスを作成するイメージです。 一方プロトタイプベースでは、必要な特性を持った他のオブジェクトを利用するイメージです。
  • プロトタイプチェインは、 以上です。
  • あれ?
  • prototype 忘れてない?
  • いえいえ、以上っすぅ。 だって prototype 無くても プロトタイプチェインできたし (・ ・)
  • 実は、prototype 属性は、 プロトタイプチェインではなく、 オブジェクトの生成で使われるもの。 ネット上の情報で、ここを一緒に解説している例を見掛けますが、 それが「ちょっと分かり難いなー」と個人的には思っています。 プロトタイプチェインはあくまでも__proto__の連鎖的検索であって、 prototype は検索対象では無いからです。 分けて 考えると イイらしい
  • では…
  • オブジェクトの生成
  • とはいえ…
  • 実は __proto__は標準仕様ではありません。 つまり、__proto__ が利用できない環境もあり、だから通常、このようなことはしません。 ここまでは、プロトタイプチェインの概念を分かり易くするために __proto__ を使って 説明していましたが、そういうことなので、実際のプログラミングではこんなことしな いでくださいねー。 (・ ・)/ では、逆に、良く見かけるのは… obj.__proto__ = otherObj あまり見たコトない…
  • これは new 演算子とコンストラクタ関数によるオブジェクトの生成です。 JavaScript におけるオブジェクトの生成で、よく見られるコードです。 var o = new Person('Nicole') このあたりの記法が、クラスベースの言語を学んだ方を惑わしますね♪ こっちのが、 よく見る感じ!
  • この手法による オブジェクト生成について 見てみます。
  • 準備 利用 準備して、利用しているのは、間違いありません。 よく見るコードの例 var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole');
  • 利用 クラス 定義? よく見るコードの意味 var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole'); クラスは存在しないので、もちろん「クラス定義」ではありません。
  • prototype の拡張 コンスト ラクタ関数 の定義 よく見るコードの意味 利用 とこんな感じで、ちゃんと分けて理解しておくのが良いと思います。 var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole');
  • コンストラクタ関数とは?
  • オブジェクトの初期化に 使われる関数。 new 演算子と組み合わせて関数をコールすることで、関数はコンストラクタ関数 として実行され、オブジェクトの生成に利用されます。
  • new 演算子は?
  • オブジェクトの 生成・継承・初期化を行う 指示をしてます。
  • このようなコードを実行した時、 実際にはどんな意味合いになるか? var o = new Person('Nicole') こんなふうに new した時は…
  • だいたいこんな意味 var newObj = {}; newObj.__proto__ = Person.prototype; Person.apply(newObj, arguments); return newObj; ※分かり易さの為に詳細を割愛しています。 擬似 コード
  • だいたいこんな意味 var newObj = {}; newObj.__proto__ = Person.prototype; Person.apply(newObj, arguments); return newObj; ①新しい Object が 生成される new する度に、新しい個別のオブジェクトが生成されます。
  • だいたいこんな意味 var newObj = {}; newObj.__proto__ = Person.prototype; Person.apply(newObj, arguments); return newObj; ②プロトタイプのオブ ジェクトの参照を代入 コンストラクタ関数の Person.prototype を、新しいオブジェクトの __proto__ に 参照を代入し、プロトタイプチェインの仕組みで、その特性を継承します。
  • だいたいこんな意味 var newObj = {}; newObj.__proto__ = Person.prototype; Person.apply(newObj, arguments); return newObj; ③ newObj を this に、コンストラクタ 関数を実行 これによって、コンストラクタ関数の中で、生成される新しいオブジェクトを this と して参照し、その属性をセットしたりできるようになります。
  • だいたいこんな意味 var newObj = {}; newObj.__proto__ = Person.prototype; Person.apply(newObj, arguments); return newObj; ④完成した オブジェクトを返す Person.prototype の特性を継承し、コンストラクタ関数によって初期化済みの、 新しいオブジェクトが返され、プログラムで利用できるようになります。
  • ポイント はココ newObj.__proto__ = Person.prototype; Person.prototype オブジェクトの機能が、 newObj で利用できるようになる! (プロトタイプチェインの仕組み)
  • 整理すると… ‣ __proto__ ● プロトタイプチェインで 検索されるプロトタイプ オブジェクトが入ってる ‣ prototype ● プロトタイプオブジェク トの控え室 ● new とコンストラクタ 関数でオブジェクトを生 成する時に、__proto__ に代入される プロトタイプチェインで 使われるもの オブジェクト生成で 使われるもの
  • コードで見てみる。 余計分かり難くなったらごめんなさいですが、 __proto__ と prototype の動きをなるべく丁寧に書いてみました。 ゆっくりじっくり読んでみてください。
  • (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); // true p.say(); // I Love Nicole p.name = 'Gyu-Ri'; p.say(); // I Love Gyu-Ri Person.prototype.name = 'Ha-Ra'; p.say(); // I Love Gyu-Ri delete p.name; p.say(); // I Love Ha-Ra })(); SAMPLE CODE
  • (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } この準備で、右側のようなオブジェ クトが出来ます(コンストラクタ 関数の実行部は省略)。 ここで、Person.prototype に 入っているオブジェクトは、name と say の2つの属性を持ちます。
  • (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } new した時の擬似コード var newObj = {}; newObj.__proto__ = Person.apply(newObj, ['Nicole']) return newObj; Person を newすると、擬似コードにあるような初期化が行われ、新しい オブジェクトの __proto__ に Person.prototype のオブジェクトが代入されます。 代入
  • p: { name:'Nicole' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } 参照 結果、新しいオブジェクト p の __proto__ は、 Person.prototype に入っているオブジェクトへの参照を持ちます。
  • p: { name:'Nicole' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } True 同じ Person.prototype が p.__proto__ に代入された直後なので、 当然ですが、同一性比較は true になります。
  • p: { name:'Nicole' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } I Love Nicole 参照 p.say() がコールされると、p 上に say を探すが見つかりません。 __proto__を って、say を見つけます。name は p 上で見つかります。
  • p: { name:'Gyu-Ri' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'nanashi' say: functon () {…} } } I Love Gyu-Ri 参照 先ほどと同じですが、p 自身が持つ name が変更されたため、 当然、表示される内容は変化します。
  • p: { name:'Gyu-Ri' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'Ha-Ra' say: functon () {…} } } I Love Gyu-Ri 参照 Person.prototype.name を変更しましたが、 name は p 自身で見つかるので、結果に変化はありません。
  • p: { name:'Gyu-Ri' __proto__: } (function() { var Person = function (name){ this.name = name; }; Person.prototype.name = 'nanashi'; Person.prototype.say = function () { alert('I Love ' + this.name); }; var p = new Person('Nicole'); alert( p.__proto__ === Person.prototype ); p.say(); p.name = 'Gyu-Ri'; p.say(); Person.prototype.name = 'Ha-Ra'; p.say(); delete p.name; p.say(); })(); Person: { prototype: { name:'Ha-Ra' say: functon () {…} } } I Love Ha-Ra 参照 p 自身の name を削除すると、p 自身は name を持たなくなるので、 say も name も __proto__ から検索されて、結果、表示内容が変わります。 削除されて無くなった
  • こんな感じです。 この解説で prototype 属性と __proto__ 属性それぞれの 役割や位置づけ、またその原理などがしっくりと理解でき たら嬉しいです。
  • それでは、次。
  • JavaScriptの もう一つの鎖などについて。 (チェイン)
  • スコープチェイン
  • スコープとは?
  • 任意の変数に アクセスできる範囲。 …で、いいか。 (;゚ ゚)
  • JavaScriptの2つのスコープ ‣ グローバルスコープ ● 文字通り「グローバル」 ● プログラム全体からアク セスできる ‣ ローカルスコープ ● ある特定の範囲でだけで アクセスできる ● JSでは基本的に、個々 の関数の中がローカルス コープになる ● ある関数の中(=ローカ ルスコープ)で作成され た変数は、その関数の 内側でのみ参照できる ● 関数の引数もその関数の 内側でのみで参照できる
  • 例を見てみます。
  • 外からは 見えないか らエラー ローカル 変数 function myfunc() { var name = 'Gyu-Ri'; } myfunc(); alert(name); //ReferenceError
  • でも逆に…
  • 期待通り 計算される var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); //738 } func2(); } func1(); 外側の変数 は見える
  • JavaScriptのスコープは、 外から内は見えなくて、 内から外は見える。 ポイント 「自分より外側は見える」ぐらいの、とてもシンプルな概念です。
  • もう少し見てみます。
  • 名前が同じでも スコープが違えば 別の変数 var name = 'Nicole'; function myfunc() { var name = 'Gyu-Ri'; } myfunc(); alert(name); //Nicole 別の変数なので、関数内 での代入は外側の name に影響しない。
  • じゃ、これは? var name = 'Nicole'; function myfunc() { name = 'Gyu-Ri'; } myfunc(); alert(name); //???
  • var name = 'Nicole'; function myfunc() { name = 'Gyu-Ri'; } myfunc(); alert(name); //Gyu-Ri 同じ変数!! 同じ変数なので、関数内 で 代 入 し た ら 外 側 の name も変わる。
  • なにが違った?
  • var name = 'Nicole'; function myfunc() { var name = 'Gyu-Ri'; } Before
  • var name = 'Nicole'; function myfunc() { name = 'Gyu-Ri'; } After
  • var name = 'Nicole'; function myfunc() { var name = 'Gyu-Ri'; } var が無い!!
  • var は、変数宣言。 「新しく作るよ」宣言。
  • var a = 123 新しく変数を作る時は必ず必要です。
  • 言い換えると、宣言してない時は、 「既に有るよ」的な意味になる。
  • 外側の変数 は見える var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 さっきの例の 変数 a の場合
  • このローカルスコープで、宣言さ れているのは c だけなので、 a と b は 既にあると解釈される …でも、func2 のローカルスコー プには、a と b は見つからない。 var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示
  • var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 そこで JavaScript は、一つ外側 の func1 のスコープで a と b を 検索する。 そして、b を見つけるが、 まだ a は見つからない。
  • var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 そこで JavaScript は、さらに 外側のスコープで a を検索し、 そして a を発見する。
  • こんな感じで、順繰りに 内側から外側へと探して行く。 なんとなく…
  • プロトタイプチェインに 似てなくない?
  • (func2) 変数 a への参照をちょっと オブジェクトっぽく書いてみた scope.a 無い 無い (func2) (func1) scope.__scope__.a アッタ! (func2) (global)(func1) scope.__scope__.__scope__.a
  • やっぱり、 プロトタイプチェインに そっくり。
  • このように、今のスコープに無い識別子を、 より外側のスコープから連鎖的に 検索して探す仕組みが… scope.__scope__.__scope__.identifier
  • スコープチェイン scope.__scope__.__scope__.identifier この事から、「グローバルスコープ」も、何も特別なものではなくて、単に「最も外側にあるローカルス コープ」という解釈もできます。実際のところ、グローバルスコープには幾つかの特別な役割も課せられてい ますが、感覚的なその理解は概ね正しいです。
  • 難しく考えないことがポイント。 原理的には内から外への連鎖的なスコープの検索ですが、利用上は単に、「関数が 定義された環境(ソースコード上の見かけ)において、その外側は見える」とい うことに過ぎません。プログラマにとっては、とても直感的な仕様です。
  • 余談ですが、 実はJavaScriptでは、変数のスコープでさえも、内部的にはオブジェクトで構成されています(=変数オブ ジェクト)。つまり、つまるところ、「スコープチェイン」も「プロトタイプチェイン」も、オブジェクトの特 殊なプロパティによる連結リストによるデータ構造と、それを終端に向かって連鎖的に検索するという、よく 似たシンプルな仕組みに過ぎません。またこれは、JavaScriptにおけるスコープの実体が、グローバルスコー プ(グローバル変数オブジェクト)をrootとする、変数オブジェクトが連なった巨大なオブジェクトのツ リー構造だということでもあります。興味深いですね。 残念ながら、それらのスコープを構成するオブジェクトのツリーを、プログラマが直接参照することは出来ま せん。しかし、プロトタイプチェインを構成する__proto__属性については、ECMA Script標準では無いも のの、幾つかのブラウザ実装では実際に目にしてその仕組みを確認することが出来ます。これを良く観察す ることで、「スコープチェイン」と「プロトタイプチェイン」両方の理解を深めることが出来るはずです。
  • さて、JavaScript のスコープには もうひとつ大事な概念があります。
  • クロージャ
  • 早速のクロージャの サンプルコード。
  • var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 (・∀・)? サッキト オナジジャネ?
  • そうです。 さっきのもクロージャ。 クロージャという言葉には広義なものと狭義なものとあります。 そして「広義」にはこれもクロージャです。
  • 引数以外の変数を実行時の環境ではなく、 自身が定義された環境(静的スコープ)において 解決することを特徴とする。 by Wikipedia(いつもありがとう) クロージャ の意味
  • ようするに、
  • var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 この関数が 定義された 環境とは…
  • var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示 この関数を含 む全てのスコー プのこと スコープチェインによって、 関数定義の外側の変数まで 参照できましたよね!
  • スコープチェインによって、 関数定義の外側の変数まで 参照できましたよね! さっきの 内側から外側は 見えるって コト! var a = 123; function func1() { var b = 3; function func2() { var c = 2; alert(a * b * c); } func2(); } func1(); //738と表示
  • スコープチェイン => クロージャ つまり JavaScript では、スコープチェインの仕組みが、 そのままクロージャとして機能することになります。 JavaScript を使っていると、ごくごく当たり前かも知れませんが…、 これが出来る汎用プログラミング言語は少し前までそう多くはありませんでした。 PHPでは 5.3 から、Java でも Java 8 から利用できるようになります。
  • では、どんなふうに使えるか?
  • 変数の隠 、別名の利用。 みんなもう普通に使ってるよ。 (*'-'*)
  • 即時関数で ラップしてローカル スコープを作成 スコープの外から は見えない。 =隠 されてる 引数を利用して オブジェクトの 別名を利用 ここで、変数 privateValue や $ は、即時関数の外からは見 えない変数になりますが、こ のスコープの内側で定義され る関数からは参照することが できます。 即時関数は文字通り即時実行 されてその処理を抜けます が、privateValue や $ と いった変数が、引き続きその 中で定義された関数から参照 できることがポイントです。 var Person = (function($) { var privateValue = 'Hogeta'; function Person(name) { ... } Person.prototype.xxxx = ... return Person; })(jQuery);
  • 変数の永続化。 とても重要でパワフルな概念。
  • 返された クロージャは i をずっと 参照 ※コードはWikipediaのクロージャのページからの引用です。 呼び出す毎 に i が1ずつ 増える newCounter は、新しい関数 オブジェクトを生成して返却 します。newCounter が実行 されるたび、変数 i が初期化 され、その i を参照するク ロージャが返却されます。 newCounter が返却するク ロージャを変数 c1 に受け 取って実行すると、実行毎に 1ずつ増えた値が得られます。 これは、newCounter が返し た関数オブジェクトが、変数 i を参照し続けていることで 実現されています。 このようにクロージャは、定 義された時点の環境を束縛し ています。 function newCounter() { var i = 0; return function() { i = i + 1; return i; } } var c1 = newCounter(); alert(c1()); // 1 alert(c1()); // 2 alert(c1()); // 3
  • クロージャを使いこなすには?
  • とりあえず、書く。 たくさんコードを書いて慣れるのが一番です。 (*'-'*) とてもシンプルで直感的な仕様なのですが、上手く利用するには訓練が必要です。 クロージャの利用シーンはとても広範囲なので、たくさん書いて少しずつ用法を 身につけていくと良いと思います。
  • では、最後に、
  • 例のややこしいやつですよ。
  • this
  • this も、ワリと簡単。 これこそ、クラスベース言語によくある考え方で理解しようとすると、 落とし穴にはまります。一旦、頭を真っ白してください。
  • this を使った例。
  • これは OK var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole'); var btn = $('button#say-hello'); btn.on('click', function() { person.sayHello(); }); よくあるパターンです。イベントにコールバック関数をバインドしています。 ここで、btn.on に渡している関数はクロージャで、クロージャ変数として person を参照しています。
  • これは NG var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole'); var btn = $('button'); btn.on('click', person.sayHello); 前の例では、呼び出したい関数をコールバックとして登録しているだけだったので、 sayHello 関数を直接バインドしたらどうだろう?とやってみると NG です…。 結果として、(多くの場合)「Hello undefined」と表示されます。
  • なぜか?
  • 関数はオブジェクトに 束縛されない ポイント常識!
  • 繰り返しになりますが、オブジェクトのメソッドはそれを持つオブジェクトに束縛され ているのではなく、その時そのオブジェクトによってただ参照されているだけです。 この意味に注意してください。 var obj = { key: function() {...} }
  • オブジェクトのメソッドは、 「オブジェクトによってただ参照されているだけ」 つまり…
  • btn.on('click', person.sayHello); person の sayHello メソッドを 渡してる 「person の sayHello」を渡しているのではない。
  • btn.on('click', person.sayHello); 「sayHello」に入っている、関数オブジェクトを渡している、が正解。 関数オブジェクトだけが渡されている、という理解が大切です。 sayHelloの中の関数オ ブジェクトを渡してる
  • では、this ってなに?
  • ポイント 関数コール時にその関数が 所属していたオブジェクト。 = this
  • this 呼び出し時に、所属していたオブジェクトが、 関数内での this になる person.sayHello(); 所属
  • では、 ただの関数呼び出しの時は?
  • 所属 sayHello();...nothing...??? ただの関数呼び出しで、関数がオブジェクトに所属していない時、 関数内の this は何になるのか?
  • 関数のコール時にレシーバーが無い時、this はグローバルオブジェクトになります。 ブラウザ実装の場合、これは通常 window オブジェクトです。 global object = window function sayHello() {     alert('Hello ' + this.name); } sayHello();
  • つまり
  • ポイント this は、 呼び出し時に決定される
  • ところで、
  • this が呼び出し元で 決定されるなら、
  • 実は、呼び出し元で、 thisを操ることもできます。
  • this を操る
  • call apply bind thisを操る メソッド 関数オブジェクトには、this をコントロールできる3つのメソッドがあります。 (関数オブジェクトだけに存在する)
  • 関数に渡す引数 call(object, arg1, arg2, ...) apply(object, Array) bind(object, arg1, arg2, ...) これらのメソッドの第1引数に与えた object が、関数内での this になります。 また、その後の引数は、それぞれ関数呼び出し時に関数に与える引数になります。 this this this
  • 3つのメソッドの利用法。
  • call と apply は 関数を実行する
  • person を this として実行 call の第1引数が this に、 第2引数以降の引数が、 say関数の引数に。 function say(arg1, arg2) {     alert(arg1 + this.name + arg2); } var person = new Person('Nicole'); say.call(person, 'Hello ', ' chan'); call の例
  • person を this として実行 apply の例 call の第1引数が this に、第2引数の 配列の内容が、say関数の引数になる。 引数の渡し方以外は call と同じ function say(arg1, arg2) {     alert(arg1 + this.name + arg2); } var person = new Person('Nicole'); say.apply(person, ['Hello ', ' chan']);
  • 他のオブジェクト のメンバだったら? var p1 = { name: 'Gyu-Ri', say: function (arg1, arg2) {      alert(arg1 + this.name + arg2); } }; var person = new Person('Nicole'); p1.say.call(person, 'Hello ', ' chan');
  • 他のオブジェクト のメンバでも関係ない 関数はオブジェクトに「束縛されていない」 ことを思い出してください。 ここで、関数 say が p1 に属していても、 指定した person が this になります。 var p1 = { name: 'Gyu-Ri', say: function (arg1, arg2) {      alert(arg1 + this.name + arg2); } }; var person = new Person('Nicole'); p1.say.call(person, 'Hello ', ' chan');
  • bind は 関数に値を束縛する
  • personをthisに束縛した 新しい関数オブジェクトを返す function say(arg1, arg2) {     alert(arg1 + this.name + arg2); } var person = new Person('Nicole'); var say2 = say.bind(person); say2('Hello ', ' chan'); 結果:Hello Nicole chan と表示される say2 関数の呼び出しでは、 常に person が this となる
  • bind の利用例 JavaScript OOP では非常に便利。
  • これは NGでした さきほど NG だった例ですが... var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole'); var btn = $('button'); btn.on('click', person.sayHello);
  • こうすれば OK bind で関数に this を束縛できるので、そのまま渡せるようになる。 person を2回使ってるように見えるけど、そういう意味でないことに注意。 var Person = function (name) {     this.name = name; } Person.prototype.sayHello = function() {     alert('Hello ' + this.name); } var person = new Person('Nicole'); var btn = $('button'); btn.on('click', person.sayHello.bind(person));
  • apply とクロージャの利用例 すこし長いし、元ネタは…だけど。
  • 元のオブジェクト(self)と関 数(func)をクロージャで保持 し、必ず self に対して関数が 適用されるようにしている。 引 数 の 数 は ま ち ま ち な の で、apply を使って実行する コンストラクタ関数の中で、 __bind を実行し、返された 新しい関数をオブジェクト のメンバーとして保持する (関数の差し替え) say には prefix も渡したい ので、bind を使っているが、 第1引数に person でなく null を渡しても、this は変 更されない // 絶対 this を離さないユーティリティメソッド function __bind (self, func) { return function () { return func.apply(self, arguments); }; } // コンストラクタ関数とプロトタイプを用意 function Person (name) { this.name = name; this.say = __bind(this, this.say); } Person.prototype.say = function (prefix) { alert(prefix + this.name); } // オブジェクト生成 var person = new Person('Nicole'); // 普通に利用 nicole.say('I Love '); // I Love Nicole // 他の値をバインドしてもOK setTimeout(person.say.bind(null, 'I Just Realy Love to '), 2000);
  • ということで…
  • JavaScript でオブジェクト指向プログラミ ングを行う際に備えておくことが望ましい、 基礎知識や概念について解説してきました。
  • ちょっと欲張り過ぎましたが、 どれも一度理解したらとても簡単な概念です。
  • JavaScript にはクラスが無いこと、
  • オブジェクトが 単純なハッシュテーブルであること。
  • この2つの理解を深めると、 たとえば this の挙動も納得できます。
  • JavaScript では、このように単純なデータ構 造を幾つかのルールに沿って組み合わせるこ とで、とても柔軟で強力なオブジェクト指向 プログラミングを実現しています。
  • JavaScript が強力なのは、 このシンプルさの所以です。
  • しかしある場面では、 そのシンプルさが冗長なソースコードの記述 を要求するかも知れません。
  • それらの問題は、例えば CoffeeScript や TypeScript といった、JavaScript を基本と する別の技術で解決できるかも知れません。
  • ただ、そういった新しい技術を利用するにあ たっても、そのベースとなる JavaScript そ のものの基礎理解は、あなたの開発を大いに 助けてくれるはずだと、僕は信じています。
  • var obj = {} JavaScript はオブジェクトが全てです。
  • ご清聴、ありがとうございました!
  • @yuka2py 書籍を執筆しました。 JavaScript についても基礎から 応用・発展まで詳しく書かれています。 良かったら書店で手に取ってください。 http://www.amazon.co.jp/dp/4798129682
  • この文書は クリエイティブ・コモンズ 表示 - 継承 2.1 日本 ライセンスの下に提供されています。