はじめに
PrototypeのHonest Private Propertiesの出版物で最近述べたように、JavaScript開発者には2つのキャンプがあります。
- プロパティ/メソッドを隠すための指定として接頭辞を容認する意思があるもの;
- 擬似カプセル化に耐える準備ができていない人。
2番目のキャンプに関連し、コンストラクターでクラス全体を宣言することで問題を解決します 。これにより、 staticとの任意の組み合わせでprivate / publicを使用できます 。
たとえば、 Computer.js :
(function (module) { 'use strict'; var privateStaticVariable = 1; function Computer() { var privateVariable = 5; this.publicVariable = 8; Computer.publicStaticVariable = 1; this.getAnswer = function () { return privateStaticVariable + Computer.publicStaticVariable + this.publicVariable * privateVariable ; }; } module.exports = Computer; }(module));
問題
すべてがうまくいきますが、OOPはオープン/クローズに限定されないため、多くの目標が破られ、クラスの保護されたプロパティの問題を解決するためのかなりの数の方法が開発されました。構文を紹介します。 検索中、コアでeval()を使用してライブラリを表示することもできましたが、次のことを知っています。
評価は悪です!しかし、奇妙なことに、後者は(主観的な意見では)以下の基準で調査されたすべての中で最高でした:
- 合意なし、真のセキュリティ。
- 特定の構文はなく、ネイティブJSのみ。
リサーチ
ソースコードの短い調査の後、正規表現、 .toSource() 、 eval() 、およびマジックを使用してプライベートコードを子クラスに移動することにより、「セキュリティ」が提供されることが発見されました。 当然のことながら、このエンジニアリングの奇跡はすぐに機能しませんでした。また、 保護のためにプライベートを寄付することはあまり面白くないです。
解決策
第一段階
階層のすべてのクラスに何があり、同時にこの階層のユーザーが完全にアクセスできないのかについて謎を解くことが決定されました。 3日目に、すべての事業を埋め、コンテンツに満足する直前に、ある考えが頭に浮かびました。コンストラクターパラメーター !
第二段階
これで、思考プロセスの最初の部分が完了しました。次のステップは、この非常に追加の引数をコードのユーザーから隠す方法を見つけることでした。 そしてこの問題により、言語と創造性は私たちを助けます:
- クラスコンストラクターを保存します。
- クラスコンストラクターを書き換えます。
- プライベート変数(オブジェクト)を作成します。
- [null、savedPrivateObject]パラメーターを使用して、保存されたコンストラクターでFunction.prototype.bind.apply()を使用します。
しかし、非常に多くのアクションを手動で行うのは長い時間であり、優れた開発者は怠zyな開発者( ObjectOriented.js )です。
(function () { /** * Inject protected-data object to class * @private * @param Class {Function} Class * @param protectedData {Object} Protected-data object * @return {Function} Result class */ function injectProtected(Class, protectedData) { return (function (Native) { function Overridden() { var args = Array.prototype.map.call(arguments, function (value) { return [value]; }); args.unshift(protectedData); args.unshift(null); return (new (Function.prototype.bind.apply(Native, args))()); } Overridden.prototype = new Native({}); return Overridden; }(Class)); } }());
「継承はどうですか?」または第4段階
この問題を解決するには、まず、保護されたセクションが実装されているかどうかを判断する機能が必要です。これは、クラスの静的メソッド、たとえばisProtectedInjected()を追加することで解決されます。 実装の存在に関する情報を取得できるようになったので、クラスとそのすべての子孫に同じオブジェクトを実装する方法について考えることができます。 これを行うには、少なくとも保護されたオブジェクトを埋め込む前に元のクラスを取得できる必要があります。したがって、元のクラスを返す静的メソッドを追加します。 継承自体は、追加の保護されたデータ変数を作成し、実装が必要なクラスに注入することを除いて、JavaScriptの標準です。
最終段階または「利便性の追加」
OOP機能の一部を実装するコードをオブジェクト指向のスタイルにするときが来ました。 injectProtected() 、 extend()メソッド、およびisProtectedInjected 、 getNative()スタブメソッドをグローバル組み込み関数クラスに追加します。 これはあなたの人生を簡素化するのに役立ちます、なぜなら その後、どのクラスにもこの一連の関数が含まれます。
結果
(function (Function) { 'use strict'; /** * Check if protected-data was injected * @returns {boolean} */ Function.prototype.isProtectedInjected = function () { return false; }; /** * Inject protected-data object to class * @private * @param Class {Function} Class * @param protectedData {Object} Protected-data object * @return {Function} Result class */ function injectProtected(Class, protectedData) { return (function (Native) { function Overridden() { var args = Array.prototype.map.call(arguments, function (value) { return [value]; }); args.unshift(protectedData); args.unshift(null); return (new (Function.prototype.bind.apply(Native, args))()); } Overridden.prototype = new Native({}); Overridden.getNative = function () { return Native; }; Overridden.isProtectedInjected = function () { return true; }; return Overridden; }(Class)); } /** * Get native class without injection of protected * @returns {Function} Class */ Function.prototype.getNative = function () { return this; }; /** * Extend from @a ParentClass * @param {Function} ParentClass * @return {Function} Result class */ Function.prototype.extend = function (ParentClass) { var protectedData = {}, parent, me = this.getNative(); if (ParentClass.isProtectedInjected()) { ParentClass = injectProtected(ParentClass.getNative(), protectedData); } parent = new ParentClass(); me.prototype = parent; me.prototype.constructor = me; protectedData.parent = parent; if (me.isProtectedInjected()) { me = injectProtected(me, protectedData); } me.prototype = parent; me.prototype.constructor = me; return me; }; /** * Injects protected-data object to class * @example * function SomeClass(protectedData/\* , ... *\/) { * protectedData.protectedMethod = function () {}; * protectedData.protectedVariable = 'Access only from children and self'; * /\* ...Realization... *\/ * }.injectProtected() * @returns {Function} */ Function.prototype.injectProtected = function () { return injectProtected(this, {}); }; }(Function));
使用例
Computer.js :
(function (module) { 'use strict'; var privateStaticVariable = 1; function Computer(protectedData) { var privateVariable = 5; this.publicVariable = 8; protectedData.badModifier = 0.1; Computer.publicStaticVariable = 1; this.getAnswer = function () { return ( privateStaticVariable + Computer.publicStaticVariable + this.publicVariable * privateVariable ) * protectedData.badModifier; }; } module.exports = Computer.injectProtected(); // <- That's it! }(module));
FixedComputer、js :
(function (module) { 'use strict'; var Computer = require('Computer'); function FixedComputer(protectedData) { Computer.call(this); // Super analogue protectedData.badModifier = 1; } module.exports = FixedComputer.injectProtected().extend(Computer); // <- That's it! }(module));
参照資料
図書館:
- 独自の構文を作成します 。
- もう1つ 。
- eval()に基づくもの ;
- この記事の結果 。