同じプロト
タイプを所有
すれば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)
クラスベースではクラスという型を拡張し、そこからインスタンスを作成するイメージです。
一方プロトタイプベースでは、必要な特性を持った他のオブジェクトを利用するイメージです。
(function() {
varPerson = 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() {
varPerson = 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() {
varPerson = 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__ から検索されて、結果、表示内容が変わります。
削除されて無くなった
外側の変数
は見える
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 を発見する。
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と表示
この関数を含
む全てのスコー
プのこと
スコープチェインによって、
関数定義の外側の変数まで
参照できましたよね!
これは
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」と表示されます。
関数に渡す引数
call(object, arg1, arg2,...)
apply(object, Array)
bind(object, arg1, arg2, ...)
これらのメソッドの第1引数に与えた object が、関数内での this になります。
また、その後の引数は、それぞれ関数呼び出し時に関数に与える引数になります。
this
this
this
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');
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 となる
これは
NGでした
さきほど NG だった例ですが...
varPerson = 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));