あくまで覚学
継承のてすと

//オブジェクト


オブジェクト{}とはプロパティ(名前と値)を入れる箱。

誤用の多い、オブジェクト = 連想配列
javascriptでは、デフォルで「プロパティ」をもつから、文字も数字も関数もオブジェクトになる。
すべてがオブジェクト。

でも、一般的な「連想配列」のことを「連想配列は存在しない」とかわざわざ「オブジェクト」と呼ぶのはやめようね。
連想配列の宣言でこうしているのが、誤用の原因だろうね。

var hoge = {};
または
var hoge = new Object();

JavaScript のほぼすべてのオブジェクトが Object のインスタンス。
ふつうのオブジェクトは、プロパティを (メソッドを含めて) Object.prototype から継承。

一方、配列の宣言は、
var hoge = [ ];
または
var hoge = new Array( );

//コンストラクタ/9つのネイティブコンストラクタ

//classがjavascriptにもできたけど、必要なくね!?
new演算子!!を使って(定型な)オブジェクトを生成する関数をコンストラクタ関数と呼ぶ、
ネイティブオブジェクト(コンストラクタ)は、9つある。
Number (),String (),Boolean(),
Object(),Arry(),Function(),
Date(),RegExp(),Error()

関数化でユーザー定義オブジェクト = 自作コンストラクタ
//先頭を大文字にするのが慣例
var Parson = function(living,age,gender) {
 this.living = living;
 this['age'] = age; //ブラケット記法
 this.gender = gender;
 this.getGender = function(){ return this.gender };
//return this; の記述はいらない
//  this.gender = this.living + gender;のような使い方はできない
}


//使用
var cody = new Parson(true,33,'male');


//Object()コンストラクタで生成する場合:使い回しできない。
var cody = new Object(); // = {};
cody.living = true;
cody.age = 33;
cody.gender = 'male';
cody.getGender =  function(){ return cody.gender };

//new 演算子

-> new 演算子

//new演算子
1.空白のプレーンな JavaScript オブジェクトを作成します。
2.他のオブジェクトを親プロトタイプとすることで、新しく作成されたオブジェクトと他のオブジェクトをリンク(コンストラクターを設定)します。
3.ステップ 1 で新しく作成されたオブジェクトを this コンテキストとして渡します。
4.関数がオブジェクトを返さない場合は this を返します。

//ユーザー定義オブジェクト
1.関数を記述して、オブジェクトの型を定義します。
2.new 演算子を使用して、オブジェクトのインスタンスを生成します。

// new Person(ホゲホゲ)で起こってること
1.Person.prototype を継承する、新しいオブジェクトを生成。
2.指定した引数を伴ってコンストラクター関数 Person が呼び出され、this が新たに生成したオブジェクトに紐づけられます。
new Person は new Person() と等価。すなわち、引数を指定しない場合は Person が引数なしで呼び出されます。
3.コンストラクター関数が返すオブジェクト (null, false, 3.1415 などのプリミティブ型ではないもの) が、new 式の結果になります。
コンストラクター関数が明示的にオブジェクトを返さない場合は、ステップ 1 で生成したオブジェクトを代わりに使用します。
(通常、コンストラクターは値を返しませんが、通常のオブジェクト生成プロセスをオーバーライドしたい場合はそのようにすることができます。)

//※ new 演算子を記述しなかった場合、コンストラクターは通常の関数として扱われ、オブジェクトを作成しません。その際、this の値も異なるものになります。


//prototype (プロトタイプ)

-> 4つの継承方法


//prototypeというのはプロパティの一種で、最初は空のオブジェクトを「参照」しています。
//「参照」というのはデータそのものではなく、メモリ上の他の場所にあるデータを指すものです。
function Car() {}
car1 = new Car();
car2 = new Car();
console.log(car1.color);    // undefined
Car.prototype.color = "original color";
console.log(car1.color);    // original color

//prototypeを使って定義したメソッドは、インスタンスが生成されるたびにコピーされることはありません。
//メソッドがコピーされるわけではないので、メモリの節約になります。

var Person = function(name, age) {
    if(!(this instanceof Person)) {
        return new Person(name, age);
    }
    this.name = name;
    this.age  = age;
}
//prototypeプロパティを使い、コンストラクタの外でメソッド定義を行う
//このように個別に定義する方がおすすめ!
Person.prototype.setName = function(name) {
    this.name = name;
}
Person.prototype.getName = function() {
    return this.name;
}


//オブジェクト名.prototype.メソッド名= function() { }


//プロトタイプをまとめる方法
User.prototype = {
    getName: function() {
        return this.name;
    },

    getAge: function() {
        return this.age;
    }
}
//しかし、この後に
User.prototype = {
    getContry: function() {
        return this.contry;
    },
}
などと、追加するために記載してしまうと、
User.prototype =
を使っちゃうことで、それより前のメソッドは消えてしまう。。。

//オブジェクトのプロパティを「this」で呼び出し

//this呼び出しテスト
const object = {
    value: 'test',
    method1: function () {
        return this;
    },
    method2: function () {
        return this.value;
    }

};
//呼び出し
console.log(object.method1());
console.log(object.method2());


const guitarObject = {
    maker: "Fender",
    country: 'America',
    model: "Telecaster",
    year: 1964,
    color: 'Black',
    showMaker: function() {
        console.log(this.maker); // ① 何が出力される?

        const showCountry = function() {
            console.log(this.country);// ② 何が出力される?
        }
        showCountry();
    }
};

//ドット記法での呼び出し <-> ブラケット記法
guitarObject.showMaker();

//es5での解決策1
const guitarObject2 = {
    maker: "Fender",
    country: 'America',
    model: "Telecaster",
    year: 1964,
    color: 'Black',
    showMaker: function() {
        const self = this;// thisをスコープ変数に持たせる。selfにはguitarObjectが入る。
        console.log(self.maker); // ① 何が出力される?
        const showCountry = function() {
            console.log(self.country);// ② 何が出力される?
        };
        showCountry();
    }
};
guitarObject2.showMaker();

//es5での解決策2
const guitarObject3 = {
    maker: "Fender",
    country: 'America',
    model: "Telecaster",
    year: 1964,
    color: 'Black',
    showMaker: function() {
        console.log(this.maker); // ① 何が出力される?
        const showCountry = function() {
            console.log(this.country);// ② 何が出力される?
        }.bind(this);//bindメソッドを使用
        showCountry();
    }
};
guitarObject3.showMaker();


//プロトタイプチェーン


var User = function() {};
var Member = function() {};

User.prototype.hello = function() {
    return 'こんにちは!';
}

Member.prototype = new User();

var taro = new User();
var hanako = new Member();

console.log( taro.hello() );
console.log( hanako.hello() );

//擬似的にオーバーロード:引数の数が違う場合


function Cat(name, age) {

//期待した引数が入力されなかった場合に初期値を設定
  if ( name === undefined ) name = '匿名';
  if ( age === undefined ) age = '不明';

  //指定の引数以上が入力された場合の条件分岐処理
  if ( arguments.length > 2 ) alert('引数を正しく設定してください');

  this.name = name;
  this.age = age;
}

//引数を1つしか設定しない場合
var cat1 = new Cat( 'かんな' );

console.log( cat1 );
//{ name: "かんな", age: "不明" }


推奨://プロトタイプオブジェクトのメンバとして追加する方法
//この方がメモリ節約になる
  function Card(num){
      this.number = num;//return省略
  }
  Card.prototype.getNumber = function(){
      return this.number;
  };
  //この地点のメモリが節約
  var c = new Card(5);
  //メソッドは必ずしも使わない
  console.log(c.getNumber());  // 5

メモリを余計に消費:this.でコンストラクタメソッド追加
外に出してメモリ消費を減らす工夫をしないとね!!
//this.でコンストラクタメソッド追加
  this.メソッド名 = function(){...}
  function Card(num){
    this.number = num;
    this.getNumber = function(){
          return this.number;
      };
  }
  var c = new Card(5);
  console.log(c.getNumber());  // 5

//newを使わないでメソッド実装:Object.create

//newを使わないで実現
//Object.create
//メモリ節約にはならない?
function a( x ){
        const obj = Object.create(a.prototype);
        obj.x = x;
        return obj;
}
a.prototype={
        getX:function(){ return this.x }
};

const b =  a( 10 );
const c =  b.getX();  // c = 10

//class (クラス)


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

      get result() {
          this.checkAge();
      }

      checkAge() {
          if(this.age < 20) {
              console.log(this.name + 'は未成年です');
          } else {
              console.log(this.name + 'は成人です');
          }
      }
  }

  var person = new Person('太郎', 22);
  person.result;

//太郎は成人です

//extends(継承)で機能拡張するーprototypeの代わりに


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

class ChildPerson extends Person {
    get result() {
        this.checkAge();
    }

    checkAge() {
        if(this.age < 20) {
            console.log(this.name + 'は未成年です');
        } else {
            console.log(this.name + 'は成人です');
        }
    }
}

var person = new ChildPerson('太郎', 22);
person.result;

//literal (リテラル)


literal (リテラル):直接記述される数値や文字列。変数に対して。
関数リテラルとは、
var $fnc = function(){};
変数に無名関数をセット

//higer-order function = 高階関数 ・コールバック関数/(引数に関数をとる関数)


//データ配列
  const idols = [
    { name: '星宮 いちご',   type: 'cute' },
    { name: '霧矢 あおい',   type: 'cool' },
    { name: '紫吹 蘭',      type: 'sexy' },
    { name: '有栖川 おとめ', type: 'pop' },
    { name: '藤堂 ユリカ',  type: 'cool' },
    { name: '神谷 しおん',  type: 'cool' },
    { name: '一ノ瀬 かえで', type: 'pop' },
    { name: '三ノ輪 ヒカリ', type: 'sexy' },
    ...
  ];
//特定のtypeの配列にする
//普通に関数化したもの
const coolIdols = [];
for (var i = 0, l = idols.length; i < l; i += 1) {
  if ( idols[i].type === 'cool' ) {
    coolIdols.push(idols[i]);
  }
}
console.log(coolIdols);


//Array.filterを使う
const coolIdols = idols.filter((idol) => {
  return idol.type === 'cool';
});
console.log(coolIdols);

// filterの引数に渡す関数を分離すると
const isCoolIdol = function(idol) {
  return idol.type === 'cool';
};

//汎用性が高まる
const coolIdols = idols.filter( isCoolIdol );

//組み込みの高階関数
Array.prototype.map(fn:関数):
Array.prototype.filter(fn:関数):
Array.prototype.reduce(fn:Function [、initial:Any]):



//higer-order function 続き

//配列から条件にマッチしている要素を除くfilter
Array.prototype.reject = function(func) {
  const res = [];
  if ( typeof(func) !== 'function' ) {
    return this;
  }
  this.forEach(function(v) {
    if ( !func(v) ) {
      res.push(v);
    }
  });
  return res;
}

//除外できる
const notCoolIdols = idols.reject( isCoolIdol );



//カリー化(Currying)でprototypeの利用を避ける?


カリー化(Currying) の "Curry" は食べ物のカレーとスペルがまったく同じですが、カリー化は食べ物とは関係ない。
この辺りの分野に大きな業績のある論理学者・数学者のハスケル・カリー(Haskell Curry)さんの名前に由来。

//カーリー化 ー> 複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」

例1)カーリー化前
function add(x, y) {
    return x + y;
}
//複数の引数を使う・・リスクが上がる
> add(1,2)
// 3

//上記をカリー化(Currying)する
function add(x) {
    return function(y) {
        return x + y;
    };
}
> add(1)(2)
// 3

//部分適用
var add1 = add(1);
add1(2)
//3

これ¥が非カリーなら
function add1(y) {
    return add(1, y);
}
と別の関数を新たに作る必要がある


例2)動的に関数を作り出せる (= カリー化)ってこと?
function over(standard_value) {
  return function(x) {
    return x > standard_value;
  }
}

const over10 = over(10);
console.log( over10(5) );  // => false
console.log( over10(11) ); // => true


//higer-order function(高階関数)とカーリー化を組み合わせる


//カーリー化
function over(standard_value) {
  console.log('over init!');
  return function(x) {
    return x > standard_value;
  }
}

//高階関数
const over5Arr = [1,2,3,4,5,6,7,8,9,10].filter( over(5) );
// => over init!
console.log( over5Arr ); // => [ 6, 7, 8, 9, 10 ]


//currying
function idolType(type) {
  return function(idol) {//ここの変数は、スコープ外をとるのね!?
    return idol.type === type
  };
}

// type "cool" のリスト
const coolIdols    = idols.filter( idolType('cool') );
// type "cool" 以外のリスト
const notCoolIdols = idols.reject( idolType('cool') );
// type "cute" のリスト
const cuteIdols    = idols.filter( idolType('cute') );


//メモ化


var myFunc = function (param) {
    if (!myFunc.cache[param]) {
        var result = {};
        // ...ここで重い処理...

        // 処理結果を関数のプロパティに追加
        myFunc.cache[param] = result;
    }
    return myFunc.cache[cache];
}

//連鎖パターン


var obj = {
    value: 1,
    increment: function() {
        this.value += 1;
        return this;
    },
    add: function(v) {
        this.value += v;
        return this;
    },
    shout: function() {
        alert(this.value);
    }
};

// メソッド呼び出しの連鎖
obj.increment().add(3).shout(); // 5

//ポリモーフィズム


ポリモーフィズム(英: Polymorphism)とは、
プログラミング言語の型システムの性質を表すもので、
プログラミング言語の各要素(定数、変数、式、オブジェクト、関数、メソッドなど)について
それらが複数の型に属することを許すという性質を指す。
ポリモルフィズム、多態性、多相性、多様性とも呼ばれる。
対義語はモノモーフィズム (Monomorphism)、単態性、単相性で、
プログラミング言語の各要素が唯一つの型に属するという性質を指す。

ポリPoly 多くの
モーフィズムmorphism 変化

ポリエチレン ⇒ エチレンが多数集まったもの(ポリ)。

例)メソッド名は同じで、オブジェクトで結果が違う。
猫.鳴く() //ニャー
犬.鳴く() //ワン
豚.鳴く() //ブー

コードが見やすい。管理しやすい。


https://qiita.com/Nossa/items/a93024e653ff939115c6

//メソッドチェーンとドット記法


メソッドチェーンは、あるオブジェクトに対してメソッドを.(ドット)で連結して繋げていくこと。
ドット記法は、オブジェクトのプロパティにアクセスする方法のひとつ(もうひとつはブラケット記法)