Javascriptの基本についてのメモ

なんとなく分かっているようで、全然わかってないjavascriptのオブジェクト・クラス作成、prototype、モジュール化・・etcに関して、少しずつ自分用にまとめていく。

オブジェクトの作成

カッコ{}で空っぽのオブジェクトを作成できる。またObjectクラスをnewすることによっても同様

var obj = {};
var obj2 = new Object();

プロパティの定義

クラスでいうところのメンバ変数に相当する。メンバ変数のようにオブジェクト作成時に定義もできるし、C++やJavaにはない代入でも定義できる。
代入で定義するところが、とても違和感を覚えるが柔軟で便利な面もある。下記例だと、空っぽのobjにpropプロパティが生えていくような感じである。

var obj = {};
obj.prop = 4;
 
var obj2 = {
    prop : 4
}
 
var obj3 = new Object();
obj3.prop = 4;
 
// output "4"
console.log( obj.prop );
console.log( obj2.prop );
console.log( obj3.prop );

関数の定義

javascriptでは、関数も同じ扱いで、プロパティに代入・保持することができる。また、メンバ関数になるため、他のメンバ変数にアクセスできる。

var obj = {};
obj.age = 15;
obj.func = function( name ){
    return "my name is " + name + ", I am " + this.age + " years old.";
};
 
var obj2 = {
    age : 15,
    func : function( name ) {
        return "my name is " + name + ", I am " + this.age + " years old.";
    }
};
 
var obj3 = new Object();
obj3.age = 15;
obj3.func = function( name ){
    return "my name is " + name + ", I am " + this.age + " years old.";
};
 
console.log( obj.func( "taro" ) );
console.log( obj2.func( "taro" ) );
console.log( obj3.func( "taro" ) );

javascriptにおけるクラスの定義、インスタンス化

そもそもjavascriptにはクラスは存在しないが、newprototypeを使って、「クラスっぽく」振舞うことはできる。new 演算子はjavascriptにもあるが、C++,Javaと全く動きが異なる。

javascriptのnewは、関数に対して実行すると、その関数はクラスのコンストラクタのように振舞う。
空オブジェクト{}が生成されて、thisから参照できるようになり、
関数が実行されると、最後にこのオブジェクトが戻り値として返るような振る舞いをする。

//関数 : newしたとき振る舞いと同じ結果になる関数
function Class_Constructor(name, age ){
    var obj = {};
    obj.name = name;
    obj.age = age;
    return obj;
}
// インスタンス化(newは使わない)
var obj = Class_Constructor( 'Alice', 7 );
 
//よく見る一般的な関数コンストラクタ
var Person = function(name, age) {
    this.name = name;
    this.age = age;
};
 
// インスタンス化
var alice = new Person('Alice', 7);
 
console.log(obj); // { name: 'Alice', age: 7 }
console.log(alice);  // { name: 'Alice', age: 7 }

javascriptのthis

Java,C++と最も違う言語仕様だと思っている。thisを使う場所によって参照先が異なる模様。JavaやC++の場合、メンバ関数内で使えば常にthisはクラスのインスタンスを指すが、javascriptは違う。

  • コンストラクタ内のthis

インスタンス化されたオブジェクトへの参照になる

var Person = function( name ){
    // thisはPersonオブジェクトを指す
    this.name = name;
};
  • オブジェクトのメソッド

そのメソッドの呼び出し元への参照になる(メソッドのインスタンスではない)

var Person = {
    name : 'hoge',
    getName : function() {
        return this.name;
    }
};
  • イベンドハンドラ内のthis

メソッドと同じで、イベントの呼び出し元への参照になる

$("p").on("click", function(){
    // 要素pのテキストをアラート表示する
    alert( $(this).text() );
});

prototype

そもそもjavascriptのオブジェクトは、keyvalueペアを保持するMap連想配列と同じものである。オブジェクトに対し、参照したプロパティが存在しない場合、プロトタイプオブジェクトから探す仕組みがあり、どんどんプロトタイプをさかのぼって探すので、プロトタイプチェーンと呼ばれる。プロトタイプオブジェクトへのアクセス方法は、

  • 関数

this.prototype (初期値は空オブジェクト{})

  • オブジェクト

Object.getPrototypeOf( obj )

var Person = function(){
};
Person.prototype.name = "abc";
var per = new Person();

// Person{}
console.log(per) 
// Person{name:"abc"}
console.log(Object.getPrototypeOf(per)) 
// Person{name:"abc"}
console.log(Person.prototype) 
 
console.log(per.name) // "abc" ※プロトタイプチェーン

newとObject.createの違い

functionとnewを使ったクラス定義とインスタンス化と関数呼び出しは、何度も出てきているが、以下のような感じ。

// newを使ったクラス定義とインスタンス化
var Person1 = function(name, age){
    this.name = name;
    this.age = age;
    this.hello = function(){
        return "my name is "+this.name+". I'm " + this.age + " years olds.";
    };
}
// インスタンス化
var ins1 = new Person1("hoge", 20");
 
// my name is home. I'm 20 years old.
console.log(ins1.hello());

感覚的にはC++やJavaと似ている。いろんなサイトをみると、これでほとんど事足りており、今も使えて、あえて考えなおす必要なしというのが、ざっと見た感触である。

ただ、Javascriptの正道ではないらしいのと、オライリー本などを執筆している人たちは否定的である。確かにほかの処理はJson型を多用しているにもかかわらず、ここだけ違う様式ってのもちょっと引っかかっている。Jsonは使いやすいし、このスタイルで通して欲しいところでもある。

次にJson型を使って、上記と同じ結果を得ようとすると以下のようになる。
決定的な違いは、コンストラクタへの引数の渡し方である。Object.create()の第2引数を使って渡すのだが、決まった構造のJsonオブジェクトしか渡せない。

var Person2 = {
    name : "",
    age : 0,
    hello : function(){
        return "my name is "+this.name+". I'm " +
            this.age + " years olds.";
    }
}
var ins2 = Object.create(Person2,{name:{value:"hoge"},age:{value:20}});
 
// my name is home. I'm 20 years old.
console.log(ins2.hello());

Object.create()の第2引数の構文およびデフォルト値は以下になる

{
    プロパティ名1 : {
        value : undefined,
        writable : true,
        configurable : false,
        enumerable : false,
        get : undefined,
        set : undefined
    },
    プロパティ名2 : {
        :
    },
    :
}
項目内容
valueプロパティの値
writabletrueの場合、プロパティ値が変更できるようになる
configurabletrueの場合、プロパティを削除することができる
enumerabletrueの場合、オブジェクトのプロパティ列挙に表示される
getプロパティのgetterとして提供する関数
setプロパティのsetterとして提供する関数