簡単に蚀うずオブゞェクト指向のJavaScript





良い䞀日、友達



JavaScriptでオブゞェクトを䜜成する方法は4぀ありたす。



  • コンストラクタヌ関数
  • クラスクラス
  • 他のオブゞェクトにリンクしおいるオブゞェクトOLOO
  • 工堎機胜


どの方法を䜿甚する必芁がありたすかどれが最高ですか



これらの質問に答えるために、各アプロヌチを個別に怜蚎するだけでなく、継承、カプセル化、キヌワヌド「this」、むベントハンドラヌの基準に埓っお、クラスずファクトリ関数を比范したす。



オブゞェクト指向プログラミングOOPずは䜕かから始めたしょう。



OOPずは䜕ですか



基本的に、OOPは、単䞀のオブゞェクトを䜿甚しおオブゞェクトを䜜成できるようにするコヌドを䜜成する方法です。これは、コンストラクタヌの蚭蚈パタヌンの本質でもありたす。共有オブゞェクトは通垞、ブルヌプリント、ブルヌプリント、たたはブルヌプリントず呌ばれ、共有オブゞェクトが䜜成するオブゞェクトはむンスタンスです。



各むンスタンスには、芪から継承されたプロパティず独自のプロパティの䞡方がありたす。たずえば、Humanプロゞェクトがある堎合、それに基づいお異なる名前のむンスタンスを䜜成できたす。



OOPの2番目の偎面は、レベルの異なる耇数のプロゞェクトがある堎合のコヌドの構造化です。これは、継承たたはサブクラス化ず呌ばれたす。



OOPの3番目の偎面はカプセル化です。これは、実装の詳现を倖郚から隠し、倖郚から倉数や関数にアクセスできないようにする堎合です。これが、モゞュヌルずファサヌドの蚭蚈パタヌンの本質です。



オブゞェクトを䜜成する方法に移りたしょう。



オブゞェクト䜜成方法



コンストラクタヌ関数


コンストラクタヌは、「this」キヌワヌドを䜿甚する関数です。



    function Human(firstName, lastName) {
        this.firstName = firstName
        this.lastName = lastName
    }


これにより、䜜成䞭のむンスタンスの䞀意の倀を保存しおアクセスできたす。むンスタンスは、「new」キヌワヌドを䜿甚しお䜜成されたす。



const chris = new Human('Chris', 'Coyier')
console.log(chris.firstName) // Chris
console.log(chris.lastName) // Coyier

const zell = new Human('Zell', 'Liew')
console.log(zell.firstName) // Zell
console.log(zell.lastName) // Liew


クラス


クラスは、コンストラクタヌ関数の抜象化「構文シュガヌ」です。むンスタンスの䜜成が簡単になりたす。



    class Human {
        constructor(firstName, lastName) {
            this.firstName = firstName
            this.lastName = lastName
        }
    }


コンストラクタヌには、䞊蚘のコンストラクタヌ関数ず同じコヌドが含たれおいるこずに泚意しおください。これを初期化するには、これを行う必芁がありたす。初期倀を割り圓おる必芁がない堎合は、コンストラクタヌを省略できたす。



䞀芋するず、クラスはコンストラクタヌよりも耇雑に芋えたす。より倚くのコヌドを䜜成する必芁がありたす。あなたの銬を保持し、結論にゞャンプしないでください。クラスはかっこいいです。理由は少し埌でわかりたす。



むンスタンスも「new」キヌワヌドを䜿甚しお䜜成されたす。



const chris = new Human('Chris', 'Coyier')

console.log(chris.firstName) // Chris
console.log(chris.lastName) // Coyier


オブゞェクトのリンク


オブゞェクトを䜜成するこの方法は、KyleSimpsonによっお提案されたした。このアプロヌチでは、プロゞェクトを通垞のオブゞェクトずしお定矩したす。次に、メ゜ッド通垞はinitず呌ばれたすが、クラスのコンストラクタヌずは異なり、これは必須ではありたせんを䜿甚しお、むンスタンスを初期化したす。



const Human = {
    init(firstName, lastName) {
        this.firstName = firstName
        this.lastName = lastName
    }
}


Object.createは、むンスタンスを䜜成するために䜿甚されたす。むンスタンス化埌、initが呌び出されたす。



const chris = Object.create(Human)
chris.init('Chris', 'Coyier')

console.log(chris.firstName) // Chris
console.log(chris.lastName) // Coyier


これをinitに戻すこずで、コヌドを少し改善できたす。



const Human = {
  init () {
    // ...
    return this
  }
}

const chris = Object.create(Human).init('Chris', 'Coyier')
console.log(chris.firstName) // Chris
console.log(chris.lastName) // Coyier


工堎機胜


ファクトリ関数は、オブゞェクトを返す関数です。任意のオブゞェクトを返すこずができたす。クラスたたはオブゞェクトバむンディングのむンスタンスを返すこずもできたす。



ファクトリ関数の簡単な䟋を次に瀺したす。



function Human(firstName, lastName) {
    return {
        firstName,
        lastName
    }
}


むンスタンスを䜜成するのに「this」キヌワヌドは必芁ありたせん。関数を呌び出すだけです。



const chris = Human('Chris', 'Coyier')

console.log(chris.firstName) // Chris
console.log(chris.lastName) // Coyier


次に、プロパティずメ゜ッドを远加する方法を芋おみたしょう。



プロパティずメ゜ッドの定矩



メ゜ッドは、オブゞェクトのプロパティずしお宣蚀された関数です。



    const someObject = {
        someMethod () { /* ... */ }
    }


OOPでは、プロパティずメ゜ッドを定矩する方法が2぀ありたす。



  • 䞀䟋ずしお
  • プロトタむプで


コンストラクタヌでのプロパティずメ゜ッドの定矩


むンスタンスにプロパティを定矩するには、それをコンストラクタヌ関数に远加する必芁がありたす。必ずこれにプロパティを远加しおください。



function Human (firstName, lastName) {
  //  
  this.firstName = firstName
  this.lastname = lastName

  //  
  this.sayHello = function () {
    console.log(`Hello, I'm ${firstName}`)
  }
}

const chris = new Human('Chris', 'Coyier')
console.log(chris)






メ゜ッドは通垞、プロトタむプで定矩されたす。これにより、むンスタンスごずに関数を䜜成する必芁がなくなりたす。すべおのむンスタンスが単䞀の機胜共有たたは分散機胜ず呌ばれるを共有できるようにしたす。



プロトタむプにプロパティを远加するには、prototypeを䜿甚したす。



function Human (firstName, lastName) {
  this.firstName = firstName
  this.lastname = lastName
}

//    
Human.prototype.sayHello = function () {
  console.log(`Hello, I'm ${this.firstName}`)
}






耇数のメ゜ッドを䜜成するのは面倒です。



//    
Human.prototype.method1 = function () { /*...*/ }
Human.prototype.method2 = function () { /*...*/ }
Human.prototype.method3 = function () { /*...*/ }


Object.assignを䜿甚するず、䜜業が楜になりたす。



Object.assign(Human.prototype, {
  method1 () { /*...*/ },
  method2 () { /*...*/ },
  method3 () { /*...*/ }
})


クラスでのプロパティずメ゜ッドの定矩


むンスタンスプロパティは、コンストラクタヌで定矩できたす。



class Human {
  constructor (firstName, lastName) {
    this.firstName = firstName
      this.lastname = lastName

      this.sayHello = function () {
        console.log(`Hello, I'm ${firstName}`)
      }
  }
}






プロトタむププロパティは、コンストラクタの埌に通垞の関数ずしお定矩されたす。



class Human (firstName, lastName) {
  constructor (firstName, lastName) { /* ... */ }

  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}






クラスで耇数のメ゜ッドを䜜成するこずは、コンストラクタヌよりも簡単です。これにはObject.assignは必芁ありたせん。他の機胜を远加しおいるだけです。



class Human (firstName, lastName) {
  constructor (firstName, lastName) { /* ... */ }

  method1 () { /*...*/ }
  method2 () { /*...*/ }
  method3 () { /*...*/ }
}


オブゞェクトをバむンドするずきのプロパティずメ゜ッドの定矩


むンスタンスのプロパティを定矩するには、これにプロパティを远加したす。



const Human = {
  init (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
    this.sayHello = function () {
      console.log(`Hello, I'm ${firstName}`)
    }

    return this
  }
}

const chris = Object.create(Human).init('Chris', 'Coyier')
console.log(chris)






プロトタむプメ゜ッドは、通垞のオブゞェクトずしお定矩されおいたす。



const Human = {
  init () { /*...*/ },
  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}






ファクトリ関数FFでのプロパティずメ゜ッドの定矩


プロパティずメ゜ッドは、返されるオブゞェクトに含めるこずができたす。



function Human (firstName, lastName) {
  return {
    firstName,
    lastName,
    sayHello () {
      console.log(`Hello, I'm ${firstName}`)
    }
  }
}






FFを䜿甚する堎合、プロトタむププロパティを定矩するこずはできたせん。このようなプロパティが必芁な堎合は、クラス、コンストラクタヌ、たたはオブゞェクトバむンディングのむンスタンスを返すこずができたすただし、それは意味がありたせん。



//   
function createHuman (...args) {
  return new Human(...args)
}


プロパティずメ゜ッドを定矩する堎所



プロパティずメ゜ッドをどこで定矩する必芁がありたすかむンスタンスたたはプロトタむプ



倚くの人は、プロトタむプがこれに適しおいるず考えおいたす。



しかし、それは本圓に重芁ではありたせん。



むンスタンスにプロパティずメ゜ッドを定矩するこずにより、各むンスタンスはより倚くのメモリを消費したす。プロトタむプでメ゜ッドを定矩する堎合、メモリの消費量は少なくなりたすが、重芁ではありたせん。最新のコンピュヌタヌの胜力を考えるず、この違いは重芁ではありたせん。ですから、あなたに最適なこずは䜕でもしおください。



たずえば、クラスたたはオブゞェクトバむンディングを䜿甚する堎合は、コヌドを蚘述しやすくするため、プロトタむプを䜿甚するこずをお勧めしたす。 FFの堎合、プロトタむプは䜿甚できたせん。むンスタンスのプロパティのみを定矩できたす。



箄 per 。䜜者に反察させおください。プロパティずメ゜ッドを定矩するずきにむンスタンスの代わりにプロトタむプを䜿甚する問題は、メモリ䜿甚量の問題だけでなく、䜕よりも定矩されおいるプロパティたたはメ゜ッドの目的の問題です。プロパティたたはメ゜ッドがむンスタンスごずに䞀意である必芁がある堎合は、むンスタンスで定矩する必芁がありたす。プロパティたたはメ゜ッドをすべおのむンスタンスで同じ共通にする堎合は、プロトタむプで定矩する必芁がありたす。埌者の堎合、プロパティたたはメ゜ッドに倉曎を加える必芁がある堎合は、個別に調敎されるむンスタンスのプロパティおよびメ゜ッドずは察照的に、プロトタむプに倉曎を加えるだけで十分です。



予備的結論



調査した資料に基づいお、いく぀かの結論を導き出すこずができたす。それは私の個人的な意芋です。



  • クラスは、耇数のメ゜ッドを簡単に定矩できるため、コンストラクタヌよりも優れおいたす。
  • Object.createを䜿甚する必芁があるため、オブゞェクトのバむンドは奇劙に思えたす。このアプロヌチを研究するずき、私はこれを忘れ続けたした。私にずっお、これはそれ以䞊の䜿甚を拒吊するのに十分な理由でした。
  • クラスずFFが最も䜿いやすいです。問題は、プロトタむプがFFで䜿甚できないこずです。しかし、先に述べたように、それは実際には問題ではありたせん。


次に、JavaScriptでオブゞェクトを䜜成する2぀の最良の方法ずしお、クラスずFFを比范したす。



クラスvs.FF-継承



クラスずFFの比范に進む前に、OOPの根底にある3぀の抂念を理解する必芁がありたす。



  • 継承
  • カプセル化
  • この


継承から始めたしょう。



継承ずは䜕ですか


JavaScriptでは、継承ずは、プロパティを芪から子に枡すこずを意味したす。プロゞェクトからむンスタンスぞ。



これは2぀の方法で発生したす。



  • むンスタンスの初期化を䜿甚する
  • プロトタむプチェヌンを䜿甚する


2番目のケヌスでは、芪プロゞェクトが子プロゞェクトで展開されたす。これはサブクラス化ず呌ばれたすが、継承ず呌ばれるこずもありたす。



サブクラス化を理解する


サブクラス化ずは、子プロゞェクトが芪を拡匵するこずです。



クラスの䟋を芋おみたしょう。



クラスによるサブクラス化


「extends」キヌワヌドは、芪クラスを拡匵するために䜿甚されたす。



class Child extends Parent {
    // ...
}


たずえば、Humanクラスを拡匵するDeveloperクラスを䜜成したしょう。



//  Human
class Human {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}


Developerクラスは、Humanを次のように拡匵したす。



class Developer extends Human {
  constructor(firstName, lastName) {
    super(firstName, lastName)
  }

    // ...
}


「super」キヌワヌドは、「Human」クラスのコンストラクタヌを呌び出したす。これが必芁ない堎合は、superを省略できたす。



class Developer extends Human {
  // ...
}


開発者がコヌドを曞くこずができるずしたしょう誰が考えたでしょう。察応するメ゜ッドを远加したしょう。



class Developer extends Human {
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }
}


これは、「Developer」クラスのむンスタンスの䟋です。



const chris = new Developer('Chris', 'Coyier')
console.log(chris)






FFによるサブクラス化


FFを䜿甚しおサブクラスを䜜成するには、次の4぀の手順を実行する必芁がありたす。



  • 新しいFFを䜜成する
  • 芪プロゞェクトのむンスタンスを䜜成したす
  • このむンスタンスのコピヌを䜜成したす
  • このコピヌにプロパティずメ゜ッドを远加したす


このプロセスは次のようになりたす。



function Subclass (...args) {
  const instance = ParentClass(...args)
  return Object.assign({}, instance, {
    //   
  })
}


サブクラス「Developer」を䜜成したしょう。FF「ヒュヌマン」はこんな感じ。



function Human (firstName, lastName) {
  return {
    firstName,
    lastName,
    sayHello () {
      console.log(`Hello, I'm ${firstName}`)
    }
  }
}


開発者を䜜成したす。



function Developer (firstName, lastName) {
  const human = Human(firstName, lastName)
  return Object.assign({}, human, {
    //   
  })
}


「コヌド」メ゜ッドを远加したす。



function Developer (firstName, lastName) {
  const human = Human(firstName, lastName)
  return Object.assign({}, human, {
    code (thing) {
      console.log(`${this.firstName} coded ${thing}`)
    }
  })
}


Developerのむンスタンスを䜜成したす。



const chris = Developer('Chris', 'Coyier')
console.log(chris)






芪メ゜ッドを䞊曞きする


サブクラス内の芪メ゜ッドを䞊曞きする必芁がある堎合がありたす。これは次のように実行できたす。



  • 同じ名前のメ゜ッドを䜜成したす
  • 芪メ゜ッドを呌び出すオプション
  • サブクラスに新しいメ゜ッドを䜜成したす


このプロセスは次のようになりたす。



class Developer extends Human {
  sayHello () {
    //   
    super.sayHello()

    //   
    console.log(`I'm a developer.`)
  }
}

const chris = new Developer('Chris', 'Coyier')
chris.sayHello()






FFを䜿甚した同じプロセス。



function Developer (firstName, lastName) {
  const human = Human(firstName, lastName)

  return Object.assign({}, human, {
      sayHello () {
        //   
        human.sayHello()

        //   
        console.log(`I'm a developer.`)
      }
  })
}

const chris = new Developer('Chris', 'Coyier')
chris.sayHello()






継承ず構成


継承に぀いおの䌚話は、構成に蚀及せずに行われるこずはめったにありたせん。゚リック゚リオットのような専門家は、可胜な限り䜜曲を䜿甚すべきだず信じおいたす。



䜜曲ずは



構成を理解する


基本的に、構成はいく぀かのものを1぀に組み合わせたものです。オブゞェクトを組み合わせる最も䞀般的で最も簡単な方法は、Object.assignを䜿甚するこずです。



const one = { one: 'one' }
const two = { two: 'two' }
const combined = Object.assign({}, one, two)


構成は䟋で説明するのが最も簡単です。DeveloperずDesignerの2぀のサブクラスがあるずしたしょう。蚭蚈者は蚭蚈方法を知っおおり、開発者はコヌドの蚘述方法を知っおいたす。どちらも「Human」クラスを継承しおいたす。



class Human {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}

class Designer extends Human {
  design (thing) {
    console.log(`${this.firstName} designed ${thing}`)
  }
}

class Developer extends Designer {
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }
}


ここで、3番目のサブクラスを䜜成するずしたす。このサブクラスは、蚭蚈者ず開発者が混圚しおいる必芁がありたす。コヌドの蚭蚈ず蚘述の䞡方ができる必芁がありたす。それをDesignerDeveloperたたは必芁に応じおDeveloperDesignerず呌びたしょう。



どうやっお䜜成したすか



「Designer」クラスず「Developer」クラスを同時に拡匵するこずはできたせん。どのプロパティを最初に指定するかを決定できないため、これは䞍可胜です。これは、ダむダモンド問題ダむダモンド継承ず呌ばれたす。







あるオブゞェクトを別のオブゞェクトよりも優先する堎合、Rhombusの問題はObject.assignで解決できたす。ただし、JavaScriptは耇数の継承をサポヌトしおいたせん。



//  
class DesignerDeveloper extends Developer, Designer {
  // ...
}


ここで䜜曲が圹に立ちたす。



このアプロヌチでは、次のように述べおいたす。DesignerDeveloperをサブクラス化する代わりに、必芁に応じおサブクラス化できるスキルを含むオブゞェクトを䜜成したす。



このアプロヌチの実装は、次のこずに぀ながりたす。



const skills = {
    code (thing) { /* ... */ },
    design (thing) { /* ... */ },
    sayHello () { /* ... */ }
}


指定されたオブゞェクトを䜿甚しお3぀の異なるクラスを䜜成できるため、Humanクラスはもう必芁ありたせん。



DesignerDeveloperのコヌドは次のずおりです。



class DesignerDeveloper {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName

    Object.assign(this, {
      code: skills.code,
      design: skills.design,
      sayHello: skills.sayHello
    })
  }
}

const chris = new DesignerDeveloper('Chris', 'Coyier')
console.log(chris)






DesignerずDeveloperでも同じこずができたす。



class Designer {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName

    Object.assign(this, {
      design: skills.design,
      sayHello: skills.sayHello
    })
  }
}

class Developer {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName

    Object.assign(this, {
      code: skills.code,
      sayHello: skills.sayHello
    })
  }
}


むンスタンスにメ゜ッドを䜜成しおいるこずに気づきたしたかこれは、可胜なオプションの1぀にすぎたせん。プロトタむプにメ゜ッドを配眮するこずもできたすが、䞍芁だず思いたすこのアプロヌチは、コンストラクタヌに戻ったようです。



class DesignerDeveloper {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

Object.assign(DesignerDeveloper.prototype, {
  code: skills.code,
  design: skills.design,
  sayHello: skills.sayHello
})






適切ず思われるアプロヌチを䜿甚しおください。結果は同じになりたす。



FFによる構成


FFを䜿甚した構成ずは、返されたオブゞェクトに分散メ゜ッドを远加するこずです。



function DesignerDeveloper (firstName, lastName) {
  return {
    firstName,
    lastName,
    code: skills.code,
    design: skills.design,
    sayHello: skills.sayHello
  }
}






継承ず構成


継承ず構成を同時に䜿甚できないずは誰も蚀っおいたせん。



Designer、Developer、およびDesignerDeveloperの䟋に戻るず、これらも人間であるこずに泚意しおください。したがっお、Humanクラスを拡匵できたす。



これは、クラス構文を䜿甚した継承ず構成の䟋です。



class Human {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }

  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}

class DesignerDeveloper extends Human {}
Object.assign(DesignerDeveloper.prototype, {
  code: skills.code,
  design: skills.design
})






そしお、これはFFの䜿甚ず同じです。



function Human (firstName, lastName) {
  return {
    firstName,
    lastName,
    sayHello () {
      console.log(`Hello, I'm ${this.firstName}`)
    }
  }
}

function DesignerDeveloper (firstName, lastName) {
  const human = Human(firstName, lastName)
  return Object.assign({}, human, {
    code: skills.code,
    design: skills.design
  })
}






実䞖界のサブクラス


倚くの専門家は、構成はサブクラスよりも柔軟であるしたがっおより有甚であるず䞻匵しおいたすが、サブクラスを軜芖すべきではありたせん。私たちが扱うこずの倚くは、この戊略に基づいおいたす。



䟋「クリック」むベントはMouseEventです。MouseEventはUIEventナヌザヌむンタヌフェむスむベントのサブクラスであり、UIEventナヌザヌむンタヌフェむスむベントはEventむベントのサブクラスです。







別の䟋HTML芁玠はノヌドのサブクラスです。したがっお、ノヌドのすべおのプロパティずメ゜ッドを䜿甚できたす。







継承に関する予備的結論


継承ず構成は、クラスずFFの䞡方で䜿甚できたす。FFでは、構成は「よりクリヌン」に芋えたすが、これはクラスよりもわずかに有利です。



比范を続けたしょう。



クラスvs.FF-カプセル化



基本的に、カプセル化ずは、あるものを別の物の䞭に隠し、内偎の゚ッセンスに倖偎からアクセスできないようにするこずです。



JavaScriptでは、非衚瀺の゚ンティティは、珟圚のコンテキストでのみ䜿甚できる倉数および関数です。この堎合、コンテキストはスコヌプず同じです。



単玔なカプセル化


カプセル化の最も単玔な圢匏は、コヌドのブロックです。



{
  // ,  ,     
}


ブロック内では、ブロック倖で宣蚀された倉数にアクセスできたす。



const food = 'Hamburger'

{
  console.log(food)
}






しかし、その逆はありたせん。



{
  const food = 'Hamburger'
}

console.log(food)






「var」キヌワヌドで宣蚀された倉数には、グロヌバルスコヌプたたは機胜スコヌプがあるこずに泚意しおください。varを䜿甚しお倉数を宣蚀しないようにしおください。



機胜によるカプセル化


機胜スコヌプはブロックスコヌプに䌌おいたす。関数で宣蚀された倉数は、関数内でのみアクセスできたす。これは、varで宣蚀されたものも含め、すべおの倉数に適甚されたす。



function sayFood () {
  const food = 'Hamburger'
}

sayFood()
console.log(food)






関数の内郚にいるずきは、関数の倖郚で宣蚀された倉数にアクセスできたす。



const food = 'Hamburger'

function sayFood () {
  console.log(food)
}

sayFood()






関数は、埌で関数の倖郚で䜿甚できる倀を返すこずができたす。



function sayFood () {
  return 'Hamburger'
}

console.log(sayFood())






閉鎖


クロヌゞャヌは、カプセル化の高床な圢匏です。これは、別の関数内の関数にすぎたせん。



//  
function outsideFunction () {
  function insideFunction () { /* ... */ }
}




outsideFunctionで宣蚀された倉数は、insideFunctionで䜿甚できたす。



function outsideFunction () {
  const food = 'Hamburger'
  console.log('Called outside')

  return function insideFunction () {
    console.log('Called inside')
    console.log(food)
  }
}

//  outsideFunction,   insideFunction
//  insideFunction   "fn"
const fn = outsideFunction()






カプセル化ずOOP


オブゞェクトを䜜成するずきは、䞀郚のプロパティをパブリックパブリックにし、他のプロパティをプラむベヌトプラむベヌトたたはプラむベヌトにする必芁がありたす。



䟋を芋おみたしょう。車のプロゞェクトがあるずしたしょう。新しいむンスタンスを䜜成するずきに、倀50の「fuel」プロパティをむンスタンスに远加したす。



class Car {
  constructor () {
    this.fuel = 50
  }
}




ナヌザヌはこのプロパティを䜿甚しお、残りの燃料の量を決定できたす。



const car = new Car()
console.log(car.fuel) // 50




ナヌザヌは自分で燃料の量を蚭定するこずもできたす。



const car = new Car()
car.fuel = 3000
console.log(car.fuel) // 3000


車のタンクが最倧100リットルの燃料を保持するずいう条件を远加したしょう。車を壊す可胜性があるため、ナヌザヌが自分で燃料の量を蚭定できるようにしたくありたせん。



これを行うには2぀の方法がありたす。



  • 慣䟋による私有財産の䜿甚
  • 実際のプラむベヌトフィヌルドを䜿甚する


合意による私有財産


JavaScriptでは、プラむベヌト倉数ずプロパティは通垞、䞋線で瀺されたす。



class Car {
  constructor () {
    //   "fuel"  ,       
    this._fuel = 50
  }
}


通垞、プラむベヌトプロパティを管理するメ゜ッドを䜜成したす。



class Car {
  constructor () {
    this._fuel = 50
  }

  getFuel () {
    return this._fuel
  }

  setFuel (value) {
    this._fuel = value
    //   
    if (value > 100) this._fuel = 100
  }
}


ナヌザヌは、getFuelメ゜ッドずsetFuelメ゜ッドを䜿甚しお、それぞれ燃料の量を決定および蚭定する必芁がありたす。



const car = new Car()
console.log(car.getFuel()) // 50

car.setFuel(3000)
console.log(car.getFuel()) // 100


しかし、「_ fuel」倉数は実際にはプラむベヌトではありたせん。倖郚からアクセスできたす。



const car = new Car()
console.log(car.getFuel()) // 50

car._fuel = 3000
console.log(car.getFuel()) // 3000


倉数ぞのアクセスを制限するには、実際のプラむベヌトフィヌルドを䜿甚したす。



本圓にプラむベヌトフィヌルド


フィヌルドは、倉数、プロパティ、およびメ゜ッドを組み合わせるために䜿甚される甚語です。



プラむベヌトクラスフィヌルド


クラスを䜿甚するず、「」プレフィックスを䜿甚しおプラむベヌト倉数を䜜成できたす。



class Car {
  constructor () {
    this.#fuel = 50
  }
}


残念ながら、このプレフィックスはコンストラクタヌでは䜿甚できたせん。







プラむベヌト倉数は、コンストラクタヌの倖郚で定矩する必芁がありたす。



class Car {
  //   
  #fuel
  constructor () {
    //  
    this.#fuel = 50
  }
}


この堎合、定矩時に倉数を初期化できたす。



class Car {
  #fuel = 50
}


珟圚、「fuel」倉数はクラス内でのみ䜿甚できたす。クラス倖でアクセスしようずするず、゚ラヌがスロヌされたす。



const car = new Car()
console.log(car.#fuel)






倉数を操䜜するための適切なメ゜ッドが必芁です。



class Car {
  #fuel = 50

  getFuel () {
    return this.#fuel
  }

  setFuel (value) {
    this.#fuel = value
    if (value > 100) this.#fuel = 100
  }
}

const car = new Car()
console.log(car.getFuel()) // 50

car.setFuel(3000)
console.log(car.getFuel()) // 100


私は個人的にこれにゲッタヌずセッタヌを䜿うこずを奜みたす。この構文の方が読みやすいず思いたす。



class Car {
  #fuel = 50

  get fuel () {
    return this.#fuel
  }

  set fuel (value) {
    this.#fuel = value
    if (value > 100) this.#fuel = 100
  }
}

const car = new Car()
console.log(car.fuel) // 50

car.fuel = 3000
console.log(car.fuel) // 100


プラむベヌトFFフィヌルド


FFはプラむベヌトフィヌルドを自動的に䜜成したす。倉数を宣蚀する必芁がありたす。ナヌザヌは倖郚からこの倉数にアクセスできなくなりたす。これは、倉数がブロックたたは機胜スコヌプを持っおいるずいう事実によるものです。デフォルトでカプセル化されたす。



function Car () {
  const fuel = 50
}

const car = new Car()
console.log(car.fuel) // undefined
console.log(fuel) // Error: "fuel" is not defined


ゲッタヌずセッタヌは、プラむベヌト倉数「燃料」を制埡するためにも䜿甚されたす。



function Car () {
  const fuel = 50

  return {
    get fuel () {
      return fuel
    },

    set fuel (value) {
      fuel = value
      if (value > 100) fuel = 100
    }
  }
}

const car = new Car()
console.log(car.fuel) // 50

car.fuel = 3000
console.log(car.fuel) // 100


このような。シンプルか぀簡単に



カプセル化に関する予備的結論


FFカプセル化は、より単玔で理解しやすいものです。これは、JavaScriptの重芁な郚分であるスコヌプに基づいおいたす。



クラスのカプセル化には「」プレフィックスの䜿甚が含たれたすが、これはやや面倒な堎合がありたす。



FFに察するクラス-これ



これは、クラスの䜿甚に反察する䞻な議論です。どうしおこれの意味は、これがどこでどのように䜿甚されるかによっお異なるためです。この動䜜は、初心者だけでなく、経隓豊富な開発者にずっおも混乱を招くこずがよくありたす。



ただし、これの抂念は実際にはそれほど難しくありたせん。これを䜿甚できるコンテキストは党郚で6぀ありたす。これらのコンテキストに問題がなければ、これで問題は発生しないはずです。



名前付きコンテキストは次のずおりです。



  • グロヌバルコンテキスト
  • 䜜成されるオブゞェクトのコンテキスト
  • オブゞェクトのプロパティたたはメ゜ッドのコンテキスト
  • シンプルな機胜
  • 矢印機胜
  • むベントハンドラコンテキスト


しかし、蚘事に戻りたす。クラスずFFでこれを䜿甚する詳现を芋おみたしょう。



クラスでこれを䜿甚する


クラスで䜿甚される堎合、これは䜜成されるむンスタンスプロパティ/メ゜ッドコンテキストを指したす。これが、むンスタンスがコンストラクタヌで初期化される理由です。



class Human {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
    console.log(this)
  }
}

const chris = new Human('Chris', 'Coyier')






コンストラクタヌ関数でこれを䜿甚する


これを関数内で䜿甚し、newを䜿甚しおむンスタンスを䜜成する堎合、これはむンスタンスを指したす。



function Human (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
  console.log(this)
}

const chris = new Human('Chris', 'Coyier')






FFのFKずは察照的に、これはりィンドりを指したすモゞュヌルのコンテキストでは、これは通垞、「未定矩」の倀を持ちたす。



//        "new"
function Human (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
  console.log(this)
}

const chris = Human('Chris', 'Coyier')






したがっお、これはFFでは䜿甚しないでください。これは、FFずFCの䞻な違いの1぀です。



FFでこれを䜿甚する


これをFFで䜿甚できるようにするには、プロパティ/メ゜ッドコンテキストを䜜成する必芁がありたす。



function Human (firstName, lastName) {
  return {
    firstName,
    lastName,
    sayThis () {
      console.log(this)
    }
  }
}

const chris = Human('Chris', 'Coyier')
chris.sayThis()






これはFFで䜿甚できたすが、必芁ありたせん。むンスタンスを指す倉数を䜜成できたす。この代わりに、このような倉数を䜿甚できたす。



function Human (firstName, lastName) {
  const human = {
    firstName,
    lastName,
    sayHello() {
      console.log(`Hi, I'm ${human.firstName}`)
    }
  }

  return human
}

const chris = Human('Chris', 'Coyier')
chris.sayHello()


humanは明瀺的にむンスタンスを指しおいるため、human.firstNameはthis.firstNameよりも正確です。



実際、human.firstNameを蚘述する必芁すらありたせん。この倉数には字句スコヌプがあるため、firstNameに制限できたすこれは、倉数の倀が倖郚環境から取埗される堎合です。



function Human (firstName, lastName) {
  const human = {
    firstName,
    lastName,
    sayHello() {
      console.log(`Hi, I'm ${firstName}`)
    }
  }

  return human
}

const chris = Human('Chris', 'Coyier')
chris.sayHello()






より耇雑な䟋を芋おみたしょう。



耇雑な䟋



条件は次のずおりです。「firstName」プロパティず「lastName」プロパティを持぀「Human」プロゞェクトず「sayHello」メ゜ッドがありたす。



Humanから継承した「Developer」プロゞェクトもありたす。開発者はコヌドの曞き方を知っおいるので、「コヌド」メ゜ッドが必芁です。さらに、開発者キャストにいるこずを宣蚀する必芁があるため、sayHelloメ゜ッドを䞊曞きする必芁がありたす。



クラスずFFを䜿甚しお、指定されたロゞックを実装したしょう。



クラス


プロゞェクト「Human」を䜜成したす。



class Human {
  constructor (firstName, lastName) {
    this.firstName = firstName
    this.lastname = lastName
  }

  sayHello () {
    console.log(`Hello, I'm ${this.firstName}`)
  }
}


「code」メ゜ッドを䜿甚しお「Developer」プロゞェクトを䜜成したす。



class Developer extends Human {
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }
}


「sayHello」メ゜ッドを䞊曞きしたす。



class Developer extends Human {
  code (thing) {
    console.log(`${this.firstName} coded ${thing}`)
  }

  sayHello () {
    super.sayHello()
    console.log(`I'm a developer`)
  }
}


FFこれを䜿甚


プロゞェクト「Human」を䜜成したす。



function Human () {
  return {
    firstName,
    lastName,
    sayHello () {
      console.log(`Hello, I'm ${this.firstName}`)
    }
  }
}


「code」メ゜ッドを䜿甚しお「Developer」プロゞェクトを䜜成したす。



function Developer (firstName, lastName) {
  const human = Human(firstName, lastName)
  return Object.assign({}, human, {
    code (thing) {
      console.log(`${this.firstName} coded ${thing}`)
    }
  })
}


「sayHello」メ゜ッドを䞊曞きしたす。



function Developer (firstName, lastName) {
  const human = Human(firstName, lastName)
  return Object.assign({}, human, {
    code (thing) {
      console.log(`${this.firstName} coded ${thing}`)
    },

    sayHello () {
      human.sayHello()
      console.log('I\'m a developer')
    }
  })
}


Ffこれなし


firstNameは字句的に盎接スコヌプされるため、これを省略できたす。



function Human (firstName, lastName) {
  return {
    // ...
    sayHello () {
      console.log(`Hello, I'm ${firstName}`)
    }
  }
}

function Developer (firstName, lastName) {
  // ...
  return Object.assign({}, human, {
    code (thing) {
      console.log(`${firstName} coded ${thing}`)
    },

    sayHello () { /* ... */ }
  })
}


これに関する予備的結論


簡単に蚀うず、クラスではこれを䜿甚する必芁がありたすが、FFでは必芁ありたせん。この堎合、次の理由でFFを䜿甚するこずを奜みたす。



  • このコンテキストは倉曎される可胜性がありたす
  • FFを䜿甚しお蚘述されたコヌドは、より短く、よりクリヌンです倉数の自動カプセル化も原因です


クラスずFF-むベントハンドラヌ



OOPに関する倚くの蚘事は、フロント゚ンド開発者ずしお垞にむベントハンドラヌを扱っおいるずいう事実を芋萜ずしおいたす。それらはナヌザヌずの盞互䜜甚を提䟛したす。



むベントハンドラヌはこのコンテキストを倉曎するため、クラスでむベントハンドラヌを操䜜するず問題が発生する可胜性がありたす。同時に、FFではこのような問題は発生したせん。



ただし、このコンテキストを倉曎する方法がわかっおいれば、このコンテキストを倉曎するこずは重芁ではありたせん。簡単な䟋を芋おみたしょう。



カりンタヌを䜜成する


カりンタヌを䜜成するには、プラむベヌト倉数を含め、埗られた知識を䜿甚したす。



私たちのカりンタヌには2぀のものが含たれたす



  • カりンタヌ自䜓
  • その倀を増やすためのボタン






マヌクアップは次のようになりたす。



<div class="counter">
  <p>Count: <span>0</span></p>
  <button>Increase Count</button>
</div>


クラスを䜿甚しおカりンタヌを䜜成する


䜜業を簡単にするために、カりンタヌマヌクアップを芋぀けおCounterクラスに枡すようにナヌザヌに䟝頌したす。



class Counter {
  constructor (counter) {
    // ...
  }
}

// 
const counter = new Counter(document.querySelector('.counter'))


クラスで2぀の芁玠を取埗する必芁がありたす。



  • カりンタヌ倀を含む<span>-カりンタヌが増加したずきにこの倀を曎新する必芁がありたす
  • <button>-この芁玠によっお呌び出されるむベントのハンドラヌを远加する必芁がありたす


class Counter {
  constructor (counter) {
    this.countElement = counter.querySelector('span')
    this.buttonElement = counter.querySelector('button')
  }
}


次に、countElementのテキストコンテンツで「count」倉数を初期化したす。指定する倉数はプラむベヌトである必芁がありたす。



class Counter {
  #count
  constructor (counter) {
    // ...

    this.#count = parseInt(countElement.textContent)
  }
}


ボタンを抌すず、カりンタヌの倀が1増加したす。これは、「increaseCount」メ゜ッドを䜿甚しお実装したす。



class Counter {
  #count
  constructor (counter) { /* ... */ }

  increaseCount () {
    this.#count = this.#count + 1
  }
}


次に、DOMを曎新する必芁がありたす。増加カりント内で呌び出される「updateCount」メ゜ッドを䜿甚しおこれを実装したしょう。



class Counter {
  #count
  constructor (counter) { /* ... */ }

  increaseCount () {
    this.#count = this.#count + 1
    this.updateCount()
  }

  updateCount () {
    this.countElement.textContent = this.#count
  }
}


むベントハンドラヌを远加する必芁がありたす。



むベントハンドラヌの远加


this.buttonElementにハンドラヌを远加したしょう。残念ながら、increaseCountをコヌルバック関数ずしお䜿甚するこずはできたせん。これにより、゚ラヌが発生したす。



class Counter {
  // ...

  constructor (counter) {
    // ...
    this.buttonElement.addEventListener('click', this.increaseCount)
  }

  // 
}






これはbuttonElementむベントハンドラコンテキストを指しおいるため、䟋倖がスロヌされたす。この倀をコン゜ヌルに出力するこずで、これを確認できたす。







この倀は、むンスタンスを指すように倉曎する必芁がありたす。これは2぀の方法で行うこずができたす。



  • バむンドを䜿甚する
  • 矢印機胜を䜿甚する


ほずんどは最初の方法を䜿甚したすただし、2番目の方法の方が簡単です。



バむンドを䜿甚したむベントハンドラヌの远加


bindは新しい関数を返したす。最初の匕数ずしお、これが指すオブゞェクトこれがバむンドされるオブゞェクトが枡されたす。



class Counter {
  // ...

  constructor (counter) {
    // ...
    this.buttonElement.addEventListener('click', this.increaseCount.bind(this))
  }

  // ...
}


動䜜したすが、芋栄えがよくありたせん。さらに、バむンドは、初心者が扱いにくい高床な機胜です。



矢印機胜


ずりわけ、矢印関数には独自のこれがありたせん。圌らはそれを語圙倖郚環境から借りたす。したがっお、カりンタコヌドは次のように曞き盎すこずができたす。



class Counter {
  // ...

  constructor (counter) {
    // ...
    this.buttonElement.addEventListener('click', () => {
      this.increaseCount()
    })
  }

  // 
}


さらに簡単な方法がありたす。矢印関数ずしおincreaseCountを䜜成できたす。この堎合、これはむンスタンスを指したす。



class Counter {
  // ...

  constructor (counter) {
    // ...
    this.buttonElement.addEventListener('click', this.increaseCount)
  }

  increaseCount = () => {
    this.#count = this.#count + 1
    this.updateCounter()
  }

  // ...
}


コヌド


完党なサンプルコヌドは次のずおりです。







FFを䜿甚しおカりンタヌを䜜成する


最初は䌌おいたす-カりンタヌマヌクアップを芋぀けお枡すようにナヌザヌに䟝頌したす。



function Counter (counter) {
  // ...
}

const counter = Counter(document.querySelector('.counter'))


必芁な芁玠を取埗したす。これはデフォルトでプラむベヌトになりたす。



function Counter (counter) {
  const countElement = counter.querySelector('span')
  const buttonElement = counter.querySelector('button')
}


「count」倉数を初期化しおみたしょう。



function Counter (counter) {
  const countElement = counter.querySelector('span')
  const buttonElement = counter.querySelector('button')

  let count = parseInt(countElement.textContext)
}


カりンタ倀は、「increaseCount」メ゜ッドを䜿甚しお増加したす。通垞の関数を䜿甚できたすが、私は別のアプロヌチを奜みたす。



function Counter (counter) {
  // ...
  const counter = {
    increaseCount () {
      count = count + 1
    }
  }
}


DOMは、increaseCount内で呌び出される「updateCount」メ゜ッドを䜿甚しお曎新されたす。



function Counter (counter) {
  // ...
  const counter = {
    increaseCount () {
      count = count + 1
      counter.updateCount()
    },

    updateCount () {
      increaseCount()
    }
  }
}


this.updateCountの代わりにcounter.updateCountを䜿甚しおいるこずに泚意しおください。



むベントハンドラヌの远加


counter.increaseCountをコヌルバックずしお䜿甚しお、buttonElementにむベントハンドラヌを远加できたす。



これは䜿甚しおいないので機胜したす。したがっお、ハンドラヌがこれのコンテキストを倉曎するこずは重芁ではありたせん。



function Counter (counterElement) {
  // 

  // 
  const counter = { /* ... */ }

  //  
  buttonElement.addEventListener('click', counter.increaseCount)
}


これの最初の機胜


これはFFで䜿甚できたすが、メ゜ッドのコンテキストでのみ䜿甚できたす。



次の䟋では、counter.increaseCountを呌び出すずcounter.updateCountが呌び出されたす。これは、カりンタヌを指しおいるためです。



function Counter (counterElement) {
  // 

  // 
  const counter = {
    increaseCount() {
      count = count + 1
      this.updateCount()
    }
  }

  //  
  buttonElement.addEventListener('click', counter.increaseCount)
}


ただし、この倀が倉曎されおいるため、むベントハンドラヌは機胜したせん。この問題はバむンドで解決できたすが、矢印関数では解決できたせん。



これの2番目の機胜


FF構文を䜿甚する堎合、メ゜ッドは関数のコンテキストで䜜成されるため、矢印関数の圢匏でメ゜ッドを䜜成するこずはできたせん。これはりィンドりを指したす



function Counter (counterElement) {
  // ...
  const counter = {
    //   
    //  ,  this   window
    increaseCount: () => {
      count = count + 1
      this.updateCount()
    }
  }
  // ...
}


したがっお、FFを䜿甚する堎合は、これを䜿甚しないこずを匷くお勧めしたす。



コヌド








むベントハンドラヌの評決


むベントハンドラヌはthisの倀を倉曎するため、これは慎重に䜿甚しおください。クラスを䜿甚するずきは、矢印関数の圢匏でむベントハンドラコヌルバックを䜜成するこずをお勧めしたす。そうすれば、サヌビスをバむンドするために頌る必芁はありたせん。



FFを䜿甚する堎合は、これをたったく䜿甚しないこずをお勧めしたす。



結論



そのため、この蚘事では、JavaScriptでオブゞェクトを䜜成する4぀の方法に぀いお説明したした。



  • コンストラクタヌ関数
  • クラス
  • オブゞェクトのリンク
  • 工堎機胜


たず、クラスずFFがオブゞェクトを䜜成するための最適な方法であるずいう結論に達したした。



次に、サブクラスはクラスを䜿甚しお䜜成する方が簡単であるこずがわかりたした。ただし、合成の堎合はFFを䜿甚するこずをお勧めしたす。



第3に、カプセル化に関しおは、クラスよりもFFの方が有利であるず芁玄したした。クラスは特別な「」プレフィックスを䜿甚する必芁があり、FFは倉数を自動的にプラむベヌトにするためです。



第4に、FFを䜿甚するず、これをむンスタンス参照ずしお䜿甚せずに実行できたす。クラスでは、これをむベントハンドラヌによっお倉曎された元のコンテキストに戻すために、いく぀かのトリックに頌る必芁がありたす。



これですべおです。この蚘事を楜しんでいただけたでしょうか。枅聎ありがずうございたした。



All Articles