JavaScript でプライベート データを安全に継承する方法
多くの場合、アンダースコアを使用して、プロパティまたはメソッドをプライベートと見なす必要があることを示します。それは悪い考えです。
アンダースコアが悪い考えである理由
アンダースコアはデータのプライバシーを保証するものではなく、いくつかの重要な問題を引き起こします:
- 初心者はアンダースコアの意味を知らないので、アンダースコアを無視します。
- 上級ユーザーは、自分が何をしているかを理解していると考えているため、アンダースコアは適用されません。
- 実装の詳細が変更される可能性があり、アンダースコア プロパティを使用したユーザー コードのコードが壊れる可能性があります。
カプセル化はオブジェクト指向設計の重要な機能であるため、これらは問題です。オブジェクトは、特定の問題を解決するために存在します。プライベート メソッドは、実装の詳細としてのみ関連する問題を解決する場合があります。実装の詳細はパブリック インターフェイスよりも変更される可能性が高いため、実装の詳細に依存するコードは、実装の詳細が変更されると壊れる可能性があります。
パブリック インターフェイスのみを公開すると、変更される可能性のある実装の詳細が隠され、サポートされていない実装の詳細ではなく、サポートされている機能にユーザーが依存するようになります。
真のデータプライバシーのために機能継承を使用する
機能継承を使用して、プライベート データを継承できます。
機能継承は、オブジェクト拡張関数をオブジェクト インスタンスに適用することによって機能を継承するプロセスです。この関数はクロージャー スコープを提供します。これは、関数のクロージャー内にプライベート データを隠す効果があります。
Douglas Crockford は、「JavaScript: The Good Parts」でこの用語を作り出しました。Crockford の例では、子ファクトリは既存の基本ファクトリからオブジェクトをインスタンス化する方法を認識しており、継承階層を作成する効果があります。しかし、それは悪い考えです。クラスの継承よりも、常にオブジェクトの構成を優先する必要があります。
基本オブジェクトをパラメーターとして受け取るようにパターンをわずかに変更することで、機能的な mixin を作成および構成できます。
関数 mixin は、提供されたオブジェクト インスタンスを拡張します。関数のクロージャ スコープには、プライベート メソッドとデータが含まれる場合があります。その後、その関数内で特権メソッドを公開できます。それはこのように動作します:
const withFlying = instance => {
let canFly = true; // private data
let isFlying = false;
// Privileged method
instance.fly = () => {
isFlying = canFly ? true : isFlying;
return instance;
};
// Privileged method
instance.land = () => {
isFlying = false;
return instance;
}
// Privileged method
instance.getFlightStatus = () => isFlying ? 'Flying' : 'Not flying';
return instance;
};
// Create a new object and mix in flight capability:
const bird = withFlying({});
console.log(bird.fly().getFlightStatus()); // true
bird.land();
console.log(bird.getFlightStatus()); // false
機能的な mixin は、標準の関数構成を使用して、他の基本オブジェクトおよび他の機能のセットと一緒に構成できます。まず、compose 関数が必要です。compose()
Lodash、Ramda、または標準の構成関数を提供する関数型プログラミング ライブラリから使用できます。または、独自に作成することもできます。
// Function composition: Function applied to the result of another function application, e.g., f(g(x))
// compose(...fns: [...Function]) => Function
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
標準の関数構成を使用して、任意の数の mixin を一緒に構成できるようになりました。
// This function returns a function which can be used
// as a functional mixin.
// `text` here is private data that determines the sound
// `quack()` will log to the console.
const withQuacking = text => instance => {
// Privileged method
instance.quack = () => console.log(text);
return instance;
};
// Compose mixins:
// ('Quack!' is private data)
const createDuck = compose(withFlying, withQuacking('Quack!'));
const malard = createDuck({});
console.log(malard.fly().getFlightStatus()); // Flying
malard.quack(); // "Quack!"
さまざまな継承手法を使用してファクトリ関数を構成するより汎用的な方法については、スタンプ仕様を参照してください。
参考文献: