40. 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();
41. ここでポイントになるのは、
__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();
48. 同じプロト
タイプを所有
すれば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
50. ‣ クラスベース ‣ プロトタイプベース
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)
クラスベースではクラスという型を拡張し、そこからインスタンスを作成するイメージです。
一方プロトタイプベースでは、必要な特性を持った他のオブジェクトを利用するイメージです。
63. 利用
クラス
定義?
よく見るコードの意味
var Person = function (name) {
this.name = name;
}
Person.prototype.sayHello = function() {
alert('Hello ' + this.name);
}
var person = new Person('Nicole');
クラスは存在しないので、もちろん「クラス定義」ではありません。
78. (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
79. (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つの属性を持ちます。
80. (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 のオブジェクトが代入されます。
代入
81. 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 に入っているオブジェクトへの参照を持ちます。
82. 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 になります。
83. 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 上で見つかります。
84. 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 が変更されたため、
当然、表示される内容は変化します。
85. 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 自身で見つかるので、結果に変化はありません。
86. 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__ から検索されて、結果、表示内容が変わります。
削除されて無くなった
100. 名前が同じでも
スコープが違えば
別の変数
var name = 'Nicole';
function myfunc() {
var name = 'Gyu-Ri';
}
myfunc();
alert(name); //Nicole
別の変数なので、関数内
での代入は外側の name
に影響しない。
101. じゃ、これは?
var name = 'Nicole';
function myfunc() {
name = 'Gyu-Ri';
}
myfunc();
alert(name); //???
102. var name = 'Nicole';
function myfunc() {
name = 'Gyu-Ri';
}
myfunc();
alert(name); //Gyu-Ri
同じ変数!!
同じ変数なので、関数内
で 代 入 し た ら 外 側 の
name も変わる。
110. 外側の変数
は見える
var a = 123;
function func1() {
var b = 3;
function func2() {
var c = 2;
alert(a * b * c);
}
func2();
}
func1(); //738と表示
さっきの例の
変数 a の場合
111. このローカルスコープで、宣言さ
れているのは 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と表示
112. 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 は見つからない。
113. var a = 123;
function func1() {
var b = 3;
function func2() {
var c = 2;
alert(a * b * c);
}
func2();
}
func1(); //738と表示
そこで JavaScript は、さらに
外側のスコープで a を検索し、
そして a を発見する。
129. var a = 123;
function func1() {
var b = 3;
function func2() {
var c = 2;
alert(a * b * c);
}
func2();
}
func1(); //738と表示
この関数が
定義された
環境とは…
130. var a = 123;
function func1() {
var b = 3;
function func2() {
var c = 2;
alert(a * b * c);
}
func2();
}
func1(); //738と表示
この関数を含
む全てのスコー
プのこと
スコープチェインによって、
関数定義の外側の変数まで
参照できましたよね!
145. これは
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 を参照しています。
146. これは
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」と表示されます。
169. 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 の例
170. 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']);
171. 他のオブジェクト
のメンバだったら?
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');
172. 他のオブジェクト
のメンバでも関係ない
関数はオブジェクトに「束縛されていない」
ことを思い出してください。
ここで、関数 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');
176. これは
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);
177. こうすれば
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));