James
James

Reputation: 2860

Problems extending object in Javascript ECMA6

I am having an issues extending an object with another object in ECMA6. After I have extended the object in question it says it cannot find the method on the updated object. Here is my code:

Subject class:

// Subject to be observed
class Subject {
    constructor() {
        this.observers = new ObserverList();
    }

    addObserver(observer) {
        this.observers.add(observer);
    }

    removeObserver(observer) {
        this.observers.removeAt(this.observers.indexOf( observer, 0 ));
    }

    notify(context) {
        for(let i = 0; i < this.observers.count(); i++) {
            this.observers.get(i).update(context);
        }
    }
}

And my code where the object is extended:

/**
 * Function to extend an object with another object
 */
function extend(obj, extension) {
    Object.assign(obj, extension);
}

// Get our DOM elements
let controlCheckbox = document.getElementById("mainCheckbox"),
    addBtn = document.getElementById("addNewObserver"),
    container = document.getElementById("observersContainer");


// Concrete subject
// extend controlling checkbox with Subject class
extend(controlCheckbox, new Subject());

// Clicking the checkbox will trigger notifications to it's observers
controlCheckbox.onclick = () => {
    controlCheckbox.notify(controlCheckbox.checked);
};

addBtn.onclick = addNewObserver;

function addNewObserver() {
    let check = document.createElement('input');
    check.type = 'checkbox';

    extend(check, new Observer());
    check.update = function(value) {
        this.checked = value;
    };
    controlCheckbox.addObserver(check);
    container.appendChild(check);
}

When the onclick is fired it is saying that the method controlCheckbox.addObserver is not defined. I have added a breakpoint to view the controlCheckbox object and I can see that method is available in the prototype. Should this not work? Can anyone point out where I am going wrong?

Upvotes: 1

Views: 94

Answers (2)

Qwertiy
Qwertiy

Reputation: 21400

No, Object.assign doesn't copy anything from proto.

console.log(Object.assign({}, Object.create({x:9})).x)

Just wrap dom-element by the Subject and work with you class.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074465

Object.assign only copies own enumerable properties from the source objects to the target. Your Subject object only has one own property: observers. All of the others (the methods) are inherited, not "own".

You'll need to write your own version of extend that handles inherited properties as well if you want to have them copied to the target.

The other issue is that by default, your methods won't be enumerable, either, so just a simple for-in won't find them. You'd need to use getOwnPropertyNames and getPrototypeOf.

For instance:

function extend(target, ...sources) {
    for (const source of sources) {
        for (let obj = source; obj && obj !== Object.prototype; obj = Object.getPrototypeOf(obj)) {
            for (const name of Object.getOwnPropertyNames(obj)) {
                if (!(name in target)) {
                    target[name] = obj[name];
                }
            }
        }
    }
    return target;
}

...but that's very quick and dirty, just concept. Note how I stopped at Object.prototype rather than copying toString and such.


Side note: Extending DOM elements in the way you are leaves you fairly open to conflicts with methods added to the DOM in the future, or by other libraries, etc. You may be fine with that, but it would worry me. Wrapping elements (like jQuery does) would be my preference.

Upvotes: 1

Related Questions