
ES6からclassシンタックスシュガーが導入され、JavaScriptでのオブジェクト指向プログラミングが分かりやすくなりました。今回はclassを使用したJavaScriptのオブジェクト指向プログラミングの方法を紹介します。
はじめに
前回Prototypeチェーンを使用したJavaScriptのオブジェクト指向プログラミングを紹介しました。今回はES6で導入されたclassシンタックスシュガーを使用したJavaScriptのオブジェクト指向プログラミングの方法を紹介します。
クラスの定義
まずは、classシンタックスシュガーでクラスを定義してみましょう。
class Component {
constructor(name, props = {}) {
this.name = name;
this.props = props;
}
toString() {
return `name: ${this.name}, props: ${JSON.stringify(this.props)}`;
}
static isComponent(obj) {
return obj instanceof this;
}
}
const myComponent = new Component('Component', { color: 'blue' });
console.log(myComponent.toString()); // => name: Component, props: {"color":"blue"}
console.log(Component.isComponent(myComponent)); // => true
この例では、toString関数がいわゆるインスタンスメソッドで、isComponent関数がいわゆるクラスメソッドです。
カプセル化とGetter/Setterの定義
2つの方法があります。
WeakMapを使う方法
WeakMapを使って実装してみましょう。
const _name = new WeakMap();
const _props = new WeakMap();
const _capitalize = new WeakMap();
class Component {
constructor(name, props = {}) {
_name.set(this, name);
_props.set(this, props);
_capitalize.set(this, letter => letter.toUpperCase());
}
get name() {
return _name.get(this);
}
get props() {
return _props.get(this);
}
set props(props) {
_props.set(this, props);
}
toString() {
return `name: ${this.name}, props: ${JSON.stringify(this.props)}`;
}
shoutName() {
console.log(`${_capitalize.get(this)(this.name)}!!`);
}
}
const myComponent = new Component('Component', { color: 'blue' });
console.log(myComponent.toString()); // => name: Component, props: {"color":"blue"}
myComponent.name = 'Not Changed'; // 変更されない
myComponent.props = { color: 'green' };
console.log(myComponent.toString()); // => name: Component, props: {"color":"green"}
myComponent.shoutName(); // => COMPONENT!!
この例では、nameプロパティはSetterが無いため値を変更できません。一方で、propsプロパティはGetter/Setterが両方あるため、変更も取得もできます。
Symbolを使う方法
Symbolを使って実装しましょう。
const _name = Symbol();
const _props = Symbol();
const _capitalize = Symbol();
class Component {
constructor(name, props = {}) {
this[_name] = name;
this[_props] = props;
}
[_capitalize](letter) {
return letter.toUpperCase();
}
get name() {
return this[_name];
}
get props() {
return this[_props];
}
set props(props) {
this[_props] = props;
}
toString() {
return `name: ${this.name}, props: ${JSON.stringify(this.props)}`;
}
shoutName() {
console.log(`${this[_capitalize](this[_name])}!!`);
}
}
const myComponent = new Component('Component', { color: 'blue' });
console.log(myComponent.toString()); // => name: Component, props: {"color":"blue"}
myComponent.name = 'Not Changed'; // 変更されない
myComponent.props = { color: 'green' };
console.log(myComponent.toString()); // => name: Component, props: {"color":"green"}
myComponent.shoutName(); // => COMPONENT!!
この例でも先程と同じく、nameプロパティはSetterが無いため値を変更できません。一方で、propsプロパティはGetter/Setterが両方あるため、変更も取得もできます。
継承とポリモーフィズム
継承
extendsを使って実装できます。
const _name = new WeakMap();
const _props = new WeakMap();
const _capitalize = new WeakMap();
class Component {
constructor(name, props = {}) {
_name.set(this, name);
_props.set(this, props);
_capitalize.set(this, letter => letter.toUpperCase());
}
get name() {
return _name.get(this);
}
get props() {
return _props.get(this);
}
set props(props) {
_props.set(this, props);
}
toString() {
return `name: ${this.name}, props: ${JSON.stringify(this.props)}`;
}
shoutName() {
console.log(`${_capitalize.get(this)(this.name)}!!`);
}
}
const _children = new WeakMap();
class Welcome extends Component {
constructor(props, children) {
super('Welcome', props);
_children.set(this, children);
}
render() {
return `<h1>${_children.get(this)}</h1>`;
}
}
const welcome = new Welcome({ color: 'orange' }, 'Welcome to here!');
console.log(welcome.toString()); // => name: Welcome, props: {"color":"orange"}
welcome.shoutName(); // => WELCOME!!
console.log(welcome.render()); // => <h1>Welcome to here!</h1>
ポリモーフィズム
続いて、ポリモーフィズムを実装してみましょう。
const _name = new WeakMap();
const _props = new WeakMap();
const _capitalize = new WeakMap();
class Component {
constructor(name, props = {}) {
_name.set(this, name);
_props.set(this, props);
_capitalize.set(this, letter => letter.toUpperCase());
}
get name() {
return _name.get(this);
}
get props() {
return _props.get(this);
}
set props(props) {
_props.set(this, props);
}
toString() {
return `name: ${this.name}, props: ${JSON.stringify(this.props)}`;
}
shoutName() {
console.log(`${_capitalize.get(this)(this.name)}!!`);
}
}
const _children = new WeakMap();
class Title extends Component {
constructor(props, children) {
super('Title', props);
_children.set(this, children);
}
render() {
return `<h1>${_children.get(this)}</h1>`;
}
}
const _children2 = new WeakMap();
class Paragraph extends Component {
constructor(props, children) {
super('Paragraph', props);
_children2.set(this, children);
}
render() {
return `<p>${_children2.get(this)}</p>`;
}
}
const title = new Title({ color: 'orange' }, 'What is OOP?');
const paragraph = new Paragraph({ color: 'black' }, 'The OOP means an object oriented programming.');
const components = [title, paragraph];
components.forEach(component => console.log(component.render()));
// => <h1>What is OOP?</h1>
// => <p>The OOP means an object oriented programming.</p>
最後に
いかがでしたか?classシンタックスのおかげでprototypeと比べて簡単かつ直感的にオブジェクト指向プログラミングが実装できるようになりましたね。最近のフロントエンドフレームワークはオブジェクト指向で書く事が多いので、書き方は理解しておきましょう。では。