Наследовать от двух (или более) встроенных объектов в JS (Map, EventTarget) - PullRequest
0 голосов
/ 30 марта 2019

Я не Noob в JS, и я знаю, что технически нет правильного способа наследования от нескольких классов. так что мой вопрос очень прост

Есть ли идея, как я могу создать класс или просто объект, который действует так, как он наследует от двух сборок в JS Native объект. особенно EventTarget и другой объект.

Я пытаюсь:

var map = new Map();
var eventtarget = new EventTarget();
mix = Object.create({...Map.prototype, ...EventTarget.prototype});
Object.assign(mix, et , map);

Кажется, это не работает, потому что методы в Map.prototype не могут быть потеряны также используя Object.assign({}, Map.prototype, ...EventTarget.prototype) в качестве того же эффекта.

Еще одна попытка:

class Base5 extends Map{
 constructor(){
    super();
    var eventTarget = new EventTarget();
    Object.assign(this,eventTarget);
 }
}

Base5.prototype = Object.create(Base5.prototype)
Object.assign(Base5.prototype,EventTarget.prototype); 

//    that seem to work
const b5 = new Base5();
b5.set('foo','bar');
//    but... 
b4.addEventListener('fire', _=>_ )
// throw Uncaught TypeError: Illegal invocation at <anonymous>:1:4

Этот работал, но он не является общим

const wm = new WeakMap();

class Base6 extends Map{
 constructor(){
    super();
    wm.set(this, new EventTarget() )
 }
 addEventListener(){ 
    wm.get(this).addEventListener(...arguments)
 }
 dispatchEvent(){
    wm.get(this).dispatchEvent(...arguments)
 }
 removeEventListener(){
   wm.get(this).removeEventListener(...arguments)
 }
}

const b6 = new Base6();
b6.set('foo','bar'); // Map(1) {"foo" => "bar"}
b6.addEventListener('foo', e=>console.log(e) );
b6.dispatchEvent( new Event('foo') ) 

Так кто-нибудь может прийти с лучшим подходом?

Может быть Reflect.construct может здесь как-то помочь

Ответы [ 3 ]

1 голос
/ 30 марта 2019

Вы можете создать функцию, которая создает частные экземпляры базовых классов и возвращает прокси, который отправляет извлечения свойств одному из этих объектов. Базовые классы могут быть переданы в конструктор, чтобы он оставался общим:

createMix(Map, EventTarget)

Некоторые вещи останутся проблематичными. Одна из проблем блокировки заключается в том, что для вызова методов часто требуется установить this на базовый объект. Обходной путь может заключаться в том, чтобы вернуть связанный метод, зная, что это само по себе может иметь нежелательные последствия (например, клиент не может взять метод и связать его самостоятельно с чем-то другим - если это вообще имело смысл).

Конечно, это не решает все потенциальные проблемы, но, кажется, работает в самом простом использовании:

function createMix(...classes) {
    const obj = {};
    const instances = [obj, ...classes.map(cls => new cls)];
    return new Proxy(obj, {
        get(obj, prop) {
            obj = instances.find(obj => prop in obj);
            const val = Object(obj)[prop];
            return typeof val === "function" ? val.bind(obj) : val;
        },
        has(obj, prop) { // Optional: if you care about the `in` operator
            return instances.some(obj => prop in obj);
        }
    });
}

// Tiny test
const obj = createMix(Map, EventTarget);
obj.set('foo','bar'); 
console.log("Map contains: ", Object.fromEntries(obj));
obj.addEventListener('foo', e => console.log("Event object type: ", e.type) );
obj.dispatchEvent( new Event('foo') );

Поскольку эта функция возвращает контейнерный объект, она не будет instanceof любым из базовых классов, переданных в функцию.

1 голос
/ 31 марта 2019

@ trincot show-me-a-way / вдохновляют меня исследовать немного больше и прийти с этим решением, которое улучшает некоторые аспекты его решения:

1) Слабое связывание для функции Inherit
2) Возможность использовать смешанный класс в качестве прототипа для расширения другого пользовательского класса.

Если вам понравился мой ответ, не забудьте также пролистать его ответ;

Итак, наконец, шорткод для расширения нескольких собственных классов, возможно, он не идеален, поэтому прокомментируйте, если найдете проблему, но это только начало, и мы можем ее улучшить в будущем:

function CreateMixedClasess(...classes) {
        return function Mixed(){
            const instances  =[this, ... classes.map(cls => new cls(...arguments))] 

            const weakbind = (proxy,fn,obj) => new Proxy(fn, { 
                apply(fn, thisArg, props){
                 return fn.apply(proxy === thisArg? obj:thisArg ,props);
            }})

            return new Proxy(this, {
                get(_, prop, proxy) {
                    obj = instances.find(obj => prop in obj);
                    const val = Object(obj)[prop];
                    return typeof val === "function" ? weakbind(proxy,val,obj) : val;
                },
                has(_, prop) { // Optional: if you care about the `in` operator
                    return instances.some(obj => prop in obj);
                },

            });   
        }
    }


    // class Mixed extends CreateMixedClasess(Map, EventTarget){}
    var Mixed =  CreateMixedClasess(Map, EventTarget);
    var obj12 = new Mixed();
    obj12.set('foo','bar'); 
    console.log("Map contains: ", Object.fromEntries(obj12));
    obj12.addEventListener('foo', e => console.log("Event object type: ", e.type) );
    obj12.dispatchEvent( new Event('foo') );
    obj12.a = 3;
    obj12.myfn = function (){console.log( this.a) };
    obj12.myfn() // 3
    obj12.myfn.call({a:4}) // 4

    class MyAwsomeClass extends CreateMixedClasess(Map, EventTarget){
        constructor(){
            super(...arguments);
            this.a = 33;
        }
        logA(){
            console.log( this.a)
        }
    }

    const  myAwsomeInstance = new MyAwsomeClass( obj12 );
    myAwsomeInstance.has('foo') // true
0 голосов
/ 30 марта 2019

На данный момент, и для других, которые ищут решение этой проблемы, я прихожу с этим решением

const wm = new WeakMap();

function Emitter(Base) {
   return class extends Base {
    constructor() {
        super(...arguments);
        wm.set(this, new EventTarget())
    }

    addEventListener() {
        wm.get(this).addEventListener(...arguments)
    }

    dispatchEvent() {
        wm.get(this).dispatchEvent(...arguments)
    }

    removeEventListener() {
        wm.get(this).removeEventListener(...arguments)
    }
  }
}

// how to use
const EmitterableMap = Emitter(Map);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...