まえがき
インスタンスオブジェクトと newコマンド
オブジェクト
オブジェクトとは、個々の物理的オブジェクトを抽象化したものです。
オブジェクトは、プロパティとメソッドをカプセル化したコンテナです。
コンストラクタ
オブジェクト指向プログラミングの最初のステップは、オブジェクトを生成することです。物理オブジェクトの特定のクラスに共通する特性を表すテンプレートが必要になることが多く、このテンプレートに従ってオブジェクトが生成されます。
クラス」はオブジェクトのテンプレートであり、オブジェクトは「クラス」のインスタンスです。しかし、JavaScriptのオブジェクトシステムは「クラス」ではなく、コンストラクタとプロトタイプチェーンに基づいています。
JavaScript言語では、オブジェクトのテンプレートとしてコンストラクタを使用します。いわゆる「コンストラクタ」は、オブジェクトのインスタンスを生成するために特別に設計された関数です。これはオブジェクトのテンプレートであり、インスタンスオブジェクトの基本構造を記述します。コンストラクタは複数のインスタンスオブジェクトを生成することができます。
var obj = function () {
    this.a = 0x3e8;
};通常の関数と区別するため、コンストラクタ名の最初の文字は通常大文字にします。
コンストラクタの特徴は2つあります。
- 関数本体では、thisキーワードを使って生成するオブジェクトのインスタンスを表します。
- オブジェクトを生成するには、new コマンドを使用する必要があります。
new
newコマンドが行うのは、コンストラクタを実行してインスタンスオブジェクトを返すことです。
var foo = function () {
    this.a = 0x3e8;
};
var c = new foo();
c.a;newコマンドを忘れてコンストラクタを直接呼び出したらどうなりますか?
var Vehicle = function (){
 this.price = 1000;
};
var v = Vehicle();
v // undefined
price // 1000
コンストラクタは普通の関数となり、インスタンス・オブジェクトを生成しない。
コンストラクタをnewコマンドと一緒に使わなければならないようにするには、コンストラクタの内部でstrictモードを使うこと、つまり、最初の行にuse strictを追加することで、newコマンドを使い忘れてコンストラクタを直接呼び出すとエラーになるようにするのがひとつの解決策です。
別の解決策としては、コンストラクタが内部的に新しいコマンドが使用されているかどうかを判断し、使用されていないと判断した場合は単純にインスタンスオブジェクトを返します。
function obj(a, b) {
    if (!(this instanceof obj)) {
        return new obj(a, b);
    }
    this.foo = a;
    this.bar = b;
}
obj(0x1, 0x2).foo(new obj(0x1, 0x2)).foo;new コマンドの原理
新しいコマンドを使用すると、それに続く関数が以下のステップを順番に実行します。
- 空のオブジェクトを、返されるオブジェクトのインスタンスとして作成します。
- この空のオブジェクトのプロトタイプを取得し、コンストラクタのprototypeプロパティに指定します。
- 関数内の thisキーワードに空のオブジェクトを代入します。
- コンストラクタ内のコードの実行を開始します。
コンストラクタの内部では、thisは新しく生成されたヌル・オブジェクトを指し、thisに対する操作はすべてこのヌル・オブジェクトに対して行われます。
コンストラクタが「コンストラクタ」と呼ばれる理由は、この関数の目的が、空のオブジェクトを操作して必要なものに「構築」することだからです。
- コンストラクタの中にreturn文があり、そのreturn文の後にオブジェクトが続く場合、newコマンドはreturn文で指定されたオブジェクトを返します。
- 通常の関数に new コマンドを使用すると、空のオブジェクトが返されます。
新しいコマンドの簡略化された内部フローは、以下のコードで表すことができます。
function _new(/* コンストラクタ*/ constructor, /* コンストラクタのパラメータ*/ params) {
 // 引数オブジェクトを配列に変換する
 var args = [].slice.call(arguments);
 // コンストラクタを取り除く
 var constructor = args.shift();
 // コンストラクタのprototype属性を継承した空のオブジェクトを作成する。
 var context = Object.create(constructor.prototype);
 // コンストラクタを実行する
 var result = constructor.apply(context, args);
 // 結果がオブジェクトの場合はそれを直接返し、そうでない場合はコンテキスト・オブジェクトを返す。
 return (typeof result === 'object' && result != null) ? result : context;
}
//  
var actor = _new(Person, ' ', 28);
new.target
new.targetプロパティは関数の内部で使用することができます。現在の関数が新しいコマンド呼び出しである場合、new.targetは現在の関数を指し、そうでない場合は未定義です。
function c() {
    console.log(new.target === c);
}
c();
new c();Object.create() インスタンスオブジェクトの作成
コンストラクタはインスタンス・オブジェクトを生成するためのテンプレートとして機能します。しかし、コンストラクタを取得できず、既存のオブジェクトしか取得できないこともあります。
この既存のオブジェクトをテンプレートとして使用し、新しいインスタンス・オブジェクトを生成したい場合は、Object.create() メソッドを使用します。
var b = {
    'name': ' ',
    'c': 0x26,
    'obj': function () {
        console.log('Hi!\x20I\x27m\x20' + this.name + '.');
    }
};
var foo = Object.create(b);
foo.name;
foo.obj();this
thisはインスタンス・オブジェクトを表すためにコンストラクタで使用することができます。さらに、thisは他の場面でも使うことができます。しかし、どのような場合でも、thisに共通するのは、常にオブジェクトを返すということです。
thisは、プロパティやメソッドが "現在 "置かれているオブジェクトです。
一例です:
var bar = {
    'name': ' ',
    'c': function () {
        return ' ' + this.name;
    }
};
bar.c();オブジェクトのプロパティは別のオブジェクトに割り当てることができるので、プロパティがある現在のオブジェクトは可変です。
var obj = {
    'name': ' ',
    'b': function () {
        return ' ' + this.name;
    }
};
var bar = ' ';
var c = obj.b;
c();実体
JavaScript言語の this設計の理由は、メモリ上のデータの構造に関係しています。
var obj = { foo: function () {} };
この時点でエンジンは、関数のアドレスをfooプロパティのvalue属性に代入する前に、関数を別途メモリに格納する。
{
 foo: {
 [[value]]: 関数のアドレスは
 ...
 }
}
関数は1つの値なので、さまざまな環境で実行することができる。
var f = function () {};
var obj = { f: f };
// 個別実行
f()
// obj 環境は
obj.f()
関数は異なる環境で実行される可能性があるため、関数本体の内部で現在の環境を取得する仕組みが必要です。そのため、thisは関数本体の内部に現れるように設計されており、関数の現在の動作環境を参照します。
var bar = function () {
    console.log(this.x);
};
var b = 0x1;
var foo = {
    'f': bar,
    'x': 0x2
};
bar();
foo.f();使用場面
グローバル環境
グローバル環境では thisを使用し、これはトップレベルのオブジェクトウィンドウを指します。
this === window;
function bar() {
    console.log(this === window);
}
bar();コンストラクタ
コンストラクタの thisはインスタンス・オブジェクトを指します。
var Obj = function (p) {
 this.p = p;
};
オブジェクトのメソッド
オブジェクトのメソッドに thisが含まれている場合、thisはそのメソッドが実行されたオブジェクトを指します。このメソッドを別のオブジェクトに代入すると、thisのポインティングが変更されます。
var obj ={
 foo: function () {
 console.log(this);
 }
};
obj.foo() // obj
//  
(obj.foo = obj.foo)() // window
//  
(false 
 obj.foo)() // window
//  
(1, obj.foo)() // window
this "が配置されているメソッドがオブジェクトの最初のレベルにない場合、thisはオブジェクトの現在のレベルを指すだけで、上のレベルを継承しません。
var a = {
 p: 'Hello',
 b: {
 m: function() {
 console.log(this.p);
 }
 }
};
a.b.m() // undefined
望みの効果を得るには、次のように書くしかない。
var a = {
 b: {
 m: function() {
 console.log(this.p);
 },
 p: 'Hello'
 }
};
ネストしたオブジェクト内のメソッドを変数に代入しても、thisはグローバルオブジェクトを指します。
var a = a.b.obj;
a();
var a = a.b;
a.obj();注意点
this "の多層化は避けましょう。
この thisの指し示し方が不確かなので、関数内に thisを何重にも含めないでください。
配列処理メソッドにおける thisの回避
配列のmapメソッドとforeachメソッドでは、引数として関数を指定することができます。この関数は、内部で this を使用してはいけません。
var obj = {
    'a': 'hello',
    'foo': [
        'a1',
        'a2'
    ],
    'f': function b() {
        this.foo.forEach(function (bar) { console.log(this.a + '\x20' + bar);
        });
    }
};
obj.f();foreachメソッドのコールバック関数内の thisは実際にはウィンドウ・オブジェクトを指しているので、o.vの値をフェッチすることはできません。この理由は、前の段落の thisの多層化と同じです。つまり、内側の thisは外側を指しているのではなく、最上位のオブジェクトを指しているのです。
この問題を解決する一つの方法は、中間変数を使ってthisを固定することです。
var b = {
    'bar': 'hello',
    'foo': [
        'a1',
        'a2'
    ],
    'f': function a() {
        var c = this;
        this.foo.forEach(function (obj) { console.log(c.bar + '\x20' + obj);
        });
    }
};
b.f();もうひとつの方法は、foreachメソッドの第2引数に thisを取り、実行環境を固定する方法です。
var a = {
    'bar': 'hello',
    'foo': [
        'a1',
        'a2'
    ],
    'f': function b() {
        this.foo.forEach(function (c) { console.log(this.bar + '\x20' + c);
        }, this);
    }
};
a.f();コールバック関数での thisの回避
コールバック関数の thisはポインタを変えてしまうので避けた方が無難です。
var a = new Object();
a.f = function () {
    console.log(this === a);
};
$('#button').obj('click', a.f);なぜなら、fメソッドはボタンオブジェクトのコンテキストで呼び出されるからです。
thisにバインドするメソッド
JavaScriptにはcall、apply、bindの3つのメソッドがあり、thisを切り替えることができます。
Function.prototype.call()
callメソッドでは、関数内のどこに thisがあるかを指定し、指定したスコープで関数を呼び出します。
var a = {};
var bar = function () {
    return this;
};
bar() === window;
bar.call(a) === a;呼び出しメソッドの引数で、オブジェクトである必要があります。引数が null、null、undefined の場合、デフォルトでグローバル・オブジェクトが渡されます。
var bar = 0x7b;
var b = { 'n': 0x1c8 };
function foo() {
    console.log(this.n);
}
foo.call();
foo.call(null);
foo.call(undefined);
foo.call(window);
foo.call(b);呼び出しメソッドは複数の引数を受け取ることもできます。
callの最初のパラメータは thisが指すオブジェクトで、それに続くパラメータは関数呼び出しに必要なものです。
function a(foo, obj) {
    return foo + obj;
}
a.call(this, 0x1, 0x2);callメソッドの応用例として、オブジェクトのネイティブ・メソッドを呼び出す方法があります。
var foo = {};
foo.hasOwnProperty('toString');
foo.hasOwnProperty = function () {
    return !false;
};
foo.hasOwnProperty('toString');
Object.prototype.hasOwnProperty.call(foo, 'toString');callメソッドはこの問題を解決するために、hasOwnPropertyメソッドの元の定義をobjオブジェクトに配置して実行します。
Function.prototype.apply()
applyメソッドは、関数を指す thisを変更してから関数を呼び出すという点で、callメソッドと似ています。唯一の違いは、関数を実行するために引数の配列を受け取ることです。
func.apply(thisValue, [arg1, arg2, ...])
function f(x, y){
 console.log(x + y);
}
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2
応用1:配列の最大要素を求める
var a = [, 15, 9];
Math.max.apply(null, a) // 15
応用例2:配列の空要素をundefinedにする
Array.apply(null, [
    'a',
    ,
    'b'
]);nullとundefinedの違いは、配列のforEachメソッドはnull要素をスキップしますが、undefinedはスキップしません。
応用3:配列のようなオブジェクトの変換
Array.prototype.slice.apply({
    0x0: 0x1,
    'length': 0x1
});
Array.prototype.slice.apply({ 0x0: 0x1 });
Array.prototype.slice.apply({
    0x0: 0x1,
    'length': 0x2
});
Array.prototype.slice.apply({ 'length': 0x1 });このメソッドは、処理対象のオブジェクトに length 属性とそれに対応する数値キーがある場合にのみ動作します。
アプリケーション 4: コールバック関数へのオブジェクトのバインド
var a = new Object();
a.f = function () {
    console.log(this === a);
};
var b = function () {
    a.f.apply(a);
};
$('#button').foo('click', b);Function.prototype.bind()
bind() メソッドは、関数内の this をオブジェクトにバインドし、新しい関数を返すために使用します。
var a = new Date();
a.getTime();
var bar = a.getTime;
bar();
var bar = a.getTime.bind(a);
bar();bindメソッドの引数は、thisがバインドされるオブジェクトです。
var obj = {
    'count': 0x0,
    'bar': function () {
        this.count++;
    }
};
var c = obj.bar.bind(obj);
c();
obj.count;counter.inc()メソッドは変数funcに代入され、bind()メソッドでinc()内のthisをカウンターにバインドしないとエラーになります。
this "を他のオブジェクトにバインドすることも可能です。
var c = {
    'count': 0x0,
    'foo': function () {
        this.count++;
    }
};
var bar = { 'count': 0x64 };
var a = c.foo.bind(bar);
a();
bar.count;bind()はさらに引数を取ることもでき、それらを元の関数の引数にバインドします。
bind() メソッドの最初の引数が null または undefined の場合、this をグローバル・オブジェクトにバインドしたのと同じことになり、関数は this がトップレベル・オブジェクトを指す状態で実行されます。
bind() メソッドに関する注意事項:
毎回新しい関数を返す
bind() メソッドは、実行するたびに新しい関数を返します。たとえば、イベントをリッスンする際には次のような書き方はできません。
element.addEventListener('click', o.m.bind(o));
クリックイベントバインディング bind() メソッドによって生成される匿名関数。これにより
element.removeEventListener('click', o.b.bind(o));正しい方法:
var bar = o.obj.bind(o);
element.addEventListener('click', bar);
element.removeEventListener('click', bar);コールバック関数との併用
this "を含むメソッドは、そのままコールバック関数として扱われます。解決策は、bind() メソッドを使用して counter.inc() をカウンタにバインドすることです。
var foo = {
    'count': 0x0,
    'b': function () {
        'use strict';
        this.count++;
    }
};
function c(bar) {
    bar();
}
c(foo.b.bind(foo));
foo.count;callIt() メソッドは、コールバック関数を呼び出します。counter.incを直接渡すと、counter.inc()内の thisがグローバルオブジェクトを指してしまいます。bind() メソッドを使用して counter.inc を counter にバインドすると、この問題は発生しません。
関数内部での thisの使用
配列メソッドの中には、関数を引数として受け取るものがあります。これらの関数は、内部で thisを指します。
var c = {
    'name': ' ',
    'a': [
        0x1,
        0x2,
        0x3
    ],
    'print': function () {
        this.a.forEach(function (foo) { console.log(this.name);
        });
    }
};
c.print();obj.print内の this.timesの thisはobjを指しています。しかし、forEach()メソッドのコールバック関数内の this.nameはグローバル・オブジェクトを指しています。
治療
obj.print = function () {
    this.b.forEach(function (c) {
        console.log(this.name);
    }.bind(this));
};
obj.print();call() メソッドと
bind() メソッドを使用すると、JavaScript のネイティブメソッドを書き換えることができます。
[
    0x1,
    0x2,
    0x3
].slice(0x0, 0x1);
Array.prototype.slice.call([
    0x1,
    0x2,
    0x3
], 0x0, 0x1);call()メソッドは基本的にFunction.prototype.call()メソッドを呼び出すので、上の式はbind()メソッドで書き換えることができます。
var b = Function.prototype.call.bind(Array.prototype.slice);
b([
    0x1,
    0x2,
    0x3
], 0x0, 0x1);配列の他のメソッドを書き換える例をもうひとつ:
var b = Function.prototype.call.bind(Array.prototype.push);
var c = Function.prototype.call.bind(Array.prototype.pop);
var foo = [
    0x1,
    0x2,
    0x3
];
b(foo, 0x4);
foo;
c(foo);
foo;




