JavaScript

Objects Ⅱ

28 May 2014

オブジェクトが数値型でも文字列型でもなくオブジェクトだとどうしたらわかるのでしょう? いいものがあります

型のわからない変数 thing があります、そこで typeof thing を呼ぶと、その型を知ることができます。

最も利用するのは、"number", "string", "function", "object"

var anObj = { job: "I'm an object!" };
var aNumber = 42;
var aString = "I'm a string!";

console.log( typeof anObj ); // print "object"
console.log( typeof aNumber ); // print "number"
console.log( typeof aString ); // print "string"

汝自身を知る

JavaScriptのオブジェクトは、道具箱を持っています。

その中には hasOwnProperty というメソッドが含まれています。

このメソッドは、オブジェクトが特定のプロパティを持っているかを truefalse で教えてくれます。

var myObj = {
    name:"Takeshi"
};

console.log( myObj.hasOwnProperty('name') ); 
                  // should print true
console.log( myObj.hasOwnProperty('nickname') ); 
                  // should print false

var suitcase = {
    shirt: "Hawaiian"
};
if (suitcase.hasOwnProperty('shorts')) {
  console.log(suitcase.shorts);
} else {
  suitcase.shorts = "boxer";
  console.log(suitcase.shorts);
}

for - in loop

オブジェクトに属するすべてのプロパティを扱います。

var dog = {
species: "bulldog",
age: 3,
color: brown
};

オブジェクト dog のすべてのプロパティを簡単に一度で表示します。

varin はキーワードです。この位置に必ず書きます。

dog を別のオブジェクトに置き替えます。

property は変数名で、別の名前で構いません。

for(var property in dog) {
  console.log(property);
}

プロパティの値を全部リストアップ

for-in loopですべてのプロパティ名を取得できました。

プロパティの値はどうでしょう?これもfor-in loopで取得できます。

dog.species = dog["species"] = "bulldog"

なので

var x = "species"

ならば

dog[x] = "bulldog"

var dog = {
species: "bulldog",
age: 3,
color: brown
};


var nyc = {
    fullName: "New York City",
    mayor: "Michael Bloomberg",
    population: 8000000,
    boroughs: 5
};

/* write a for-in loop 
  to print the value of nyc's properties */
for (var property in nyc) {
    console.log(nyc[property]);
}

Object Oriented Programmingの基礎

Class

コンストラクタを作成するとき、実は新しいクラスを定義しているのです。

クラスは、ある種の型と考えられるし、または、オブジェクトのカテゴリとも 考えられます(JavaScriptにおいて NumberString が型の種類であるように)。

bobsusan は、2つの別オブジェクトです。 いかし両者は Person クラスに属しています。

function Person(name,age) {
  this.name = name;
  this.age = age;
}

var bob = new Person("Bob Smith", 30);
var susan = new Person("Susan Jordan", 35);

スヌーピーに教える

クラスは、プロパティとメソッドを持ちます、しかし、与えられたクラスが 何ができて、何ができないのか、何を持っていて、何を持っていないのかを 見ているものは何なのでしょう。

この仕事をするのが prototype です。

JavaScriptは、コンストラクタでもって自動的にクラスのためのプロトタイプを 定義します。

Dog コンストラクタは、 Dog プロトタイプに必ず breed プロパティを持たせます。

buddy にメソッド bark を追加します。

buddy は吠えることができますが、

snoopy は同じ Dog クラスですが吠えることはできません。

snoopy にも吠え方を教えてあげなくてはいけません。

function Dog (breed) {
  this.breed = breed;
}

// buddyを作成し、吠え方(bark)を教えます
var buddy = new Dog("Golden Retriever");
buddy.bark = function() {
  console.log("Woof");
};
buddy.bark();

// snoopyを作成します
var snoopy = new Dog("Beagle");

// snoopyに吠え方を教えていないので、エラーになります
snoopy.bark();

// snoopyに 吠え方を教えます
snoopy.bark = function() {
   console.log("Wow");
};
// snoopyは吠えます
snoopy.bark();

クラスはどのように役に立つのか

クラスはoopでとても重要です。

オブジェクトに関する有用な情報を、クラスは教えてくれるからです。

オブジェクトはクラスの実体と考えられます。

また Person クラスを見ましょう。

どの Person にも nameage があります。

それはコンストラクタにあるからです。

このことは、 Person を引数として受け取り、その名前を表示する printPersonName のような関数を作ることができます。

この関数はどの Person に対しても動作します。

なぜなら name はそのクラスの有効なプロパティだからです。

function Person(name,age) {
  this.name = name;
  this.age = age;
}
// 引数Personの名前を表示する関数
function printPersonName(p) {
  console.log(p.name);
}

var bob = new Person("Bob Smith", 30);
printPersonName(bob);

var me = new Person("Takeshi Koyama", 54);
printPersonName(me);

プロトタイプを応援!

また似たようなコードですが、重要な違いがあります。

buddy オブジェクトに bark メソッドを追加するのに

buddy.bark を使う代わりに Dog.prototype.bark を使っています。

これは、 Dog クラスのプロトタイプを変更したことになります。

これで buddysnoopy も吠えることができます。

こうするとすべての Dog に、直ちに新しいメソッドを教えられます。

function Dog (breed) {
  this.breed = breed;
}

// buddyを作成し吠え方(bark)を教える
var buddy = new Dog("golden Retriever");
Dog.prototype.bark = function() {
  console.log("Woof");
};
buddy.bark();

// snoopyを作成
var snoopy = new Dog("Beagle");
/// 今度は動く
snoopy.bark();

一般に、クラスのメンバーすべてが使えるメソッドをクラスに追加するには、 下記のように記述しプロトタイプを拡張します。

className.prototype.newMethod =function() {
statements;
};

富を受け継ぐ

すべては遺伝子に

OOPでは、 継承 は、あるクラスが他のクラスの プロパティを見たりメソッドを使えるようにします。

まずは復習

// Animal class を作る
function Animal(name,numLegs){
  this.name = name;
  this.numLegs = numLegs;
}

// sayName method for Animal を作る
Animal.prototype.sayName = function() {
  console.log("Hi my name is " + this.name);
};

// penguinオブジェクトを作り、メソッドを実行する
var penguin = new Animal("Captain Cook", 2);
penguin.sayName();

ペンギンの行進

Animalクラスと同様なPenguinクラスを作ります。

// Animal class を作る
function Animal(name,numLegs){
  this.name = name;
  this.numLegs = numLegs;
}

// sayName method for Animal を作る
Animal.prototype.sayName = function() {
  console.log("Hi my name is " + this.name);
};

// Penguin class を作る
function Penguin(name,numLegs){
  this.name = name;
  this.numLegs = numLegs;
}

// sayName method for Penguin を作る
Penguin.prototype.sayName = function() {
  console.log("Hi my name is " + this.name);
};

// penguinオブジェクトを作り、メソッドを実行
var penguin = new Penguin("Captain Cook", 2);
penguin.sayName();

DRY ペンギン

前掲のコードでは、*Penguin* は Animal クラスの多くのコードを再度書いています。

これはプログラミングの"DRY"principle(コードの重複記述を避ける)に反します。

ここで 継承 が役に立ちます。

PenguinAnimal ですから、 Animal として すべてのプロパティとメソッドを持たせるべきです。

X is Y の関係がある場合は、継承を使ういいチャンスです。

継承 は他のクラスのプロパティやメソッドを見たり使ったりさせます。

PenguinAnimal を継承させましょう。

それには PenguinprototypeAnimal にします。

Penguin クラスを定義します。

Penguin コンストラクタは元の Animal コンストラクタに比べユニークです。

ペンギンはすべて2本脚ですし、引数は name しかとりません。

PenguinAnimal から_継承_ させましょう。

Penguin.prototype = new Animal()

// the original Animal class and sayName method
function Animal(name, numLegs) {
    this.name = name;
    this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
    console.log("Hi my name is "+this.name);
};

// define a Penguin class
function Penguin(name){
  this.name = name;
  this.numLegs = 2;
}

// set its prototype to be a new instance of Animal
Penguin.prototype = new Animal();
penguin = new Penguin("Take");
penguin.sayName();

ペンギン、プロパティ、プロトタイプ

Penguin が、どのように sayName メソッドを Animal から継承するかをみました。

今度はクラスがプロパティを継承するのを見てみましょう。

Penguin クラスを定義します。 Animal から何も継承していません。

Emperor クラスを定義します。

引数 name を受け取り、プロパティ name にセットします。

numLegs プロパティは、このコンストラクタの中ではセットしません。

EmperorPenguin を継承します。

emperor オブジェクトを作成し、 numLegs を表示します。

このプロパティは Penguin から継承されたものです。

function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}

// Emperor class を作成し Penguin classを継承します
function Emperor(name) {
  this.name = name;
}
Emperor.prototype = new Penguin();
// emperor オブジェクトを作成し number of legs を表示します。
var emperor = new Emperor("Kate");
console.log(emperor.numLegs);

食べ物は上! プロトタイプ連鎖

ペンギンは動物で、皇帝ペンギンはペンギンで、 そして皇帝ペンギンは動物です。

JavaScriptの"プロトタイプ チェーン"は、このことをよく知っています。

JavaScriptが今いるクラスのメソッドやプロパティに見つからないものがあると、 継承したクラスの中に定義されているかどうか調べに、プロトタイプ チェーンを 上へと探っていきます。この探索は最上位の Object.prototype で止まるまで続きます。

既定では、すべてのクラスは直接 Object を継承しています、 ただし PenguinEmperor のようにクラスの prototype を変更していなければ。

// original classes
function Animal(name, numLegs) {
    this.name = name;
    this.numLegs = numLegs;
    this.isAlive = true;
}
function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}
function Emperor(name) {
    this.name = name;
    this.saying = "Waddle waddle";
}

// set up the prototype chain 
// Animal -> Penguin -> Emperor
Penguin.prototype = new Animal();
Emperor.prototype = new Penguin();

var myEmperor = new Emperor("Jules");

console.log( myEmperor.saying ); // should print "Waddle waddle"
console.log( myEmperor.numLegs ); // should print 2
console.log( myEmperor.isAlive ); // should print true

プライバシーで

公開する

JavaScriptでは、オブジェクトのすべてのプロパティは自動的にパブリックになります。

パブリック とは、そのクラスの外部からアクセスできることです。

このプロパティは、クラスが共有したい情報として考えられます。

Person クラスには、パブリックなプロパティの firstName , lastName , age があります。

john のプロパティ firstName , LastName , age にアクセスして、 myFirst , myLast , myAge に設定しています。

function Person(first,last,age) {
   this.firstName = first;
   this.lastName = last;
   this.age = age;
}

var john = new Person('John','Smith',30);
var myFirst = john.firstName;
var myLast = john.lastName;
var myAge = john.age;

プライベート変数

オブジェクトが情報のいくつかを隠したい場合はどうするか?

関数が、その関数の中からのみアクセスできるローカル変数を持てるように、 オブジェクトはプライベート変数を持てます。

プライベート 変数は、共有したくない情報であり、 そのクラスの中からだけ直接アクセスできるものです。

Person クラスを変更して、プライベート変数 bankBalance を追加します。

普通の変数のように見えますが、 Person コンストラクタの中に定義されていて、 this の代わりに var が使われています。

これで bankBalance はプライベート変数になります。

function Person(first,last,age) {
   this.firstname = first;
   this.lastname = last;
   this.age = age;
   var bankBalance = 7500;
}

// create your Person 
var john = new Person("Kate","John",33);

// try to print his bankBalance
// should print "undefined"
console.log(john.bankBalance); 

プライベート変数を扱う

プライベート変数に、そのクラスの外から直接アクセスできませんが、方法があります。

プライベート変数の値を返すパブリック・メソッドを定義することができます。

bankBalance を返す getBalance メソッドを Person クラスに追加しています。

function Person(first,last,age) {
   this.firstname = first;
   this.lastname = last;
   this.age = age;
   var bankBalance = 7500;
  
   this.getBalance = function() {
      // your code should return the bankBalance
      return bankBalance;
   };
}

var john = new Person('John','Smith',30);
// should print "undefined"
console.log(john.bankBalance);

/* create a new variable myBalance 
               that calls getBalance() */
var myBalance = john.getBalance();
console.log(myBalance); // should print 7500

プライベートメソッド

メソッドも、クラスの外からアクセスできないプライベートにすることができます。

前項の this. getBalancevar getBalance に変更すると、このメソッドはプライベートになります。これだけではまだ駄目です。

プライベート メソッドにアクセスする方法は、 プライベート変数にアクセスするのと似ています。

そのクラスにプライベート メソッドを返す、パブリック メソッド this.askTeller を作成します。

function Person(first,last,age) {
   this.firstname = first;
   this.lastname = last;
   this.age = age;
   var bankBalance = 7500;
  
   // private method
   var getBalance = function() {
      return bankBalance;
   };
       
   // public method
   this.askTeller = function() {  
   return getBalance();
   };
}

var john = new Person('John','Smith',30);
// print "undefined" error
console.log(john.getBalance);  
// puplic method -> private method
var myBalanceMethod = john.askTeller(); 
var myBalance = myBalanceMethod;
console.log(myBalance); // print 7500

引数を受け渡す

Person クラス内の askTeller 関数を修正し 直接口座情報を返すようにしてあります、 しかし、 bankBalance を返すには、引数としてパスワードを必要としています。

function Person(first,last,age) {
   this.firstname = first;
   this.lastname = last;
   this.age = age;
   var bankBalance = 7500;
  
  // public method
   this.askTeller = function(pass) {
     if (pass == 1234) return bankBalance;
     else return "Wrong password.";
   };
}

var john = new Person('John','Smith',30);
//パスワードを引数にしてaskTellerにアクセス
var myBalance = john.askTeller("1234");