2017-08-26 13 views
4

組み込みのMapオブジェクトを拡張しようとしています。これは動作します一部です:ネイティブオブジェクトを拡張するファクトリ/クラス

var Attributes = class extends Map { 
 

 
    get text() { 
 
     let out = []; 
 
     for (let [ k, v ] of this.entries()) { 
 
      out.push(k + '="' + v + '"'); 
 
     } 
 
     return out.join(' '); 
 
    } 
 

 
    // simplified for brevity! 
 
    set text (raw) { 
 
     this.clear(); 
 
     var m, r = /(\w+)="([^"]+)"/g; 
 
     while ((m = r.exec(raw))) { 
 
      this.set(m[1], m[2]); 
 
     } 
 
    } 
 
}; 
 

 
var a = new Attributes(); 
 
a.text = 'id="first"'; 
 
console.log(a.get('id')); // first 
 
a.set('id', 'second'); 
 
console.log(a.text); // id="second"

しかし、私は自分のライブラリにシームレスにこのクラスを統合し、むしろ、コンストラクタとして、二重の目的を果たすファクトリメソッドを公開します。ユーザーは、この特定の方法が珍しいことを知る必要はありません。それは物事を複雑にするだけです。私自身のコードでは、入力検証の目的でinstanceofを使用できる必要があります。これは私がつもりです:

var obj = {}; 
 
obj.attr = function (text) { 
 
    if (new.target) { 
 
     this.text = text; 
 
    } else { 
 
     return new obj.attr(text); 
 
    } 
 
}; 
 

 
console.log(obj.attr() instanceof obj.attr); // true

は、あまりにも、上記の動作します。しかし、どのように2つのアプローチを組み合わせようとも、ChromeとFirefoxの両方でさまざまなエラーが発生します。例えば以下のコード。例外TypeError "this.entries(...)[Symbol.iterator]機能ではありません" 例外:

var obj = {}; 
 
obj.attr = function (text) { 
 
    if (new.target) { 
 
     this.text = text; 
 
    } else { 
 
     return new obj.attr(text); 
 
    } 
 
}; 
 

 
obj.attr.prototype = Object.assign(new Map(), { 
 
    get text() { 
 
     let out = []; 
 
     for (let [ k, v ] of this.entries()) { 
 
      out.push(k + '="' + v + '"'); 
 
     } 
 
     return out.join(' '); 
 
    }, 
 
    // simplified for brevity! 
 
    set text (raw) { 
 
     this.clear(); 
 
     var m, r = /(\w+)="([^"]+)"/g; 
 
     while ((m = r.exec(raw))) { 
 
      this.set(m[1], m[2]); 
 
     } 
 
    } 
 
});

は、私がここにおよび/または誤解何をしないのですか?

更新:Object.setPrototypeOf()を使用しても可能ですが、パフォーマンスの低下はおそらく相当になります。 でもを使用してが可能です。正確にはこれがObject.setPrototypeOf()とどのように違うのかはわかりません。リフレクションは現在のブラウザでは最適化されていないため、一般的に遅いようです。

var Attributes = class extends Map { 
 
    
 
    constructor (text) { 
 
     super(); 
 
     this.text = text; 
 
    } 
 
    
 
    get text() { 
 
     let out = []; 
 
     for (let [ k, v ] of this.entries()) { 
 
      out.push(k + '="' + v + '"'); 
 
     } 
 
     return out.join(' '); 
 
    } 
 
    
 
    // simplified for brevity! 
 
    set text (raw) { 
 
     this.clear(); 
 
     if (!raw) return; 
 
     var m, r = /(\w+)="([^"]+)"/g; 
 
     while ((m = r.exec(raw))) { 
 
      this.set(m[1], m[2]); 
 
     } 
 
    } 
 
}; 
 

 
var obj = {}; 
 

 
obj.attr = function (text) { 
 
    if (new.target) { 
 
     return Reflect.construct(Attributes, [ text ], obj.attr); 
 
    } else { 
 
     return new obj.attr(text); 
 
    } 
 
}; 
 

 
obj.attr.prototype = Object.create(Attributes.prototype); 
 

 
console.log(obj.attr() instanceof obj.attr); 
 
console.log(obj.attr() instanceof Attributes); 
 
console.log(obj.attr() instanceof Map); 
 

 
var a = obj.attr(); 
 
a.text = 'id="first"'; 
 
console.log(a.get('id')); 
 
a.set('id', 'second'); 
 
console.log(a.text);

+1

申し訳ありませんが、次のことができるようにしたいので、 '新しい属性(...)と'属性(...)の両方を使用しますか? –

+0

はい。まあ、一種。ドキュメントでは、 'obj.attr()'を使用するようにユーザーに指示します。私は、 'new obj.attr()'も同様に機能することを省略する予定です。 –

+1

this.entriesはエラーをスローしますが、this.entriesメソッドのコードは含めないと書いています。 – Sebastian

答えて

1

ITSはObject.assign doesntのコピーゲッター/セッターので作業していて、それらを呼び出しません。

MDN:

Object.assign()メソッドのソースからコピーのみ列挙し、独自のプロパティを:だからゲッターは、(テキストを取得)このがobviosly失敗しますウィンドウ、というと呼ばれていますオブジェクトをターゲットオブジェクトに追加します。それはソース上で[[Get]]を使用し、ターゲット上で[[Set]]を使用するので、getterとsetterを呼び出します。これにより、マージソースにゲッターが含まれている場合、新しいプロパティをプロトタイプにマージすることが不適切になることがあります。列挙可能性を含むプロパティ定義をプロトタイプにコピーするには、Object.getOwnPropertyDescriptor()およびObject.defineProperty()を代わりに使用する必要があります。

セッターを失うことなく、地図にオブジェクトをコピーするには、行うことがあります。

obj.attr.prototype = new Map(); 
var settings = { 
    get text(){}, 
    set text(v){} 
}; 

for(key in settings){ 
    Object.defineProperty(
    obj.attr.prototype, 
    key, 
    Object.getOwnPropertyDescriptor(settings,key) 
); 
} 

をしかし、これはまだ失敗します。マップはオブジェクトとは異なります。 this.clear()を呼び出すと、が通常のオブジェクトではなくマップになると予想されるため、クラッシュします。だから、2つのオプションがあります左:

1)クラスの構文と工場を使用します。

{ 
    let internal = class extends Map { 
    constructor(text){ 
    super(); 
    this.text = text; 
    }; 
    set text(v){}; 
    get text(){}; 
    }; 

    var obj = { 
    attr(text){ 
    return new internal(text); 
    } 
    }; 
} 

2)内部のマップを保つ:

obj.attr = function(text){ 
    if(this === window) return new obj.attr(text); 
    this.map = new Map(); 
    this.text = text; 
}; 
obj.attr.prototype = { 
    set text(v){ 
    this.map.set("text",v); 
    }, 
    get text(){ 
    return this.map.get("text"); 
    } 
}; 
+0

それはたくさんの意味があります。私はまだそれを動作させるように見えることはできません。私の編集をチェックしてください! –

+0

@ピーターコアスターも編集;) –

+0

確かに。マップはPOJOではありませんが、 'obj.attr.prototype = new Map();'は 'obj.attr'オブジェクトを通常のMapオブジェクトに変換する必要がありますか? setterとgetterに 'console.log(this)'を追加すると、私は見たいと思う拡張Mapオブジェクトを正確に見ることができます。 –