Why is the prototype chain being executed differently?

84 Views Asked by At

I'm trying to learn more about Javascript and dig a bit into the prototype chain. I wanted to create a small extension for an HTMLElement when I came across this problem.

The way I understand Object.create is that the object that is passed to it is used to create the context for the new object and that the first link in the prototype chain of the newly created object will point to the object passed to the Object.create method. That being the case, the method of extension used in the bar method below seemed to be the correct approach to me as this newly created object would be given its HTMLElement as its context.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <span id="test"></span>

    <script>
      HTMLElement.prototype.foo = function() {
        let foo = Object.create(null);
        foo.parentElement = this;
        foo.parentElement.appendChild(document.createElement('div'));
      }

      HTMLElement.prototype.bar = function() {
        let fooRenderer = Object.create(this);
        fooRenderer.appendChild(document.createElement('div'));
      }

      document.getElementById('test').foo();
      document.getElementById('test').bar();
    </script>
</body>
</html>

What happens though is that the foo method works correctly by appending a new div child element to <span id="test"></span>, but bar does not.

When I open the developer tools in my browser and try to follow the prototype chains of the two objects that are having appendChild invoked on them, they look almost identical:

foo Object
    .parentElement <span#test2>
        .__proto__ HTMLSpanElementPrototype
            .__proto__ HTMLElementPrototype
                .__proto__ ElementPrototype
                    .__proto__ NodePrototype
                        .appendChild
                        .__proto__ EventTargetPrototype
                            .__proto__ Object
                                .__proto__
                                    get
                                    set

fooRenderer Object
    .__proto__ <span#test2>
        .__proto__ HTMLSpanElementPrototype
            .__proto__ HTMLElementPrototype
                .__proto__ ElementPrototype
                    .__proto__ NodePrototype
                        .appendChild
                        .__proto__ EventTargetPrototype
                            .__proto__ Object
                                .__proto__
                                    get
                                    set

I have created a jsFiddle with this example.

Can someone please explain to me why bar isn't working? Is bar in fact the more correct approach? If so, how should it be setup to work properly?

Thanks in advance for any help!!!

1

There are 1 best solutions below

1
Andy Ray On

Neither of these examples are "correct." In prototype methods you should not be trying to instantiate a new copy of the object you're already attached to. All you need to do is this:

HTMLElement.prototype.bar = function() {
    let div = document.createElement('div');
    div.innerHTML = 'fooRenderer';
    this.appendChild(div);
}

The first example only works because foo.parentElement will be a valid, native HTMLElement, not a custom created object.

See this answer for why you can't invoke "native" browser methods like appendChild on custom objects.