去年のボク
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日
株式会社フォーエンキー調べ
愛
恐
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();
同じプロト
タイプを所有
すれば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)
クラスベースではクラスという型を拡張し、そこからインスタンスを作成するイメージです。
一方プロトタイプベースでは、必要な特性を持った他のオブジェクトを利用するイメージです。
これは 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 した時は…
(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__ から検索されて、結果、表示内容が変わります。
削除されて無くなった
名前が同じでも
スコープが違えば
別の変数
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 を発見する。
引数以外の変数を実行時の環境ではなく、
自身が定義された環境(静的スコープ)において
解決することを特徴とする。
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と表示
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」と表示されます。
関数に渡す引数
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);
1–5 of 5 Post a comment
http://foreignkey.jp/wp-content/uploads/2013/07/js-javascript-oop-reintroduction.pdf